État de l'art du packaging python
La distribution des programmes python a toujours été décrit comme étant la bête noire des développeurs python. Les choses ont changés en 2013 mais il reste beaucoup de documentation obsolète sur internet et la confusion règne encore.
Qu’est ce qu’un package ?
Un package, ou dans la terminologie python une distribution c’est:
- Un ensemble de modules python
- Potentiellement aussi des modules en C
- Des métadonnées (auteur, version, dépendances etc)
Pour créer un package on crée un fichier setup.py
qui va avoir pour fonction:
- Pour le développeur, de créer une distribution et de l’uploader sur http://pypi.python.org.
- Pour l’utilisateur, d’installer le package via la commande
python setup.py install
Il y a deux sortes de distributions:
- Les distributions sources
sdist
c’est généralement un fichier tar.gz - Les distributions compilées
bdist
, là il y a plusieurs formats (egg, .exe etc)
Le format egg est à python ce qu’est le .jar à Java, du moins dans l’idée. Le problème c’est que ce format n’est pas standard, l’outil pip ne sait pas les installer et les eggs ne tiennent pas compte des différents interpréteurs python (CPython, pypy, jython etc).
Petit historique
Tout les développeurs python on déjà entendu des noms comme distutils
,
setuptools
, distribute
et distutils2
. Voilà un petit historique:
- distutils est inclus dans python, mais ne gère pas les dépendances ni les egg.
- setuptools gère les eggs et le dépendances. Le projet a eu des problèmes de maintenance dans le passé mais il est maintenant activement développé.
- distribute a été un fork de setuptools et apporte le support python 3, mais en mars 2013 les équipes de setuptools et de distribute ont décidés de re-merger leur code dans setuptools depuis la version 0.7
- distutils2 n’est plus maintenu.
Pour résumer, il faut maintenant utiliser setuptools
.
pip et pypi.python.org
pip est un gestionnaire de package
pour python. Il va télécharger les distributions dans l’index
pypi et les installer en tenant compte des dépendances.
Certains packages sont référencés sur l’index sans pour autant que les
distributions ne soient disponibles sur pypi, du coup pip avant la version 1.5
allait chercher par défaut des distributions dans les urls définies par le
développeur dans le fichier setup.py
et l’installation des packages
pouvait prendre des plombes et parfois ne pas aboutir suivant la disponibilités
des différents sites. Une campagne d’information a été faite auprès des
développeurs enregistrés sur pypi pour les inciter à uploader les distributions
directement sur l’index et dans la version 1.5 de pip les liens externes ne
sont plus utilisés par défaut.
Une bonne façon de faire c’est de mettre à jours pip avec pip:
pip install -U pip
Le format wheel
Installer des distributions sources c’est bien, mais ça peut prendre du temps
et ça nécessite pour certains packages d’avoir accès à un compilateur et a des
librairies de développement, choses qu’on ne trouve pas toujours sur un
environnement de production. Pour répondre à ce besoin le format compilé
wheel a été crée. Les wheels
tiennent compte de l’interpréteur et de l’architecture. Et la bonne nouvelle
c’est que pip et setuptools prennent en charge les fichiers .whl
% pip install -U pip
% pip install -U setuptools
% pip install wheel
% pip wheel --wheel-dir=/tmp/wheels numpy==1.8.0
[....]
Running setup.py bdist_wheel for numpy
Destination directory: /tmp/wheels
% ls -l /tmp/wheels/
numpy-1.8.0-cp27-none-linux_x86_64.whl
% time pip install -f /tmp/wheels --no-index numpy==1.8.0
pip install -f /tmp/wheels --no-index numpy==1.8.0 0.96s user 0.12s system 99% cpu 1.083 total
% Et voilà numpy installé en moins d'une seconde !!
Ainsi un fichier comme Django-1.6.2-py2.py3-none-any.whl
est compatible
python 2 et 3 pour tout interpréteur alors qu’un fichier comme
numpy-1.8.0-cp27-none-linux_x86_64.whl
n’est compatible qu’avec CPython 2.7
sur linux 64 bit.
Toutefois si votre package utilise des librairies partagées (comme c’est le cas
pour numpy), le fichier wheel va dépendre de la version de l’ABI de la
librairie partagée. Donc il n’est pas conseillé de le partager directement sur
l’index. Par contre vous pouvez très bien préparer des wheels pour un OS cible
à partir d’un fichier requirements.txt
.
% pip wheel --wheel-dir=/var/www/wheels/wheezy64 -r requirements.txt
Et sur la machine cible en production:
% pip install -f http://mirror/wheels/wheezy64 --no-index -r requirements.txt
Conclusion
Un pip/setuptools à jours, des wheels précompilés et voilà comment déployer du
python sereinement même quand pypi.python.org
est down le tout sans
compilateur et en quelques secondes. Le packaging python devient enfin
utilisable en tenant compte des contraintes de la production et avec les mêmes
outils que le développeur utilise (pip/virtualenv/setuptools/wheel).
Voir aussi:
- Le très bon The hacker’s guide to python, un chapitre est consacré au packaging.
- pbr, un bon outil pour distribuer les packages si vous utilisez git.
- La conférence de Christophe de Vienne au PyConFR 2013