<?xml version="1.0" encoding="utf-8"?>
<entry title="Mais quelle galère que la distribution de programmes Python packagés !">
<date>2011-12-10</date>
<content>
<p>Je sais, je ferais mieux d'être constructif, au lieu de râler, et
de dire du mal des autres programmeurs. Mais c'est trop énervant, il
faut que je me défoule : lorsqu'on écrit un programme en
<wikipedia name="Python (langage)">Python</wikipedia>, et qu'il dépend de plusieurs paquetages
(dont certains peuvent être installés et d'autres pas), c'est très
galère. Il n'existe aucun mécanisme standard pour indiquer dans la
distribution quels paquetages sont nécessaires.</p>
<p>Démonstration avec la bibliothèque <link
url="https://github.com/bortzmeyer/seenthis-python">seenthis-python</link>,
qui permet d'accéder au service de court-bloguage et de partage de
liens <link local="seenthis">SeenThis</link>. seenthis-python dépend
de deux paquetages, <link url="http://www.owlfish.com/software/simpleTAL/">SimpleTAL</link>, un système de
<wikipedia name="Gabarit (mise en page)">gabarits</wikipedia>, pour générer du
<wikipedia name="Extensible Markup Language">XML</wikipedia> propre, et <link
url="http://code.google.com/p/feedparser/">FeedParser</link>, pour analyser les messages en
<wikipedia>Atom</wikipedia> que renvoie SeenThis. Dans la distribution de
seenthis-python, comment indiquer (à part par du texte libre dans le
README) qu'on dépend de ces paquetages ?</p>
<p>Commençons par le grand classique, car c'est le seul distribué avec
Python, dans la bibliothèque standard, et sur lequel tout le monde
peut compter : <link
url="http://docs.python.org/distutils/">distutils</link>. Le principe
est qu'on écrit un fichier <computer>setup.py</computer> qui va
indiquer un certain nombre de choses sur le programme :
<code>
from distutils.core import setup

setup(name='SeenThis',
      version='0.0',
      description='Use the SeenThis API',
      license='BSD',
      author='Stephane Bortzmeyer',
      author_email='stephane+seenthis@bortzmeyer.org',
      url='https://github.com/bortzmeyer/seenthis-python',
      )
</code>
Ce fichier peut ensuite être exécuté avec des options qui vont dire
par exemple d'installer le paquetage (<computer>python setup.py
install</computer>) ou de l'enregistrer dans le <foreign><link
url="http://pypi.python.org/">Python Package Index (PyPI)</link></foreign> (<computer>python setup.py register</computer>).
</p>
<p>distutils a des tas de limites. Par exemple, il ne permet pas de
faire des <foreign><wikipedia xml:lang="en"
name="Python eggs">eggs</wikipedia></foreign>, des paquetages binaires pour
Python. Mais, surtout, il ne permet pas d'exprimer des dépendances :
aucun moyen dans le script ci-dessus de dire qu'on a besoin de
SimpleTAL et de FeedParser. Le script <computer>setup.py</computer>
s'exécutera et installera la bibliothèque, même si les pré-requis
manquent. En lisant <link
url="http://docs.python.org/distutils/setupscript.html">la
documentation</link>, on a l'impression qu'on peut ajouter :
<code>
      requires=['SimpleTAL', 'FeedParser']
