Bonjour,
Je viens ce matin avec un problème somme toute assez classique de nos jours: "comment gérer ses dépendances ?" (dans mon cas en python). Je m’explique:
- D’une part (et surtout avec
npm
, me concernant1), j’ai fini par me mettre en tête qu’avoir des build reproductibles, c’était plutôt pas mal (pour la CI). En python, ça consiste donc à fixer ses versions avecmachin==1.0.3
, ce qui devient assez vite chiant à faire à la main. J’ai donc commencé à utiliserpipenv
et me rendant compte de certains de ces défauts (la communauté semble lui reprocher de ne pas être régulièrement être mis à jour, mais me concernant, c’est le fait d’avoir une version fixée de python), j’ai rétropédalé et j’utilise actuellementpip-tools
. - Oui, mais. Ce que je viens de dire plus haut est à peu près simple lorsqu’on développe une "application" python. Les choses se compliquent lorsqu’on développe un "package" python. Par "package", je n’entend pas forcément quelque chose qui a se retrouver sur PyPi, mais qui est quand même pensé pour être un composant d’autres applications ou packages. Et là, on a deux "problèmes": 1) Si on fixe les versions des dépendances, on va très vites avoir des ennuis avec les autres développeurs, donc on a plutôt intérêt à ne pas fixer les versions du tout ou à utiliser des bricolages genre
>=2.0,<3.0
en espérant que les gens en face son au courant de SEMVER. 2) J’ai l’impression qu’après plusieurs hésitations, la communauté du packaging sous python tend à utilisersetuptools
. Ce qui est bien, mais il faut donc s’adapter à ce format (et ils n’ont pas été toujours d’accord). - Bonus: et les dépendances spécifiques au développement ? Je pense en particulier à
flake8
et consorts, qui donc n’ont aucun intérêt à se retrouver dans les dépendances d’un package … Mais qui doivent être facilement installable pour l’éventuel développeur.
La meilleure solution que j’ai trouvée pour le moment c’est de:
- Avoir un fichier
requirements.in
(comme suggéré par la documentation depip-tools
), qui va être lu parsetup.py
pour servir de liste de packages en cas d’installation. C’est pas idéal, parce quesetuptools
etpip
n’ont pas toujours été d’accord sur la manière d’écrire les choses (en particulier quand on s’éloigne du package sur PyPi et qu’on va chercher des trucs sur Github), mais pour le moment ça fonctionne. - Avoir un fichier
requirements-dev.in
, contenant un-r requirements.in
et les dépendances additionnelles de développement (flake8
etSphinx
, en gros). C’est alors ce fichier qui sert de base àpip-compile
pour générer un fichierrequirements.txt
avec lui des dépendances fixées et donc des builds reproductibles.2 - Suggérer au développeur d’utiliser
pip-sync && pip install -e .
(ou lui forcer la main via unMakefile
ou un script).
Et ça fonctionne (modulo, comme je dis, que setuptools
et pip-tools
soient d’accord sur le format, ce qui finit toujours par avancer, mais y’a une période de latence).
Sauf que j’ai l’impression que je m’y prend pas encore tout à fait bien. Par exemple, je suis obligé de faire pip install -e .
pour avoir mon package installé dans mon environnement, mais je peux pas uniquement faire pip install -e .
(qui installe également les packages, donc), parce que sinon j’ai plus de build reproductible dans un contexte de CI. Et inversément, je peux pas demander à setuptools
de lire le requirements.txt
, puisque y’a toutes les versions fixées dedans, et les dépendances de développement. setuptools
propose ceci dit une option extra_require
, mais ça me demanderai de toute façon de faire un pip install nom-du-package[dev]
, donc .. Quel intérêt ?
Bref. Est ce que vous auriez des conseils à me donner ?
- Ne me lancez pas sur le sujet, je risque de dire des conneries par ignorance et c’est pas le but.↩
- Maintenant que je l’écrit, je me dit que ce fichier devrait être appellé
requirements.in
et l’autrerequirements-quelque-chose.in
vu quepip-compile
recherche automatiquement le premier, mais soit, c’est du détail↩