Un petit retour d’expérience sur pip

Il y a deux features qui sont très discutées autour de pip. La première c’est l’ajout d’une option upgrade all permettant de mettre à jour tous les packages pip installés (issue #59 datant du 15/03/2011). La seconde (issue #988 datant du 11/06/2013) découle de la première. Pour mettre à jour correctement tous les packages pip installés, il faudrait un gestionnaire de dépendances (ce que fait apt par exemple).

Je souhaitais vous illustrer les problématiques qu’on peut avoir autour de cela.

Pip et pip3

J’utilise Glances et Borg sur l’ensemble de mes serveurs Debian. Il se trouve que de nouvelles releases sont sorties récemment, j’ai donc voulu mettre à jour mes serveurs en conséquence. Je lance une commande Ansible pour mettre à jour Glances sur l’ensemble de mes serveurs, ça fonctionne pour quelques-uns, ça échoue pour une majorité. Je commence à creuser et je m’aperçois que sur certains serveurs j’ai installé Glances avec pip3 install glances et sur d’autres pip install glances. Comme on peut le voir sur la page Python Package Index (pypi), Glances peut être utilisé aussi bien avec Python 2 qu’avec Python 3. On notera également que dans l’ensemble de la page on ne parle que de pip install (et jamais pip3 install).

J’ai un choix à faire, je me dis que je vais dorénavant utiliser Glances avec Python 3. J’installe donc Glances pip3 install glances sur tous mes serveurs puis j’en lance un au hasard. Je me rends compte qu’il utilise Python 2… En effet je me retrouve avec des serveurs sur lesquels j’ai donc fait pip install glances ET pip3 install glances. Il suffit de faire un petit pip list puis pip3 list pour se rendre compte que j’ai le package Glances installés dans les deux.

Je décide donc de supprimer Glances de Python 2 avec pip uninstall glances. Je lance la commande glances… commande introuvable. Et oui ! Finalement un pip3 uninstall glances suivi d’un pip3 install glances fera comprendre au système qu’il faut utiliser Glances avec Python 3.

La morale de cette histoire c’est que je vous invite à vous méfier de ce que vous avez installé avec pip et avec pip3. Il suffit d’une erreur d’inattention une fois et vous pouvez vous retrouver avec le même package en Python 2 et Python 3 installé. Les commandes à avoir en tête pip list et pip3 list. Je vous rappelle également mon Mémo pip (que je maintiens à jour).

L’erreur récurrente que je fais au boulot est de balancer un petit pip install --upgrade glances qui installera la dernière version de Glances en Python 2 alors qu’il n’était pas installé en Python 2. En effet pip considère pip install --upgrade glances de la même manière que pip install glances n’affichant aucun message et installant le package. Si l’option upgrade all existait, je ferais un pip upgrade all et un pip3 upgrade all ainsi je ne tomberais pas dans l’erreur pip install --upgrade glances.

Un autre exemple mais à la maison, je me suis souvent arraché les cheveux à cause de youtube-dl et mps-youtube. Le package youtube-dl est disponible sur Python 2 et Python 3. Le package mps-youtube n’est disponible que sur Python 3 et nécessite le package youtube-dl pour fonctionner. Si vous suivez la documentation de youtube-dl vous allez faire pip install youtube-dl puis vous ferez après pip3 install mps-youtube. Là vous allez avoir un problème car mps-youtube ne va pas fonctionner puisqu’il cherchera youtube-dl dans Python 3. Au final il faut faire pip3 install youtube-dl mps-youtube. Si le gestionnaire de dépendances existait, pip3 install mps-youtube installerait automatiquement youtube-dl dans Python 3 et on n’aurait pas toute cette complexité au niveau de la compréhension et de l’utilisateur final.

Pourquoi mettre à jour tous les packages pip installés est une mauvaise idée

Je passe à présent à la mise à jour de borgbackup. J’ai un problème de dépendance ! Un peu échaudé par tout ça je commence à en avoir marre et je me dis que c’est une bonne excuse pour tester la commande pip list --outdated | cut -d' ' -f1 | xargs -n1 pip install --upgrade citée dans mon Mémo pip à titre d’information mais que je déconseille d’utiliser qui met à jour tous les packages pip installés. La commande fonctionne très bien, elle met à jour tous les packages sauf un. Je lance alors la même commande pour pip3 (borgbackup n’est disponible que sur Python 3) et la mise à jour de borgbackup réussit.

Le lendemain je lance Ansible qui refuse de fonctionner… Ansible nécessite une version de Jinja2 inférieure à la 2.9. Ma super commande d’hier a installé la dernière version de Jinja2, la 2.9.5. Je désinstalle Jinja2 pip uninstall jinja2 puis j’installe une version au hasard inférieure à la 2.9 pip install jinja2==2.8. Ansible fonctionne.

Vous avez un exemple de pourquoi il ne faut pas mettre à jour tous les packages pip installés parce qu’il n’y a pas de gestionnaire de dépendances semblable à apt.

Déjà 11 avis pertinents dans Un petit retour d’expérience sur pip

Afin de lever toute ambiguïté, tu peux aussi utiliser des environnements virtuels: `virtualenv` (python2) ou `pyvenv` (python3). C’est bien plus propre. Voire `pipenv` (pas testé), voire même `conda` (qui serait sans doute un peu excessif).
Perso, j’utilise exclusivement pip avec Python 3, le paquet python-pip n’est même pas installé comme ça pas de confusion. Un coup de `pip3 install –user -U pip` (avec `~/.local/bin` dans PATH) pour pouvoir utiliser `pip`. Si une application n’est disponible que pour Python 2, je considère qu’elle est obsolète et je ne l’utilise pas.

Ensuite, j’ai un fichier `~/.config/pip-packages.txt` dans lequel j’ajoute manuellement tous les paquets que j’installe avec pip. Puis pour mettre à jour je fais `pip install –user -U -r ~/.config/pip-packages.txt`. Jusque-là je n’ai jamais de conflit de version en fonctionnant ainsi.

    Salute,

    Merci pour cette idée, je la trouve très bonne. Je précise que l’End Of Life de Python 2 est tout de même 2020 et que Ansible est toujours sur Python 2 (en cours vers Python 3).

    Tcho !

C’est un soucis que je discutais encore ce matin, le développement ‘moderne’ n’est pas assez rétro-compatible. Les nouvelles versions cassent les anciennes. Du coup, on se retrouve avec 50 versions de la même chose.
Si ce que je te propose te semble overkill, laisse moi t’éclairer un peu : `pip` ne doit être utilisé `system-side` pour au moins 2 raisons :

risque de conflit avec des paquets de ta distribution: si tu installes par exemple `requests` avec pip, et qu’ensuite tu décides d’installer `python2-requests` par exemple, ou équivalent, tu auras des conflits de fichiers. C’est normal. Si c’est à la main que tu as installé `requests`, tu as une chance d’identifier le problème. Si c’est une bibliothèque moins connue qui a été initialement installée automatiquement par un `pip install -r …` ou que ta distribution décide sans te le demander l’installation du paquet par dessus en tant que dépendance d’autre chose, tu seras bien ennuyé.
impossibilité de gérer des codes python dont les dépendances sont incompatibles : imagine que `glances` et `borgbackup` fassent tout deux appels à une lib commune, mais que pour une raison indépendante de ta volonté, ils ne fonctionnent pour l’un avec la version `0.0.1223-trucmuch` et l’autre avec la `0.0.1224-zigoto`. Ce n’est visiblement pas le cas avec ces deux là, mais que se passe-t-il si tu découvres demain un autre logiciel que tu dois impérativement installer ? Te faudra-t-il débobiner toute ton installation de `glances` et de `borgbackup` faute d’avoir anticipé ?

Cf. ici et ici. Les environnements virtuels ont été pensés pour résoudre ces deux problèmes élégamment, c’est à dire de façon modulaire, et sont à ma connaissance la seule possibilité simple.

Concernant l’obsolescence supposée des applications Python 2, c’est une supposition hasardeuse qui escamote le fait que nous n’avons pas toujours le choix d’installer ce que nous souhaitons : il peut s’agir d’une dépendance nécessaire ou, dans le cadre professionnel, d’une figure imposée.

    Salute,

    Mouais j’ai davantage l’impression que c’est une nécessité en tant que développeur. En tant qu’utilisateur, j’ai jamais vu quelqu’un faire ça (ceci étant directement lié aux packages Python qu’on utilise évidemment). Enfin bon je sais bien que c’est LA solution définitive et propre mais perso tant que je peux m’en passer, je m’en passe.

    Merci camarade, Tcho !

Pour ma part, j’abuse des fichiers de contraintes pour fixer les versions de certains packages, mais je bosse de toute façon dans les venv.
Pour les installations « System-wide », apt est mon ami.
Les environements virtuel me semble également être la solution la plus simple:

* En tant qu’utilisateur, j’installe chaque outil python (les outils « cli ») dans son propre environement virtuel à l’aide de « pipsi » (https://github.com/mitsuhiko/pipsi). Les commandes sont relativement intuitives (`pipsi install youtube-dl`, pipsi upgrade youtube-dl`, `pipsi uninstall youtube-dl`). Ainsi, le répertoire des packages python système n’est pas ‘pollué’ et on evite les futures problèmes.

* En tant que développeur, chaque projets possède sont propre environement crée avec virtualenvwrapper (https://pypi.python.org/pypi/virtualenvwrapper) qui facilite grandement la gestion des environements.

source: https://hynek.me/articles/virtualenv-lives/

Laisser un commentaire

indique des champs obligatoire.