Sommaire
- Installer des modules Python
- Mettre à jour les modules Python
- Rendre un script exécutable
- Compatibilité Python-2 et Python-3
- Arborescence d'un projet Python
- Et tes astuces ?
J'ai découvert/appris Python en le pratiquant au bureau à l'arche, et sans collègue à la fois expert et pédagogue. Du coup, j'ai accumulé plein de mauvaises pratiques que je tente désormais de corriger. Ce journal pour vous partager mes astuces et vous éviter les mêmes pièges :-)
Je ne suis pas encore un expert Python, alors merci de me corriger gentiment dans les commentaires ;-)
Je publie ce journal sous licence CC0 (sous domaine publique dans les pays où cela et possible) car je veux te permettre de recopier/modifier/réutiliser/republier ce contenu sans avoir à me citer comme auteur (sauf que la loi de pays comme la France t'oblige quand même à me citer).
Installer des modules Python
Le gestionnaire de paquets Python de référence s'appelle pip
et au tout début, j'installais les dépendances Python manquantes avec :
sudo pip install "nom-du-module-python" # en root
car cette commande ne semblait pas toujours fonctionner :
pip install "nom-du-module-python"
En fait la première commande ci-dessus rentre en conflit avec :
sudo apt install "autre-nom-du-meme-module-python"
Ça fout le bazar dans l'installation des paquets de sa distrib… :-(
En lisant pip ticket #5599 on comprend le besoin de préciser l'argument --user
:
pip install --user "nom-du-module-python"
Mais il y a mieux:
python -m pip install --user "nom-du-module-python"
Et encore mieux si nos scripts ont migré sous Python-3:
python3 -m pip install --user "nom-du-module-python"
Donc, remettons bien en gras les bonne façons d'installer un module Python:
python2 -m pip install --user "nom-du-module-python"
python3 -m pip install --user "nom-du-module-python"
Mais quelle différence entre pip3
et python3 -m pip
? Les détails sur stackoverflow (en anglais). En bref, on est sûr que python3 -m pip
utilise la même arborescence d'installation que le script que tu exécutes avec python3 "nom-du-script.py"
. C'est d'autant plus recommandé si ton $PATH
est tarabiscoté, si tu as plusieurs installations de Python ou que tu utilises MS-Windows.
Mettre à jour les modules Python
Est-ce que pip
permet de mettre à jour tous les modules d'un coup comme un apt upgrade
? Et bien, pas vraiment. Il faut le faire pour chaque module :
python3 -m pip install --user --upgrade "nom-du-module-python"
Attention, j'ai déjà réussi à casser pip
avec cette commande :
pip install --upgrade pip
Pour tout mettre à jour, j'enregistre préalablement les versions des modules actuellement présents :
python3 -m pip list --user --format freeze > ~/requirements_avant_pip_upgrade.txt
Et une longue ligne de commande pour tout mettre à jour :
python3 -m pip list --user --format columns | awk 'FNR>2{print $1}' | while read module ; do python3 -m pip install --user --upgrade $module ; done
Visualiser les changements :
python3 -m pip list --user --format freeze > ~/requirements_apres_pip_upgrade.txt
diff -u ~/requirements_avant_pip_upgrade.txt ~/requirements_apres_pip_upgrade.txt | vi -
# quitter en tapant :q! [Entrée]
Ou avec une application graphique :
meld ~/requirements_avant_pip_upgrade.txt ~/requirements_apres_pip_upgrade.txt
En cas de problème, revenir aux versions précédentes :
python3 -m pip install --user -r ~/requirements_avant_pip_upgrade.txt
En fait, ce n'est pas recommandé de mettre à jour tous ses modules Python car cela risquerait de casser une autre application Python qui utilise la même dépendance. Cela m'est arrivé récemment avec le module redis
dont la version 3 casse la comptabilité avec la version 2 ! (mais pourquoi ne pas avoir créé de nouvelles fonctions pour la nouvelle fonctionnalité ?)
Rendre un script exécutable
Pour lancer leur script Python, de nombreuses personnes font :
python mon-script.py
Avec deux petits changements, il est possible de le lancer juste comme ça :
./mon-script.py
Premier changement, dire que le script est exécutable :
chmod +x mon-script.py
Second changement, on rajoute sur LA PREMIÈRE ligne du fichier mon-script.py
:
#!/1usr/bin/python
Ou mieux :
#!/usr/bin/python3
Ou encore bien mieux :
#!/usr/bin/env python3
Le #!
s'appelle shebang et même des langages dont le #
n'est pas utilisé pour les commentaires le prennent en charge, comme pour PHP.
Compatibilité Python-2 et Python-3
Les distributions GNU/Linux sont enfin en train de migrer tous leurs scripts vers Python-3 (Pyton-3.0 a 10 ans). Mais difficile d'identifier si un script est compatible seulement v2 ou seulement v3 ou s'il est compatible avec les deux.
Pour aider les intégrateurs, merci de remplacer python
par python2
ou python3
dans vos projets open-source. Et si compatible avec les deux versions de Python ? Si ton application compatible v2 et v3 pourrait être utilisée sur une ancienne distribution GNU/Linux qui n'a pas Python3, alors continue d'utiliser python
. Si ce n'est pas le cas, utilise alors python3
.
Pour s'assurer que tes scripts Python2 soient compatibles Python3 rajoute au début de tous tes fichiers *.py
:
from __future__ import absolute_import, division, unicode_literals, print_function
Attention, je me suis fait avoir avec un bug uniquement reproductible avec la ligne précédente. Quand je lançais python2
en mode interactif pour vérifier mon bout de code, je n'avais aucun soucis. Donc pense à taper from __future__ import absolute_import, division, unicode_literals, print_function
quand tu veux reproduire un bug dans ton script python2. Il y a moyen de pré-exécuter cette ligne automatiquement. Avec ipython
:
$ cat ~/.ipython/profile_default/startup/ipython_config.py
from __future__ import absolute_import, division, unicode_literals, print_function
Arborescence d'un projet Python
Kenneth Reitz recommandait https://github.com/kennethreitz/samplemod
docs\
sample\
tests\
LICENSE
MANIFEST.in
Makefile
README.rst
requirements.txt
setup.py
Plus récemment, l'équipe Python Packaging Autority recommande https://github.com/pypa/sampleproject
data\
sample\
tests\
LICENSE.txt
MANIFEST.in
README.md # PyPi accepte Markdown depuis 2018
setup.cfg # Alternative à setup.py
setup.py
tox.ini # Plus besoin de requirements-test.txt
Si tu préfères Markdown à reStructuredText, sache que PyPi gère même les tableau Markdown.
Mais une révolution est en cours avec les PEP-517 et PEP-518, avec l'adoption du fichier pyproject.toml
en alternative du vénérable setup.py
et son setup.cfg
(même pip
adopte pyproject.toml
). Mais la vrai révolution est le découplage entre empaquetage et installation. Pour les anglophones je recommande la lecture de Python packaging - Past, Present, Future par Bernát Gábor.
Donc bientôt nous pourrons avoir une arborescence plus simple :
docs\
sample\
tests\
LICENSE.txt
README.md
pyproject.toml
tox.ini
Par exemple, les dépôts de code source de poetry
et de la PEP-517.
Et tes astuces ?
Merci de partager tes recommandations, tes mésaventures, tes bonne pratiques… :-D
# Installation de paquets de développement
Posté par flan (site web personnel) . Évalué à 5.
On peut avoir au moins deux raisons bien différentes d'installer des paquets Python : pour les utiliser comme des outils classiques, ou parce qu'ils sont nécessaires dans un projet en développement.
Dans le premier cas, j'installe avec --user. Dans le second, il me semble assez impératif de créer un virtualenv (ou équivalent, en gros c'est un chroot pour Python) qui permet d'isoler ce projet du reste du système et d'y mettre les bonnes versions.
Sinon, deux sites à connaître, le premier est Sam & Max (attention, lien NSFW mais avec de bons conseils et des retours d'expérience intéressants), le second est The Hitchhiker's Guide to Python.
[^] # Re: Installation de paquets de développement
Posté par Anonyme . Évalué à 3.
Personnellement, j'utilise pipenv pour la gestion des virtualenv par projet. L'outil est bien fait, ne nécessite pas de configuration particulière de son shell et permet de gérer finement les dépendances en prod ou en dev.
[^] # Re: Installation de paquets de développement
Posté par Benoît Sibaud (site web personnel) . Évalué à 6.
pip et virtualenv, ça devrait toujours aller ensemble. Histoire d'éviter les soucis de mises à jour pyOpenssl en conflit avec la version fournie par le gestionnaire de paquets de la distrib' qui veut passer une mise à jour de sécurité et qui tombe sur la version fournie par pip qui a écrasé en douce la version dudit gestionnaire de paquets, par exemple… Pareil dans ansible, le module pip devrait toujours utiliser le paramètre virtualenv (et ça vaut le coup d'avoir une règle ansible-lint pour l'imposer).
# vimdiff
Posté par George_abitbol . Évalué à 4.
Ça vaut ce que ça vaut, mais…
Plutôt que de faire un pipe dans le stdin de vi, il me semble plus efficace de demander à vimdiff de faire directement le diff lui même :
Puis tant qu'à faire, quitte à utiliser vimdiff, autant ne économiser une commande et économiser la création du deuxième fichier :vimdiff ~/requirements_avant_pip_upgrade.txt ~/requirements_apres_pip_upgrade.txt
vimdiff ~/requirements_avant_pip_upgrade.txt <(python3 -m pip list --user --format freeze)
# pip
Posté par saimn . Évalué à 5.
pip s'est enrichit au fil du temps de quelques options supplémentaires bien pratiques, notamment pour voir les paquets qui peuvent être mis à jour:
A combiner si besoin avec
--user
ou--local
(pour les virtualenv ayant accès au site-packages global). Et aussi--editable
pour les paquets installés en mode develop (avecpip install -e
).Il manque essentiellement une commande pour tout mettre à jour, du coup j'utitlise ça:
mais comme il n'y a pas de vérification des versions requises par les dépendances, il faut parfois ajuster manuellement et forcer les versions de certains paquets.
# Xkcd
Posté par octane . Évalué à 10.
Le xkcd de référence :
Et mes machines ressemblent un peu à ça. Pourquoi personne ne centralise les gestionnaires de paquets ?
Systemd --pack upgrade
doit devenir la solution.
(Ami lecteur un troll s'est glissé dans ce message, saura tu le reconnaître ?)
[^] # Re: Xkcd
Posté par Guillawme (site web personnel, Mastodon) . Évalué à 3.
GNU essaye précisément de faire ça avec leur gestionnaire de paquet Guix. Voilà la section de la documentation qui explique ça : https://www.gnu.org/software/guix/manual/en/html_node/Invoking-guix-import.html
Ils ont déjà un mécanisme d’import pour les paquets Python, Ruby, R, Perl, et quelques autres.
[^] # Re: Xkcd
Posté par Guillaum (site web personnel) . Évalué à 3.
Et nix fait déjà ça et gère les packets python.
# Attention à pip hors des environnements virtuels
Posté par lolop (site web personnel) . Évalué à 4.
Je passe sur le pip qui va bidouiller dans les répertoires du système, à prohiber.
Mais le pip qui installe des librairies
--user
est aussi risqué. Ces librairies peuvent masquer celles installées au niveau du système… et faire que certains logiciels écrits en Python ne fonctionnent plus. C'est au moins à savoir, pour pouvoir diagnostiquer, et ça se répare facilement (on supprime de l'installation de la librairie pour l'utilisateur).Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: Attention à pip hors des environnements virtuels
Posté par Anonyme . Évalué à 5.
Genre
pip3 install --upgrade pip
qui cassepip3
si~/.local/bin
n'est pas dans lePATH
.# Quelques bonnes pratiques Python pour 2019
Posté par Anonyme . Évalué à 10.
J'en ai au moins une moi : arretez d'utiliser Python 2 (bordel !).
# Autres astuces non mentionnées dans mon journal
Posté par Oliver (site web personnel) . Évalué à 5.
Quelques autres astuces intéressantes dans l'écosystème Python que j'ai oublié de mentionner dans mon journal.
Règles du code source
Bien que de nombreux développeurs sont d'accords pour respecter la PEP-8, on a tous nos préférences pour telle ou telle nuance. Par exemple, personnellement j'apprécie lire des valeurs alignées comme ceci :
Mais ce n'est pas l'avis de tous et chacun a de bons arguments. Par conséquent, du temps précieux est consacré à convaincre/négocier sur tel ou tel aspect. Et puis, avec la rotation naturelle de l'équipe (turn-over), les décisions du passé ne conviennent pas toujours à la nouvelle équipe ! La preuve que l'on y passe du temps, mon bout de code ci-dessus va être repris dans un commentaire plus bas.
Pour mettre tout le monde d'accord, économiser son temps et éviter les polémiques/frustrations, utilisons
black
. C'est un script python tout-en-un de 4000 lignes qui décide/réécrit tout seul vos fichiers Python. L'absence de fichier de configuration évite tout marchandage. Son objectif est de réduire lediff
entre deux commits (minimiser le nombre de lignes modifiés).Mon code ci-dessus devrait être converti en : (pas testé)
Tests unitaires
Historiquement, nous avons
unittest
. D'autres alternatives intéressantes :nose
etpytest
. Ce dernier permet d'écrire très simplement ses tests unitaires sans avoir à implémenter une classe :Analyse statique de code
Quelques outils intéressants :
Annotation de type
Comme Spack nous le signalait en 2014 et récemment amélioré par Python 3.7, nous pouvons aider l'analyse statique du code source Python en spécifiant les types attendus :
Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)
[^] # Re: Autres astuces non mentionnées dans mon journal
Posté par flan (site web personnel) . Évalué à 3.
Je suis tout à fait d'accord pour black.
L'aspect qui en résulte ne me plaît pas forcément, mais c'est une affaire de goûts. Comme il y a de vraies raisons objectives pour l'utiliser, je m'y suis mis également.
Je m'impose (mais sans outil) de trier dès que possible par ordre alphabétique (par exemple l'ordre d'écriture dans un set, l'ordre des arguments par mot-clef, …). Ça permet de limiter également des diff d'une part, et de simplifier la recherche visuelle.
[^] # Re: Autres astuces non mentionnées dans mon journal
Posté par liberforce (site web personnel) . Évalué à 2.
Au boulot on utilise yapf, mais je n'étais pas emballé par la config utilisée. J'ai essayé black mais je ne suis vraiment pas convaincu par le formatage. Il ne va pas changer que l'indentation et les retours chariots, mais aussi le type de guillemets utilisé (guillemets doubles partout). C'est bien pour l'homogénéité, mais cela altère plus que juste le formatage. Perso je préfère des lignes courtes avec un paramètre par ligne car cela permet d'avoir des diff minimalistes et faciles à lire, et d'éviter au maximum les conflits. Black va lui au contraire essayer de tout faire tenir sur une ligne.
De son côté, yapf permet de donner des indications sur le formatage, mais c'est toi qui les contrôles.
Par exemple, prendre l'habitude de mettre une virgule après le dernier paramètre d'une liste (de valeurs ou d'arguments) permet aussi de réduire les diff. Ainsi quand on ajoute un nouveau paramètre, le pas besoin de modifier la ligne précédente pour rajouter une virgule. On a un diff propre avec juste une ligne ajoutée correspondant à la valeur ajoutée. Et yapf a la bonne idée de comprendre qu'une virgule sans autre valeur derrière indique que tu préfères un formatage avec un paramètre par ligne.
On est finalement restés sur yapf avec quelques améliorations sur la config.
# Python Build Reasonableness
Posté par Oliver (site web personnel) . Évalué à 4. Dernière modification le 03 avril 2019 à 05:32.
Pour éviter de passer du temps à réaliser des tâches répétitives à chaque version, l'équipe de OpenStack les a automatisées dans un script qui est devenu en 2010, le module
pbr
pour Python Build Reasonableness (avec sagesse). Ce module a énormément évolué en s'adaptant aux évolutions de Python sur dix ans (150 contributeurs).Installation
Fonctionnalités
setuptools
(setup.py
) ;requirements.txt
verssetuptools
(install_requires
) ;AUTHORS
à partir dugit log
;ChangeLog
en cherchant les motsfeature
,api-break
,deprecation
etbugfix
dans legit log
;reno
la gestion desRelease Notes
;MANIFEST.in
;Lance les tests via(déprécié) ;tox
sphinx
pour produire la documentation.Avant
Après
Bon, c'est vrai, depuis
setuptools-30.3.0
(déc. 2016) on peut remplacersetup.py
parsetup.cfg
. Cependant, le Python Packaging Authority recommande d'utiliser principalement lesetup.py
. Néanmoins, des projets commetox
utilisent principalementsetup.cfg
.Distinction entre
install_requires
etrequirements.txt
Le module
pbr
permet d'éviter de gérer en double la liste des dépendances :pbr
spécifie le paramètreinstall_requires
à partir du fichierrequirements.txt
. Mais, sémantiquement, ces deux listes ont deux objectifs différents :install_requires
du fichiersetup.py
(ousetup.cfg
) est inséré dans le livrable et ne précise pas toujours la version des dépendances, du moins l'intervalle des versions pour lequel le livrable est censé être compatible ;requirements.txt
est fourni àpip
pour installer des versions précises pour lesquelles l'application a été validée.De plus,
requirements.txt
peut contenir des arguments de la ligne de commandepip
comme--index-url https://pypi.python.org/simple/
afin d'indiquer explicitement à partir de quel dépôt télécharger les dépendances. Les développeurs utilisent souvent ce fichier pour avoir les mêmes dépendances et éviter de perdre du temps avec une incompatibilité absconse.En résumé,
install_requires
définit les dépendances compatibles (pour la livraison) etrequirements.txt
spécifie les versions validées (pour le déploiement). Pour plus de détail sur cette sémantique, lire le coup de gueule de Donald Stufft (2013, en anglais). Lire aussi la documentation officielle (en anglais).Cependant, dans la pratique, ces deux listes sont souvent gérées de la même façon, donc autant mutualiser les efforts. :-)
Futur
Actuellement,
pbr
n'est pas encore compatible avec le fichierpyproject.toml
(PEP-517 et PEP-518) et ne prend pas en charge les alternatives àsetuptools
commeflit
etpoetry
(découplage entre empaquetage et installation). Mais les développements sont prévus d'après la description du module :Commentaire sous licence Creative Commons Zero CC0 1.0 Universal (Public Domain Dedication)
[^] # Commentaire supprimé
Posté par Anonyme . Évalué à 2.
Ce commentaire a été supprimé par l’équipe de modération.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.