Comment gérer ses dépendances convenablement, au final ?

a marqué ce sujet comme résolu.

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:

  1. 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 avec machin==1.0.3, ce qui devient assez vite chiant à faire à la main. J’ai donc commencé à utiliser pipenv 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 actuellement pip-tools.
  2. 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 à utiliser setuptools. Ce qui est bien, mais il faut donc s’adapter à ce format (et ils n’ont pas été toujours d’accord).
  3. 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:

  1. Avoir un fichier requirements.in (comme suggéré par la documentation de pip-tools), qui va être lu par setup.py pour servir de liste de packages en cas d’installation. C’est pas idéal, parce que setuptools et pip 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.
  2. Avoir un fichier requirements-dev.in, contenant un -r requirements.inet les dépendances additionnelles de développement (flake8 et Sphinx, en gros). C’est alors ce fichier qui sert de base à pip-compile pour générer un fichier requirements.txt avec lui des dépendances fixées et donc des builds reproductibles.2
  3. Suggérer au développeur d’utiliser pip-sync && pip install -e . (ou lui forcer la main via un Makefile 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 ? :)


  1. Ne me lancez pas sur le sujet, je risque de dire des conneries par ignorance et c’est pas le but.
  2. Maintenant que je l’écrit, je me dit que ce fichier devrait être appellé requirements.in et l’autre requirements-quelque-chose.in vu que pip-compile recherche automatiquement le premier, mais soit, c’est du détail
+0 -0

Salut,

Tu te compliques beaucoup la vie je pense. Perso j’utilise uniquement pip, tous ces autres outils compliquent les choses beaucoup trop. Les dépendances pour le fonctionnement de ton application sont dans setup.py, les dépendances pour le développement dans un/des fichiers requirements.

Tu peux avoir intérêt à utiliser pip freeze pour générer un fichier requirements pour build reproductible (qui va donner les versions des dépendances indirectes aussi).

Voir ici.

+2 -0

+1 au message d’adri1. En ajoutant que ce n’est pas forcément dans le setup.py mais que ça peut être un setup.cfg (nécessitant un setup.py minimaliste) ce qui simplifie bien les choses.

  1. 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.
pierre_24

Alors il y a en effet l’option extra_requires qui permet de régler ce soucis. Dans le package que tu distribues, tu n’inclues que le strict nécessaire au runtime, et tu proposes d’autres groupes de dépendances pour le développement (pytest, flake8, mypy, etc.). À l’utilisation ça donnerait pip install -e.[dev] par exemple pour installer toutes les dépendances nécessaires pour le projet + dev (dans le répertoire du projet cloné).

Voici un exemple de fichier setup.cfg qui exploite cela : https://pastebin.com/DFfcKcUw (et le setup.py correspondant).

J’entends bien l’argument de la complexité, d’ailleurs c’est une des raisons pour laquelle j’ai posté mon message ^^

Ceci dit, en me relisant, je pense que ma question se résume a: est ce qu’un dévellopeur lambda a tout intérêt à installer des versions fixes (dans ce cas, mon bricolage ou pip-freeze se justifie) ou à laisser la version qui tombe à ce moment là, quitte à avoir des surprises ? J’ai l’impression que vous êtes partisans de la seconde … Et j’étais d’accord avec vous jusqu’à croiser la route de npm (pour qui, pour le dire gentiment, certains développeurs de packages n’ont rien compris à SEMVER).

Plus fondamentalement, est ce que l’idée d’avoir à tout pris des builds reproductibles ce justifie vraiment dans le cas d’un package ou est ce que je me prend la tête pour rien ? (pour une application, on a techniquement pas besoin de setup.py)


Tu peux avoir intérêt à utiliser pip freeze pour générer un fichier requirements pour build reproductible (qui va donner les versions des dépendances indirectes aussi).

adri1

J’ai quand même l’impression que pip-compile ne fait pas grand chose de plus intelligent, en fait ^^

Normalement dans Python il n’y a pas trop ce genre de soucis, les paquets mettent bien à jour la version majeur en cas de changement incompatible, et restent rétro-compatible sinon.

Voilà une suite d’articles en cours de rédaction qui pourrait t’être utile par rapport à tes questions sur le packaging / distribution : https://www.stella.coop/blog/00003-l-enfer-des-paquets-python-le-sac-de-noeuds.html

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte