Stella

L’enfer des paquets Python : la boîte à outils (5 / 7)

La gestion des paquets Python est parfois un enfer. Pour créer, envoyer, installer des paquets, nous avons à disposition beaucoup d’outils, qui font parfois la même chose, mais pas de la même manière.

💕💕💕

Cet article fait partie d’une série de 7 articles larmoyants sur la création de paquets Python :

  1. Le sac de nœuds
  2. Les racines du mal
  3. La folie des formats
  4. Des fichiers partout
  5. La boîte à outils
  6. L’expression des besoins
  7. La solution minimale

Avant toute autre chose, j’aimerais faire un gros bisou à l’ensemble des membres de l’équipe PyPA. Je me plains beaucoup dans cette suite d’articles, cela ne m’empêche pas d’avoir une immense et sincère dose de respect pour le travail sisyphéen déjà accompli.

Ceci étant dit : nous voilà (re)partis pour le chouinage 😭.

💕💕💕

Des outils pour tout faire

Des bibliothèques, des scripts, des exécutables… Même en tant que simple utilisateur, il faut connaitre et tester une bonne dose d’outils avant d’utiliser un programme Python. Vous allez devoir créer un environnement virtuel pour installer des paquets. Avec le temps, vous aurez vos petites habitudes, qui changeront au gré des évolutions et des bonnes pratiques.

Nous n’allons pas vraiment parler de cela. Mais un peu quand même.

Si l’on se concentre sur la gestion de paquets, nous pouvons déterminer trois opérations spécifiques : l’installation, la création, l’envoi sur un dépôt de paquets. Chacune de ces opérations peut être découpée en de plus petites étapes, et ce serait intéressant de comprendre dans le détail comment tout cela fonctionne.

Nous n’allons pas parler de cela en détail non plus.

Ce que nous allons tenter de faire : dresser un tableau partial, superficiel, approximatif et non-exhaustif de ce que nous pouvons utiliser pour faire ces trois opérations de base. Ça n’envoie pas du rêve mais c’est déjà, mine de rien, une tâche complexe.

Nous aurons forcément à mettre un pied dans le détail de fonctionnement des opérations, nous aurons forcément à mettre un pied dans les environnements virtuels. Mais nous tâcherons de ne pas nous perdre dans des détails, sinon nous en aurions pour − au bas mot − des heures. Et vous avez mieux à faire.

Commençons donc par deux tableaux. Le premier liste les bibliothèques, utilisables par d’autres outils.

Bibliothèque Installation Création Envoi
distutils Oui Oui Non
setuptools Oui, basé sur distutils Oui, basé sur distutils Oui, jusqu’à la version 42

Le second tableau liste les outils, qui proposent des exécutables.

Outil Installation Création Envoi
easy_install Oui, distribué avec setuptools Non Non
pip Oui, inclut une copie partielle de setuptools Oui, des wheels avec setuptools et wheel Non
wheel Non Oui, des wheels Non
twine Non Non Oui
pipenv Oui, inclut une copie modifiée de pip Non Non
pipx Oui, basé sur pip Non Non
poetry Oui, basé sur pip Oui Oui
flit Oui, basé sur pip Oui Oui

Ces deux tableaux ne listent que les fonctionnalités qui nous intéressent, mais certains outils font bien d’autres choses. Ce n’est donc pas la peine de les comparer bêtement selon le nombre de cases qu’ils cochent, d’autant plus que d’autres paramètres doivent rentrer en compte dans cette comparaison, y compris des petits détails tels que la qualité et la maintenabilité du code.

Mandatory Related XKCD™
Le XKCD obligatoire. Prendre quelques points de comparaison n’a jamais été suffisant. N’essayez jamais ça chez vous.

Et d’ailleurs, nous n’allons pas à proprement parler comparer ces outils ; nous allons plutôt présenter les trois fonctionnalités et décrire les bibliothèques et outils qui les accomplissent. Ce n’est pas la peine de bouder, je vous assure que ce sera plus simple ainsi.

Installation de paquets

Au début, lorsque la notion de paquets a été introduite dans Python en 2000, c’est distutils qui a été chargé de créer et installer les paquets. Nous l’avons déjà dit dans les épisodes précédents, mais nous allons le répéter : il l’a fait sans gestion de dépendances et sans dépôt de paquets.

