Tour d'horizon en 15 minutes
Par Axel Haustant
Être réutilisable
La solution:
setuptools
!
from setuptools import setup
setup(
name='my-project',
version='0.1.0',
# ...
)
C'est du Python donc tout est permis !
extras_require = {
'tests': ['factory-boy']
}
$ pip install my-project[tests]
Doit permettre de démarrer rapidement.
Pour ê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.
Contrôlez ce que vous allez publier
$ python setup.py --long-description | rst2html
$ python setup.py sdist
# 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
Selon la PEP 396, le module doit contenir un attribut __version__
from project import __version__, __description__
setup(
name='project'
version=__version__
description=__description__
)
RE_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'),
},
)
PYPI_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'),
''
))
try:
from pkg_resources import get_distribution
VERSION = get_distribution('project').version
except:
VERSION = __import__('project').__version__
Prend en compte la version "installée" (ex: 0.1.0.dev1234)
Pas besoin de répertoire bin
entry_points={
'console_scripts': [
'myexec = project.commands:main',
]
}
$ myexec
entry_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()
Un 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()
présentation:
├── doc
├── myproject
│ ├─ __init__.py
│ └─ ..
├── requirements
│ ├─ develop.pip
│ ├─ install.pip
│ ├─ tools.pip
│ └─ test.pip
├─ .gitignore
├─ Makefile/Fabfile
├─ bumpr.rc
├─ CHANGELOG.rst
├─ pep8.rc
├─ pylint.rc
├─ MANIFEST.in
├─ README.rst
└─ setup.py