Stella

Pour vous accompagner dans vos choix numériques

L’enfer des paquets Python : les racines du mal (2 / 7)

La gestion des paquets Python est parfois un enfer. La faute à la stupidité des personnes derrière Python ? Et si c’était un peu plus compliqué que cela ?

💕💕💕

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 (à venir)
  7. La solution minimale (à venir)

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 😭.

💕💕💕

Ce n’est pas la bonne techno

Nous avons vu la dernière fois qu’il était parfaitement possible de faire les choses bien concernant la création et la distribution de paquets, qu’il n’y avait qu’à regarder ce que faisaient par exemple les gens de Rust, et que ce n’était pas somme toute si compliqué.

Ce n’est pas totalement faux, mais soyons honnêtes, ce n’est pas totalement vrai non plus. Tout d’abord, Rust bénéficie d’atouts techniques indéniables par rapport à Python, avec par exemple la possibilité de distribuer des bibliothèques et des exécutables compilés. Python étant interprété, se pose la question de la distribution et de l’installation de l’interpréteur, avec tout son lot de questions annexes (quel interpréteur, quelle version…). Certains outils comme Nuitka visent à apporter des embryons de solutions dans ce sens, mais ils sont condamnés à vivre leur existence en dehors de la bibliothèque standard, et donc probablement d’une utilisation massive.

Au-delà de l’aspect technique, le principal avantage de Rust est son âge. Créé en 2010, il est un jeune enfant comparé à l’antique Python né environ 20 ans plus tôt. Entre 1990 et 2010 sont apparus CVS, Subversion et Git ; Netscape, Internet Explorer, Firefox et Chrome ; HTML, CSS et JavaScript. C’est incroyable, mais c’est vrai, et ça met un peu en perspective la situation relative de Python et Rust.

Ce n’est pas le bon moment

On a du mal à se souvenir ou imaginer comment était l’informatique quand Python a commencé à germer à la fin des années 80, mais on arrive sans grande peine à comprendre pourquoi la création et la distribution de paquets n’étaient pas à la pointe des problèmes à prendre en compte.

Écran de démarrage de Netscape 6
Le bel écran de démarrage de Netscape 6 sorti en 2000, la même année que distutils.

Python n’a bien sûr pas intégré d’outils pour distribuer le code dès ses débuts. La Python Package Authority (PyPA) maintient un historique très instructif de l’évolution des paquets, où l’on apprend entre autres que :

Le plus frappant est peut-être l’impression d’amateurisme, en particulier à la naissance des premiers outils. Quiconque a déjà utilisé easy_install sait que la situation était alors extrêmement douloureuse, que l’installation d’un paquet nécessitait une dose non négligeable de persévérance, de connaissances techniques, et bien sûr de chance. L’accumulation de noms, d’outils, de bibliothèques internes et externes montre que tout le monde est parti bille en tête dans des solutions partielles, hasardeuses, voire carrément bancales.

Comme on peut l’entendre dans cet excellent épisode de Podcast.__init__, l’écriture des outils a longtemps été faite avant de soumettre à discussion et acceptation une spécification formelle. Par manque de temps, et par manque de moyens également : il n’y a pas d’entreprise derrière PyPI ou PyPA, comme il y en a par exemple derrière npm. La grande majorité des développements sont faits par des bénévoles, qui ne seraient pas contre un coup de main ✋ (oui, c’est un appel du pied 🦶).

Certes, cet état des lieux n’explique pas tout. D’autres changements majeurs ont été intégrés dans le langage avec beaucoup plus de tact, en particulier récemment les coroutines. Cette fonctionnalité a nécessité une discussion large et houleuse avant une intégration plutôt appréciée. Avant de graver ces changements de syntaxe dans le marbre du langage, des propositions moins intrusives ont été testées et éprouvées, rendant la solution finalement retenue plus largement acceptable.

