Développer un projet Python en 2021 : les outils

9 minute read

Alors que Python poursuit en 2020 sa croissance exceptionnelle, la multiplication des outils à disposition du développeur Python peut être déconcertante. Essayons d’y voir plus clair.
Ce premier post détaille les outils de gestion de projets Python qui me semblent les plus intéressants. Dans un deuxième volet, je parlerai de la programmation Python en elle-même.

Note : Tout ce qui est écrit ici a été testé sous Ubuntu/Debian mais reste valable sous Windows, avec un peu plus d’efforts. Les versions récentes de Windows permettent en outre d’utiliser Linux via WSL.

Gérer les versions Python : pyenv

https://xkcd.com/

Commençons par le commencement : installer Python. Bien que Python soit installé nativement sur toute distribution Linux qui se respecte, changer de version à partir des sources peut être fastidieux. pyenv est un utilitaire écrit en shell qui simplifie l’installation et l’utilisation de différentes versions de Python.
Pour installer pyenv, le plus simple est de passer par ce script :

curl https://pyenv.run | shell

Ceci télécharge puis exécute https://pyenv.run, qui clone le dépôt github.com:pyenv/pyenv dans ~/.pyenv et ajoute pyenv dans ~/.shellrc.
pyenv utilise un hook pour intercepter les commandes Python et choisir la bonne version à utiliser.
Pour voir les versions de Python détectées :

pyenv global
system # exécutable Python apporté par le système d'exploitation

Note : Sous Ubuntu, il n’y a pas de commande python par défaut, ce qui empêche pyenv de le détecter. Une solution est de créer un lien symbolique vers python3: ln -s /usr/bin/python3 /usr/bin/python.

Installons les éventuelles dépendances manquantes de Python :

sudo apt-get update; sudo apt-get install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

Ajoutons la version 3.9.1 de Python :

pyenv install 3.9.1

python3.9.1 est alors installé dans ~/.pyenv/versions/3.9.1.
Il est possible de spécifier une version de Python au niveau global ou local :

pyenv global 3.9.1 # utiliser python3.9.1 globalement
pyenv local 3.9.1 # utiliser python3.9.1 localement (répertoire en cours)

python, python3 ou python3.9 permet alors d’utiliser notre version 3.9.1 fraîchement installée.

EDI : PyCharm vs Visual Code

https://www.jetbrains.com/lp/python-developers-survey-2019

PyCharm et Visual Code sont, de loin, les environnements de développement privilégiés par les développeurs Python.1 Alors que PyCharm est spécifique au langage Python, Visual Code est un EDI “tout en un” qui est très utilisé aussi en développement web, par exemple. À noter que PyCharm est gratuit dans sa version Community mais payante et propriétaire en version Professional. Au contraire, Visual Code est entièrement gratuit et open source. J’ai personnellement adopté Visual Code pour sa légereté et ses nombreux plugins.

Voici quelques commandes Visual Code que j’ai trouvé particulièrement utiles:

  • Ctrl + Maj + P / F1 : ouvrir la liste des commandes
    Certaines commandes n’ont pas de raccourci mais sont très pratiques : “Transform to Lower Case” ou “Sort Imports”, par exemple
  • Ctrl + P : naviguer parmi les fichiers/fonctions
  • Ctrl + F5 : exécuter un fichier
  • F5 : débugger un fichier
  • Ctrl + C / Ctrl + V / Ctrl + X (sans sélection) : copier/coller/couper la ligne en cours
  • Alt + Up/Down : déplacer une ligne vers le haut/bas
  • Alt + Clic : selection multi-curseurs
  • Ctrl + Maj + L : selectionner tous les mots identiques
  • Ctrl + Space : complétion automatique

Dépendances : Poetry vs Pipenv vs conda

https://xkcd.com

Il est fortement déconseillé d’installer tous ses packages Python globalement : il deviendrait difficile de partager uniquement les dépendances nécessaires pour un projet et certains projets peuvent nécessiter des versions différentes du même package…
Un environnement virtuel permet de regrouper les packages requis pour un projet particulier. Depuis Python 3.3, venv permet de créer nativement des environnements virtuels. De nombreux développeurs utilisent venv pour installer leurs packages et pip freeze pour partager la liste requirements.txt des versions à installer. Cette approche se heurte à de nombreux problèmes : la mise à jour des packages est délicate et pip est notoirement connu pour sa déficience dans la résolution des dépendances2.

