Vive setup.cfg (et mort à pyproject.toml) !


Après un long débat sur hackernews, qui n’est que le reflet de toutes les conversations que j’ai déjà eues à ce sujet sur twitter, github, et divers mailling lists, il est grand temps de faire un article. Urgent même.

Est-ce que vous savez quel chemin de croix on a vécu avec le packaging Python durant ces 15 dernières années ?

D’abord on a distutils, setuptools, distribute, and distribute2 qui ont tous été à un moment les “standards” recommandés pour packager une lib. Ensuite on a eu l’époque des eggs, exe, et autres trucs que easy_install allait chercher n’importe où dans la nature en suivant aveuglément des liens sur PyPi. Sans compter les machins qu’il fallait compiler à tout bout de champ. Et puis rien n’était chiffré au download, pip n’était pas packagé avec Python, il crevait sur des erreurs stupides type encodage mal géré…

À ça se rajoute que virtualenv était un truc à part, avec plein de concurrents, et linkait les packages système par défaut. Sans oublier qu’on avait pas Python -m.

Bref, le packaging Python, ça a été vraiment la merde. Avec en plus une doc de merde.

Aujourd’hui, le standard wheel a énormément amélioré la donne. On a des tutos corrects (ex: notre tuto sur comment créer son package avec setup.py). ensurepip fait qu’on a une version récente du truc presque partout.

En gros, notre situation est stable, saine. Améliorable de bien des façons, certes, mais un bon socle sur lequel s’appuyer.

Arrive setup.cfg

En 2016, l’équipe de setuptools, la lib utilisée par à peu près tout le monde pour créer des packages en Python aujourd’hui, a créé le format setup.cfg, un fichier INI dont le but est de remplacer setup.py.

Le problème de setup.py c’est que c’est du code Python exécutable, et en plus dépendant de la lib setuptools. Cela empêche non seulement l’interfaçage avec des outils externes, mais aussi freine l’émergence de nouveaux outils.

En effet, il y a un désir de continuer à améliorer la situation du packaging en Python, comme on peut le voir avec des projets comme pipenv ou poetry (je recommande d’ailleurs fortement ce dernier, et je vous invite à voter sur cette issue pour enfoncer le clou).

setup.cfg est la suite logique de tout ça, un format en texte brut, facile à manipuler pour le reste du monde.

Et ça a été bien fait:

  • setup.cfg marche out of the box. Il n’y a rien à faire de particulier : si vous aviez un setup.py, vous pouvez juste le convertir en setup.cfg. Si vous avez un nouveau projet, vous pouvez juste écrire le setup.cfg. Il n’y a pas de piège. Ça marche depuis… 2016. Ouais.
  • setup.cfg a une documentation décente. Si, si. C’est très améliorable, mais mieux que tout ce qu’on a jamais eu pour les premières années de setup.py
  • setup.cfg couvre la plupart des cas de figure que setup.py faisait et se permet de rajouter des goodies très cools. version = attr: src.__version__ inclue la version depuis __init__.py, "options.data_files" remplace le MANIFEST et "license = file: LICENCE.txt" injecte le corps de la licence depuis un fichier texte. Tous les hacks à la noix de setup.py ? Fini.
  • C’est simple à utiliser. Voici quelques projets pour démontrer le bouzin: exemple 1, exemple 2, exemple 3, exemple 4. Ça se comprend même sans lire la doc !
  • C’est compatible avec tous les outils legacy. En fait, il suffit de créer un fichier setup.py avec une seule ligne dedans (import setuptools; setuptools.setup()) et pouf, tout le worflow d’avant marche: python setup.py develop, python setup.py sdist upload, etc. Du coup, tout ce qui comprenanait setup.py (c’est à dire le putain de monde entier en Python) marche avec setup.cfg.

Donc setup.cfg, malgré des lacunes, c’est sympa.

Setup.cfg, ou es-tu ?

“Attend une minute, si c’est si bien que ça, pourquoi j’en ai jamais entendu parler ? Moi on m’a dit que setup.cfg il servait à rien…”

Je ne vous le fais pas dire !

Je n’ai aucune idée de la raison pour laquelle cette information ne circule pas plus. La seule raison pour laquelle je l’ai trouvée c’est parce que je passe des heures à faire de la veille informationnelle en Python, et que je suis tombé dessus au détour d’un carrefour, dans une ruelle sombre du Web. Et que j’ai pris le temps et le risque de le tester sur un de mes projets pour vérifier que oui, ça marche comme prévu.

Même la doc Python vous fait croire que setup.cfg c’est un artéfact vaguement utile pour une pauvre option monoligne.

Et merde, j’ai déjà écrit 900 articles sur ce blog, je ne peux pas mettre à ma charge d’avertir tout le monde pour chaque truc à savoir sur Python.

C’est comme pour le # -*- coding: utf-8 -*-. Un jour un mec utilisant Emacs a écrit un tuto avec ça, et tout le monde a copié-collé les hiéroglyphes alors qu’en fait # coding: utf8 est parfaitement valide. Ou comme nuikta, qui permet de compiler de Python de manière fiable, et que personne ne connait. Combien de temps doit-on gâcher avec des trucs comme ça ?

pyproject.toml vient foutre le bordel

Maintenant, figurez-vous que nous avons un groupe de personnes chargées de faire évoluer la situation du packaging, la Python Packaging Authority, ou PyPA.

Une très bonne initiative, vu que le free-for-all du passé ne nous avait pas trop réussi. Et un succès puisque la situation actuelle en packaging Python est maintenant beaucoup plus propre.