distutils est une bibliothèque qui permet avant tout d’écrire des fichiers setup.py, qui ont longtemps été l’alpha et l’oméga des paquets Python. Grâce à distutils qu’ils importent, ces fichiers sont exécutables et offrent deux commandes : install pour installer, sdist pour créer le paquet. On peut dès lors partager des archives qui contiennent l’ensemble du code, et les installer après les avoir décompressées. En d’autres termes : ces archives sont des paquets.

L’idée d’avoir un endroit où stocker et partager ces paquets arrive vite. PyPI est mis en ligne trois ans après l’arrivée de distutils ; il permet de partager et de retrouver, à un endroit public central, un grand nombre de paquets Python.

La bibliothèque setuptools arrive en 2004 pour apporter de nouvelles fonctionnalités, en particulier la gestion de dépendances. Au niveau de l’installation de paquets, c’est la révolution : easy_install est inclus dans la bibliothèque et permet d’installer les paquets de PyPI directement avec leur nom.

Cependant, setuptools et easy_install vont eux aussi commencer à montrer leurs limites. Créés sans véritable spécification, basés sur un format de paquets imparfait (les eggs), ils vont attiser les volontés de remplacement.

Ce sera le cas pour easy_install qui sera remplacé 4 ans plus tard par pip. pip a pour but premier d’installer des paquets Python en gérant correctement leurs métadonnées, ce qui permet entre autres de lister et désinstaller les paquets installés.

pip a beaucoup évolué et est aujourd’hui l’application de référence concernant l’installation de paquets. L’outil a su s’adapter aux différentes évolutions et est aujourd’hui capable de se débrouiller avec les paquets sources et les wheels. Son architecture réfléchie et sa large utilisation lui ont permis d’intégrer des évolutions étape par étape, et de rester vivant depuis plus de 10 ans.

L’outil est utilisé ou inclus dans tous les outils récents d’installation de paquets. Si l’on prend certains outils largement utilisés comme Pipenv, Poetry et pipx, tous lancent pip pour l’installation de paquets.

Alors, pourquoi utiliser d’autres outils que pip ? Pipenv, Poetry, pipx et d’autres permettent, chacun à leur manière, de cloisonner les installations. Par défaut, pip installe les paquets dans un dossier central, ce qui est assez gênant lorsque l’on veut avoir différents projets qui utilisent différentes versions de la même bibliothèque.

Pipenv et Poetry offrent à peu près la même mécanique pour l’installation : ils créent un environnement virtuel par projet, dans lequel ils installent les dépendances. Pour cet usage, ils se comportent en réalité comme une simple capsule autour de pip et venv, avec des commandes permettant de gérer les cas les plus simples.

pipx a un but différent : il propose la même interface que pip pour installer des exécutables. Il s’arrange automatiquement pour créer un environnement virtuel par exécutable, et pour rendre cet exécutable accessible à l’utilisateur. Il est donc plus adapté aux utilisateurs finaux qu’au développeurs, cibles privilégiées de Pipenv et Poetry.

Une dernière chose étonnante au sujet de pip : il crée désormais des paquets, pour pouvoir mieux les installer. Pourquoi ? Il vous faudra lire la suite…

Création de paquets

Photo de paquets
Rappel : ce n’est pas parce que l’emballage est joli que le contenu va nécessairement plaire.

Oui, vous avez bien lu : pip crée désormais des paquets. Avec la volonté sous-jacente de se séparer de setuptools et des autres formats de paquets, pip se transforme doucement en un simple installeur de wheels. Lorsqu’il a devant lui un paquet source, il tente désormais de transformer ce paquet source en wheel avant de l’installer, au lieu de passer par l’installeur de setuptools.

Ce système comporte de nombreux avantages. Tout d’abord, cela signifie qu’à terme pip pourrait n’être qu’un installeur de wheels (qui est le format le plus simple à installer) couplé à un transformateur de paquets source en wheels. Un tel fonctionnement simplifierait grandement le code source de pip, qui aujourd’hui fait beaucoup d’autres choses, dont installer des paquets depuis les sources en utilisant setuptools.

Il faut également souligner le fait que, pour créer des paquets, il n’est désormais plus nécessaire d’utiliser setuptools. D’autres outils existent pour générer un paquet source ou un paquet wheel. Cela signifie que, de la création à l’installation, on commence à entrevoir le bout du tunnel : il est possible d’utiliser des outils relativement simples, basés en grandes partie sur des spécifications, qui n’ont pas à gérer l’ancienneté que trimballe setuptools avec lui.

Poetry et Flit sont sur ce point-là assez proches. Les deux sont capables de créer des paquets sans setuptools et donc sans fichier setup.py. Suivant les PEP 517 et 518, utilisant le fichier pyproject.toml comme unique source d’information, ils proposent une solution alternative pour créer des paquets source et des wheels classiques, installables par pip.

Flit ne fait à peu près que cela. Il contient également de quoi installer un paquet avec pip et par liens symboliques, ce qui est utile pour le développement. Poetry est beaucoup plus complet : il propose comme Pipenv la création d’environnements virtuels utiles pour le développement.

Bien sûr, le passage d’un fichier setup.py en Python à un fichier pyproject.toml limite les possibilités. Malgré les efforts de ces outils pour proposer une grande flexibilité, il n’est pas possible de faire tout ce que l’on pouvait faire avec setuptools, laissant à l’honorable bibliothèque le soin de gérer les cas complexes dont la plupart ne sont que le résultats d’esprits malades relevant plus de la psychiatrie que de l’informatique.

Envoi de paquets

De la même manière, Poetry et Flit proposent tous deux l’envoi de paquets sur PyPI ou sur des serveurs compatibles. Cette fonctionnalité ne nécessite guère que de suivre les APIs HTTP proposées par PyPI, et peut sembler assez simple au premier abord.

Cela n’a cependant pas toujours été le cas. setuptools a longtemps proposé une commande pour envoyer les paquets, non sans problèmes. Afin d’assurer une compatibilité avec toutes les versions de Python, il a fallu jongler avec les versions de protocoles TLS supportées, les failles de sécurité, les mots de passe… Et bien sûr, ce qui devait rester assez simple s’est vite transformé en triste cauchemar de soupe de code indigeste.

Pour régler ce problème, le projet Twine a été développé. Twine a comme unique but d’envoyer les paquets sur PyPI, et de le faire bien. Il propose en bonus la possibilité de stocker le mot de passe dans le gestionnaire de mots de passe du système, plutôt qu’en clair dans un fichier texte comme le proposait setuptools. Autre détail : Twine envoie tels quels les fichiers générés par l’outil de créations de paquets. Cela peut paraître comme la moindre des choses, mais il faut savoir que setuptools recrée le paquet avant de l’envoyer, compliquant grandement les tests.

On résume (enfin, on essaie)

Il y eut une longue période de dépendance à setuptools, ses incompréhensibles fichiers de configuration, son implémentation qui fait foi, son architecture vieillissante et ses commandes discutables. Mais cette époque est bientôt révolue, et d’autres solutions existent d’ores et déjà pour créer et envoyer ses paquets.

Nous sommes dans une période d’incertitudes. Il est difficile, voire impossible, de savoir quels outils seront utilisés demain. Il est difficile de construire des paquets avec une architecture qui résistera à l’épreuve du temps. Mais une chose est sûre : nous avons plus de libertés et de possibilités que jamais.

Après tout, avoir de nombreux outils pour créer les paquets n’est pas un mal en soi, tant qu’ils créent tous des paquets interopérables, installables par les mêmes outils. On n’a pas les mêmes besoins lorsqu’on crée un tout petit paquet en Python pur dans son coin, ou un paquet contenant du C destiné à toutes les plateformes.

À ce sujet Flit propose une vision intéressante :

« Rendre faciles les choses faciles, et possibles les choses difficiles », telle est une vieille devise de la communauté Perl. Flit se concentre uniquement sur la partie « choses faciles », et laisse les choses difficiles aux autres outils.

(On en arrive à s’inspirer de la communauté Perl, tout est possible…)

Tout dépend de ce que l’on veut faire. Et ça tombe bien : c’est ce que nous verrons dans le prochain article !