Poetry, Pipenv et conda sont des surcouches plus évoluées qui offrent plus de possibilités.
Conda est un peu à part : alors que les autres outils sont basés sur PyPI/pip (et leurs packages wheel/source), conda a son propre dépôt (Anaconda Cloud) et système de packages. De ce fait, conda est plutôt utilisé comme outil tout-en-un (il permet aussi de mettre à jour Python, par exemple) et s’interface assez mal.

Dans la suite, je détaille l’utilisation de Poetry : il est similaire à Pipenv mais, contrairement aux deux autres, il se base sur les recommandations officielles PEP 517 et PEP 518.

Installer Poetry :

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -

Créer un nouveau projet :

poetry new projet

Ceci génère la structure suivante :

projet/
├── projet
│   └── __init__.py
├── pyproject.toml
├── README.rst 
└── tests
    ├── __init__.py
    └── test_projet.py

Il est aussi possible utiliser un projet déjà existant :

poetry init

Le fichier .toml généré ressemble à :

[tool.poetry]
name = "projet"
version = "0.1.0"
description = ""
authors = ["Quentin Fortier <qpfortier@gmail.com>"]

[tool.poetry.dependencies]
python = "^3.9"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" 

Ce fichier remplace requirements.txt en définissant les dépendances utilisées pour le projet. Ainsi, python = “^3.9” signifie que la version de Python doit être supérieure à 3.9, mais ne doit pas être mise à jour à la prochaine major release (4.*).

Ajoutons la dernière version de NumPy à notre projet :

poetry add numpy

Poetry ajoute alors numpy à pyproject.toml. Pour installer les packages ajoutés :

poetry install # ou poetry update si un install a déjà été fait

Poetry créé alors un environnement virtuel dans ~/.cache/pypoetry/virtualenvs qui ressemble à ceci :

projet-v_ZTu9ix-py3.9/
├── bin
│   ├── activate
│   ├── pip
│   ├── pytest
│   ├── python -> /home/qfortier/.pyenv/versions/3.9.1/bin/python # lien symbolique
├── lib # contient les packages installés avec poetry/pip
└── pyvenv.cfg

On peut ensuite utiliser notre environnement virtuel en exécutant poetry run ou poetry shell.
Constatons par exemple que Poetry ajoute automatiquement notre projet au PYTHONPATH (plus besoin de manipuler sys.path à la main ni d’imports relatifs!) :

poetry shell
(projet-v_ZTu9ix-py3.9) python -c "import sys; print(sys.path)"
['', '/home/qfortier/.pyenv/versions/3.9.1/lib/python3.9', '/home/qfortier/.cache/pypoetry/virtualenvs/projet-v_ZTu9ix-py3.9/lib/python3.9/site-packages', 
'/home/qfortier/code/projet' # chemin vers notre projet
]

Ainsi, un module projet/projet/a/b.py sera importé de la façon suivante dans un fichier Python :

import projet.a.b

Pour utiliser l’environnement virtuel de Poetry dans Visual Code, utiliser la commande “Python : Select Interpreter” puis donner le chemin correspondant (~/.cache/pypoetry/virtualenvs/<projet>/bin/python). Visual Code affiche l’environnement en cours d’utilisation :

Environnement virtuel

Note : Par défaut, Visual Code cherche un environnement virtuel dans le répertoire du projet. Il est possible de chercher parmi les environnements Poetry en ajoutant la ligne suivante au fichier settings.json: "python.venvPath": "~/.cache/pypoetry/virtualenvs/"

Environnement virtuel et Jupyter

Pour utiliser un environnement virtuel dans Jupyter, il faut l’ajouter en tant que kernel :

  1. Activer l’environnement (poetry shell, par exemple)
  2. Installer ipykernel : pip install ipykernel
  3. Ajouter l’environnement : ipython kernel install --user --name=envname
  4. Sélectionner le kernel correspondant dans Jupyter

Packaging

Après avoir rempli pyproject.toml, Poetry permet de construire une archive de package pour notre projet :

poetry build

Cette commande crée en fait deux archives du projet : projet-0.1.0.tar.gz et projet-0.1.0-py3-none-any.whl. Ce dernier est un format wheel qui peut ensuite être installé, avec ses dépendances :

pip install projet-0.1.0-py3-none-any.whl

Il est tout aussi simple de publier le projet sur un dépôt, par défaut PyPI :

poetry publish

Votre projet devient alors accessible à la communauté via pip install projet.

Voici un tableau résumant les fonctionnalités de Poetry et ses alternatives :

  python poetry pipenv conda