Il est facile de jeter ses pierres acérées sur les gens qui ont créé ces outils. Il est facile de vilement railler le manque de vision de l’équipe derrière Python, qui a intégré à la va-vite certaines solutions discutables et qui en a laissé d’autres aux portes de la bibliothèque standard. Mais si l’on pense que distutils a été intégré à un moment où le navigateur en vogue s’appelait Internet Explorer 5.5…

Et en plus, il n’est pas trop tard pour changer tout cela. Si ?

Ce n’est pas le bon standard

En quelque sorte, malheureusement, si, c’est trop tard.

Mandatory Related XKCD™
Le XKCD obligatoire

Le problème est qu’il existe depuis le début des années 2000 des paquets dans la nature, sur PyPI, dans des dépôts publics et privés. Ces paquets sont utilisés par de nombreuses personnes, parfois sur des systèmes obsolètes (je te regarde, Python 2). Et Python, qui a déjà douloureusement éprouvé les changements brutaux et incompatibles (je te regarde, Python 3) ne voudra certainement pas rendre ininstallable cette masse informe et nébuleuse de paquets.

Sinon, ça va sortir les fourches et les guillotines.

Toutes les propositions, que ce soit dans les fichiers (requirements.txt, setup.py, Pipfile, setup.cfg, pyproject.toml…) ou dans les outils (easy_install, pip, pipenv, poetry, setuptools, distutils…) ne peuvent que s’ajouter les unes aux autres, sans jamais parfaitement les remplacer, sans jamais jeter aux oubliettes les défauts de leurs illustres prédécesseurs.

Alors évidemment, tout n’est pas perdu. Les changements les plus importants vont aujourd’hui globalement dans le sens d’une spécification, d’une rationalisation et d’une simplification systématiques. De nombreuses PEP sont proposées pour décrire et discuter avant de coder, pour que les détails d’implémentation ne fassent plus la loi au détriment d’idées nées de consensus.

Mais le chemin est long.

Ce n’est pas la bonne solution

Si vous pensez que la gestion de dépendances est un problème résolu de longue date et que les personnes derrière pip manquent sérieusement de compétences, rappelez-vous que le nom d’un paquet Python est souvent donné dans le fichier setup.py, et qu’il peut donc en théorie dépendre d’informations comme le système d’exploitation, la présence de modules externes ou même de l’heure qu’il est. Connaître les modules dont dépend un paquet nécessite parfois de lancer un interpréteur, et l’arbre de dépendances est donc différent pour chaque personne qui installe un paquet.

En pratique, des solutions ont été trouvées pour pallier cette flexibilité qui vire clairement au cauchemar dans certains cas. Il n’est bien heureusement pas nécessaire de télécharger et d’exécuter toutes les versions possibles de tous les paquets avant de déterminer celles qu’il faut installer. Mais tout cela se fait avec de nouveaux formats et de nouvelles métadonnées qui, par définition, ne font pas partie des paquets précédents.

Concrètement, cela signifie que pour les outils d’installation des paquets, la gestion des solutions obsolètes va encore durer de nombreuses et douloureuses années. Le rythme effréné des nouvelles versions de setuptools et pip donnent une idée des améliorations constantes qui se trament sans que nous nous en rendions compte ; mais nous ne sommes pas prêts de sortir de l’historique démoniaque que nous devrons trimballer longtemps encore.

Cependant, pour les personnes qui créent des paquets, la solution s’améliore considérablement. Le sujet est relativement peu traité et il est compliqué de trouver une documentation de référence sur le sujet (même de la part de PyPA, surtout de la part de PyPA). C’est bien dommage, parce qu’il deviendrait presque facile et élégant ces derniers mois de faire des paquets Python.

Sans setup.py du tout, par exemple.

Vous aimeriez en savoir plus ? Il nous reste trois articles pour faire un tour de l’outillage existant, mieux définir ce que nous voulons faire, avant de finalement créer notre paquet de toute beauté.

À suivre…