</code>
mais c'est purement décoratif. La documentation ne le dit pas mais
cette variable est complètement ignorée par distutils. 
</p>
<p>Au passage, le langage d'expression des dépendances (qui ne sert à
rien, on l'a vu) permet également d'indiquer un numéro de version, par
exemple le fait que, pour FeedParser, en raison de <link url="http://code.google.com/p/feedparser/issues/detail?id=91">la
bogue #91</link> (« <computer>sgmllib.SGMLParseError: unexpected
'\xe2' char in declaration</computer> »), il faut au moins la version 5.</p>
<p>Lorsque le débutant en Python se plaint de cet état de chose, les
vieux experts blanchis sous le harnais lui rétorquent que tout le
monde le sait, et que personne n'utilise le système standard (alors,
pense le débutant naïf, pourquoi est-ce que ce truc reste dans la
bibliothèque standard ?). Et c'est
là que les choses se compliquent, car il existe plusieurs remplaçants possibles.</p>
<p>D'abord, le premier, <link
url="http://pypi.python.org/pypi/setuptools">setuptools</link> (le
lien pointe vers PyPi puisque, à partir de là, on n'est plus dans la
bibliothèque standard, ce qui oblige l'utilisateur qui va installer
la bibliothèque à installer un autre programme d'abord, alors que le
but était de lui simplifier la vie). Il existe une bonne <link url="http://packages.python.org/an_example_pypi_project/setuptools.html
"> documentation pour débutant</link>. En gros, on remplace la
première ligne de code du <computer>setup.py</computer> par :
<code>
from setuptools import setup
</code>
et on a alors des fonctions et variables supplémentaires comme
<computer>install_requires</computer> qui permet d'indiquer des
dépendances. Tout content, le programmeur Python naïf édite le README
pour indiquer à ses futurs utilisateurs qu'il va falloir installer
setuptools d'abord avant de faire le <computer>python setup.py
build</computer>. Mais il va vite être déçu : même si SimpleTAL est
installé, le <computer>setup.py</computer> va tenter de le télécharger
(et échouer, car il n'est pas dans PyPi). En effet, setuptools ne
teste pas si un module Python donné est disponible (<computer>import
simpletal</computer>), il regarde s'il y a un <foreign>egg</foreign> du même
nom. Ce qui n'est pas le cas. Les dépendances de setuptools sont donc
vers les <foreign>eggs</foreign>, pas vers les modules, ce qui ôte une
bonne partie de leur intérêt.</p>
<p>D'autant plus que, on l'a vu, des paquetages fréquemment répandus,
comme SimpleTAL, ne sont pas forcément disponibles dans PyPi... (SimpleTAL est arrivé plus tard.)</p>
<p>Bon, troisième tentative : <link
url="http://packages.python.org/distribute/">distribute</link>, qui
prétend remédier aux problèmes de setuptools. Il reprend en partie son
nom (ce qui contribue beaucoup à la confusion des programmeurs, par
exemple le paquetage Debian de distribute se nomme setuptools) :
<code>
from distribute_setup import use_setuptools
use_setuptools()
from setuptools import setup

[puis setup.py comme avant]
</code>
Comme setuptools, il permet de
<link
    url="http://packages.python.org/distribute/setuptools.html#declaring-dependencies">déclarer
des dépendances</link>, mais il les gère tout aussi mal :
<code>
# python setup.py install
...
Searching for simpletal
Reading http://pypi.python.org/simple/simpletal/
Reading http://www.owlfish.com/software/simpleTAL/index.html
No local packages or download links found for simpletal
error: Could not find suitable distribution for Requirement.parse('simpletal')

%  python
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import simpletal
>>> 
</code>
(La dernière commande montre que SimpleTAL est pourtant bien installé.)
</p>
<p>Bref, on a trois systèmes concurrents et dont aucun ne permet
d'assurer une tâche aussi simple que « vérifie que simpletal est bien
disponible ». Pire, rien dans la documentation n'indique leurs
limites. Ainsi tous les programmeurs Python semblent savoir que
distutils est très limité mais cela n'apparaît pas dans la
documentation de la bibliothèque standard. Et ce n'est pas fini. Comme l'écrit Victor Stinner «Le packaging Python est une série à la <wikipedia name="Dallas (feuilleton télévisé)">Dallas</wikipedia> avec ses drames et ses
rebondissements. Le dernier événement marquant est que la réécriture de
distutils, "distutils2", a été intégrée dans Python 3.3 sous le nom
"packaging". Sa version pour Python 2.5 - 3.2 sera maintenue en dehors
de Python sous le nom "distutils2". » Il y a aussi des compétiteurs comme <link url="http://cournape.github.com/Bento/">Bento</link> ou 
plus exotiques comme <link url="http://pypi.python.org/pypi/zc.buildout">zc.buildout</link>.
</p>
<p>Le problème du <foreign>packaging</foreign> Python est discuté avec
bien plus de détails techniques (et moins de râleries pas
constructives) dans l'excellent article <foreign>Python packaging</foreign> de
Tarek Ziadé dans le livre
<foreign><link
	     url="http://www.aosabook.org/en/index.html">Architecture
of Open Source Software</link></foreign>, (co-dirigé par Greg Wilson et Amy Brown).</p>
<p>Un petit truc : si on veut regarder la variété des fichiers
<foreign>setup.py</foreign> possibles, on peut <link
url="http://www.google.com/codesearch?hl=en&amp;start=10&amp;sa=N&amp;q=requires+lang:python+file:setup.py">demander
à Google</link> (ici, en se limitant à ceux qui contiennent <computer>requires</computer>).</p>
<p>Pour terminer, une bonne série de lectures, due à Nicolas Chauvat :
<enum>
<item><foreign><link url="http://www.logilab.org/blogentry/9860">The
Configuration Management Problem</link></foreign>,</item>
<item><foreign><link
		   url="http://www.logilab.org/blogentry/9861">Looking
for a Windows Package Manager</link></foreign>,</item>
<item><foreign><link
		   url="http://www.logilab.org/blogentry/22498">Virtualenv - Play safely with a Python</link></foreign>,</item>
<item><foreign><link
		   url="http://foobar.lu/wp/2012/05/13/a-comprehensive-step-through-python-packaging-a-k-a-setup-scripts/">A
comprehensive guide through Python packaging (a.k.a. setup
scripts)</link></foreign>, un bon document d'introduction pour ceux
qui veulent souffrir le moins possible,</item>
<item><foreign><link url="http://www.logilab.org/blogentry/60163">PyPi
is not a software distribution</link></foreign>,</item>
<item><foreign><link
		   url="http://www.logilab.org/blogentry/57611">Sprint
Distutils2</link></foreign> et <link
url="http://www.logilab.org/blogentry/60262">la suite</link>,</item>
<item>Un <wikipedia xml:lang="en" name="Python (programming language)" anchor="Development">PEP</wikipedia> qui spécifie un format de fichier qui
décrit les dépendances d'un module python <link
url="http://www.python.org/dev/peps/pep-0345/">PEP345</link>, 
et qui, si un outil s'en servait, simplifierait
formidablement la vie de ceux qui font des paquets pour les
distributions.</item>
</enum>
</p>
</content>
</entry>