installer un package pip install poetry add pipenv install conda install
liste des dépendances requirements.txt pyproject.toml + poetry.lock Pipfile + Pipfile.lock environment.yml
installer toutes les dépendances pip install -r requirements.txt poetry install pipenv install conda install
mettre à jour toutes les dépendances pip-review poetry update pipenv update conda update
créer un environnement virtuel python -m venv (automatique) (automatique) conda create
activer un environnement virtuel source venv/bin/activate poetry shell pipenv shell conda activate
lancer une commande dans l’environnement X poetry run pipenv run X
construire une archive du package setuptools poetry build X conda skeleton + conda-build
publier un package twine poetry publish X anaconda upload

Gérer les versions du code source : Git et GitHub

https://xkcd.com

Git est un logiciel permettant de conserver un historique des modifications du code source, dans ce qu’on appelle un dépôt.
Après avoir installé git (sudo apt install git), on peut initialiser un dépôt dans le répertoire courant :

git init

Chaque modification du dépôt est appelé un commit. Après avoir modifié un fichier, il faut enregistrer les modifications pour le prochain commit :

git add fichier

Le fichier est alors staged.

Après avoir rajouté les modifications souhaitées, vous pouvez vérifier le contenu du prochain commit avec git status puis le valider :

git commit

Git vous demande alors un message expliquant les modifications apportées par ce commit.
Voici quelques règles pour écrire un bon commit:

  • Un commit doit être atomique (une seule modification) : si votre message contient “et”, il faut probablement utiliser deux commits
  • Un commit doit être complet : évitez de faire la moitié d’une tâche et attendez de finir votre modification pour commit
  • Un message commence en général par un verbe et doit être concis, en sautant éventuellement une ligne pour donner des détails

Il est possible de parcourir l’historique des commits avec git log :

git log

commit 2ce5ff0 (HEAD -> master)
Author: Quentin Fortier <qpfortier@gmail.com>
    Fix out of bound error

commit f0a1b71
Author: Quentin Fortier <qpfortier@gmail.com>
    Add README

Note : l’historique de Git a une structure arborescente, constituée (en général) de plusieurs branches. Une nouvelle branche peut être créée lorsque l’on souhaite tester une nouvelle piste sans être sûr qu’elle aboutisse, par exemple. Une fois que le développement de la branche est terminé, on peut la fusionner (merge) avec la branche principale. Pour simplifier, nous utilisons ici une seule branche appelée master (branche principale).

Chaque commit possède un hash permettant d’y faire référence. Par exemple :

git checkout f0a1b71 # revient au commit correspondant
git diff f0a1b71 2ce5ff0 # affiche les différences entre deux commits

À ce stade, le dépôt est stocké en local. Pour travailler à plusieurs sur le même projet, on peut utiliser un serveur distant. Le serveur le plus connu est GitHub. Après avoir créé un dépôt https://github.com/utilisateur/depot.git sur GitHub, on peut le rajouter en tant que dépôt distant, nommé ici origin :

git remote add origin git@github.com:utilisateur/depot.git 

Note : Cette méthode de connection utilise SSH et demande de générer une clé. Il est également possible de se connecter via HTTPS.

Pour envoyer nos commits sur le dépôt distant :

git push origin master # envoie la branche master sur le dépôt distant origin

Il est possible de récupérer un dépôt sur GitHub avec git clone. Par exemple, le dépôt de NumPy :

git clone git@github.com:numpy/numpy.git

Le code source de NumPy est alors téléchargé dans le dossier numpy.

Voici un résumé du workflow de base avec git :

git pull # récupère les derniers commits sur origin
git add fichiers # ajoute des modifications
git commit # crée un commit
git push # envoie un ou plusieurs commits sur origin

Note : Ceci constitue le minimum vital pour l’utilisation de git. Les choses se compliquent quand il y a des conflits (plusieurs modifications sur la même portion de code) ou lorsque plusieurs branches sont utilisées. Voir le site officiel pour une utilisation plus avancée.

En pratique, de nombreux environnements de développement simplifient l’utilisation de git via une interface graphique. Par exemple, Git Graph est un plugin de Visual Code permettant d’agir visuellement sur le graphe Git. Voici un extrait du graphe Git de NumPy :

Git Graph de NumPy

Références

  1. Python Developers Survey 2019, mené par la Python Software Foundation et Jetbrains, éditeur de PyCharm et CLions, entre autres. 

  2. même s’il y a des améliorations, voir : https://pyfound.blogspot.com/2020/11/pip-20-3-new-resolver.html 

Comments