Python packaging

29
Packaging Python Par Axel Haustant

description

Packaging de projets Python avec setuptools (Français)

Transcript of Python packaging

Page 1: Python packaging

Packaging PythonPar Axel Haustant

Page 2: Python packaging

Packaging ?

Page 3: Python packaging

Pourquoi ?• pour distribuer

• pour déployer

• pour archiver

Être réutilisable

3

Page 4: Python packaging

Comment ?• standard

• multiplateformes

• documenté

• à moindre coût

La solution: setuptools !

4

Page 5: Python packaging

Les bases

Page 6: Python packaging

setup.pyfrom setuptools import setup

setup( name='my-project', version='0.1.0', # ...)

C'est du Python donc tout est permis !

6

Page 7: Python packaging

Identification• name

• version

• description

• long_description

• url

• classifiers

• ...

7

Page 8: Python packaging

Versionning• respect des normes (PEP 386, semver...)

• release: {major}.{minor}.{patch}

• dev/master/...: {major}.{minor}.{patch}.dev

• Automatisez la release !

• script shell

• outil dédié (ex: Zest.releaser, Bump'R, ...)

8

Page 9: Python packaging

Gestion des dépendances• install_requires

• tests_require

• extras_require

extras_require = { 'tests': ['factory-boy']}

$ pip install my-project[tests]

9

Page 11: Python packaging

READMEDoit permettre de démarrer rapidement.

• Présentation fonctionnelle rapide

• Procédure d'installation

• Documentation (ou lien)

• Complété par un changelog

11

Page 12: Python packaging

Commandes

Page 13: Python packaging

DéveloppezPour être prêt à développer:

$ python setup.py develop# ou$ pip install -e .

A refaire dès que les dépendances et les entrypoints changent.

13

Page 14: Python packaging

PrévisualisezContrôlez ce que vous allez publier

$ python setup.py --long-description | rst2html$ python setup.py sdist

14

Page 15: Python packaging

Publiez# Enregistrer le module sur PyPI$ python setup.py register# Publier sur PyPI$ python setup.py sdist upload# Créer un version avec un suffix$ python setup.py -q egg_info -b ".1234" sdist

15

Page 16: Python packaging

Réutilisez !

Page 17: Python packaging

Réutiliser les metadonnées du moduleSelon la PEP 396, le module doit contenir un attribut __version__

from project import __version__, __description__setup( name='project' version=__version__ description=__description__)

17

Page 18: Python packaging

Réutiliser les requirements de pipRE_REQUIREMENT = re.compile(r'̂\s*-r\s*(?P<filename>.*)$')

def pip(filename): requirements = [] for line in open(join('requirements', filename)).readlines(): match = RE_REQUIREMENT.match(line) if match: requirements.extend(pip(match.group('filename'))) else: requirements.append(line) return requirements

setup( # ... install_requires=pip('install.pip'), tests_require=pip('test.pip'), extras_require = { 'tests': pip('test.pip'), },)

18

Page 19: Python packaging

Réutiliser les fichiers rstPYPI_RST_FILTERS = ( (r'\.\.\s? code-block::\s*(\w|\+)+', '::'), # (r'.*travis-ci\.org/.*', ''), (r'.*pypip\.in/.*', ''), (r'.*crate\.io/.*', ''), (r'.*coveralls\.io/.*', ''),)

def rst(filename): content = open(filename).read() for regex, replacement in PYPI_RST_FILTERS: content = re.sub(regex, replacement, content) return content

long_description = '\n'.join(( rst('README.rst'), rst('CHANGELOG.rst'), ''))

19

Page 20: Python packaging

Réutilisez la versiontry: from pkg_resources import get_distribution VERSION = get_distribution('project').versionexcept: VERSION = __import__('project').__version__

Prend en compte la version "installée" (ex: 0.1.0.dev1234)

20

Page 21: Python packaging

Entry Points

Page 22: Python packaging

Console scriptsPas besoin de répertoire bin

entry_points={ 'console_scripts': [ 'myexec = project.commands:main', ]}

$ myexec

22

Page 23: Python packaging

Créer ses propres commandesentry_points = { 'distutils.commands': 'do_it = project.commands:DoSomething',},

from setuptools import Command

class DoSomething(Command): description = "Do something" user_options = []

def initialize_options(self): pass

def finalize_options(self): pass

def run(self): do_something()

23

Page 24: Python packaging

Chargement d'extensionsUn project qui exporte

entry_points = { 'myproject.plugins': [ 'someplugin = other_project.plugins:SomePlugin', ],},

Un autre qui importe

import pkg_resources

for entrypoint in pkg_resources.iter_entry_points('myproject.plugins'): plugin = entrypoint.load()

24

Page 25: Python packaging

Un peu de lecture• Documentation officielle de setuptools

• The Hitchhiker's Guide to Packaging

• PEP 386 (numéro de version)

• PEP 396 (version d'un module)

• PEP 345 (métadonnées)

• PEP 426 (métadonnées 2.0)

• semver

25

Page 26: Python packaging

Questions

Page 27: Python packaging

A suivre...• présentation:

• http://noirbizarre.github.io/slides/paris.py/

• http://slides.noirbizarre.info/paris.py/

• blog: http://noirbizarre.info

• twitter: @noirbizarre

• google+: noirbizarre

27

Page 28: Python packaging

Extras

Page 29: Python packaging

Layoutfi── docfi── myproject│ fi─ __init__.py│ └─ ..fi── requirements│ fi─ develop.pip│ fi─ install.pip│ fi─ tools.pip│ └─ test.pipfi─ .gitignorefi─ Makefile/Fabfilefi─ bumpr.rcfi─ CHANGELOG.rstfi─ pep8.rcfi─ pylint.rcfi─ MANIFEST.infi─ README.rst└─ setup.py

29