Et on en a besoin. En effet, setup.cfg doit être amélioré car il a certains défauts. Le format n’est pas parfaitement standardisé. Seule la doc fait figure de description, et pour l’instant setup.cfg, c’est whatever configparser comprends, sachant que configparser parse les trucs au motoculteur, et selon la version de Python. C’est pas gravissime, et ça n’a pas vraiment été un problème jusque là, mais pour la pérennité de la chose, une consolidation est nécessaire. On doit avoir une spec solide, un parseur robuste et stable, etc.

Sachant la purge qu’a été l’historique du packaging en Python, on s’attend donc a ce que la PyPA fasse le choix de standardiser setup.cfg, qui marche depuis 2 ans, fait le job, est compatible avec l’existant et résout déjà le problème de permettre au reste du monde de manipuler les données du package en texte brut. Puis, comme un bon groupe de décision sage, elle va proposer des solutions incrémentales aux défauts de setup.cfg et son écosystème.

Un exemple possible serait de s’allier avec l’équipe de setuptools pour figer le format setup.cfg, clarifier les edge cases de ce INI en particulier, et si besoin, créer une alternative a configparser (ou figer configparser) afin d’obtenir une implémentation de référence irréprochable pour parser le format. Puis désigner ce format comme le nouveau standard, en version 1 implicite, et le documenter puis en faire la promotion afin que tous les nouveaux outils puissent l’utiliser. Ensuite, ayant identifié des limitations, on crée le successeur de ce format, qui aura le même nom, mais un header de version qui lui sera explicite afin de permettre aux parseurs de s’adapter. Et on fait en sorte que cette nouvelle version soit plus propre, plus belle, super green. Et on l’introduit aussi progressivement et en douceur qu’un sex toy anal.

Bref, on s’attend à ce que la PyPA nous amène vers une totale liberté de pensée cosmique vers un nouvel âge réminiscence.

Sauf que non.

Ces ânes ont décidé… de créer un nouveau format et ignorer tout l’existant.

Fuck. That. Je hais que ce dessin de XKCD puisse être d’actualité encore et encore, chaque mois que l’humanité passe:
Le packaging en Python, c'est une forme de solidarité masochiste avec la communauté JS

Je ne comprends pas cette décision, et leurs justifications sont d’une grande faiblesse, pour ne pas dire insultante pour une communauté qui en a marre de payer le prix de l’égo des gens qui ont envie d’avoir leur nom sur la nouvelle barre de fer officielle.

C’est d’autant plus étrange que la PyPA n’est pas composée de cons. Non. On a Brett Cannon (core dev, qui fait un travail exceptionnel avec Python VSCode), Nathaniel Smith (qui nous a révolutionné l’async avec trio) et Kenneth Reitz (l’auteur de Python requests).

Comment ce pet de cerveau a-t-il pu émané de ces brillantes personnes, je ne le sais.

Mais je vous invite tous à non seulement utiliser setup.cfg en masse, mais aussi à activement contester cette décision sur tous les mediums à votre disposition.

Parce qu’évidemment je l’ai faits, et comme d’hab, ils font la sourde oreille. Et on va en payer le prix. Mais bon, depuis le temps, vous avez l’habitude. C’est pas comme si j’avais pas déjà annoncé que redux et dockers étaient overkill pour la plupart des projets, que le NoSQL allait avoir un retour de baton, que vue était génial, flask PAS pour les débutants, bitcoin intéressant, pipenv –three stupide, python parfait pour l’enseignement… des années avant. Pour les consultations de boule de cristal, c’est uniquement le mardi à 15h.

N’est-ce pas trop tard ?

Au contraire, c’est exactement le bon moment. Le format le plus utilisé actuellement n’est ni le setup.cfg, ni le pyproject.toml, mais toujours le bon vieux setup.py. En fait, n’en déplaise aux defenseurs de pyproject.toml qui veulent nous faire croire que le projet est bien plus populaire et avancé qu’il ne l’est vraiment, il vaut l’équivalent d’un draft de proposal en beta testing. Une simple recherche github retourne:

De toute façon la transition sera longue, et les outils commencent à peine à supporter le nouveau format. En fait, ils ne sont même pas d’accord sur comment l’utiliser. On n’a pas de standard pour le lock file (pipfile semble se dégager, mais n’est pas enterriné) et les outils utilisent pyproject.toml en créant une section custo dedans au lieu des champs standardisés (poetry), voire pas du tout comme pipenv. De plus, setup.cfg est déjà utilisé dans la nature (voir les exemples plus haut).

Par ailleurs, extraire les données de setup.cfg plutôt que de pyproject.toml n’est pas très compliqué, les outils de packaging n’ont qu’une toute petite partie de leur code dédié à la gestion du fichier, le reste c’est la logique de management des dépendances, le téléchargement, la command line, le virtualenv, etc. Ils peuvent par ailleurs tout à fait supporter les deux pendant la transition.

Vu que c’est nous qui allons nous coltiner les conséquences du choix pour les 10 prochaines années à venir, autant élever la voie. D’autant que, surprise, ça ne coûte quasiment rien à la majorité des projets de migrer ou commencer avec setup.cfg. Tout marche déjà, et on peut produire de jolies wheels. On ne peut pas dire autant de pyproject qui demande d’adopter des outils tout neufs et qui doivent encore faire leur preuve. Aussi, bonne chance pour faire marcher votre toolchain de CI avec, j’ai essayé, et croyez moi c’est relou.

Maintenant, j’aime que le packaging avance. J’aime même les nouveaux outils comme poetry. Mais la suite n’est pas inéluctable, et on peut concilier modernité avec sanité.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.