É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: