Coder peut être une tâche très ardue, en particulier lorsqu'on travaille sur un projet avec différents développeurs. Chaque membre de l'équipe a sa propre manière de coder, ce qui mène à des scripts très hétérogènes.
C'est pourquoi il est important d'avoir un code formatter et un code linter communs afin de rendre vos commits git plus propres. Cela peut être effectué soit entre la phase de staging et de commit, soit pendant la chaîne CI/CD.
Dans cet article, nous verrons comment le faire en tant qu'étape pre-commit en utilisant les git hooks.
Le sommaire est le suivant :
- Black
- Pylint
- Git Hooks
Black
Black est un code formatter Python dont les options de configuration sont délibérément limitées. Il rend votre code plus propre, ce qui vous permet de vous concentrer davantage sur le contenu. La revue de code est également plus efficace, puisque les diffs sont aussi réduits que possible.
Il peut être installé via la ligne de commande suivante :
pip install blackVous pouvez exécuter black sur n'importe quel fichier Python en tapant :
Black pathtofile.py

Black peut être légèrement configuré via le fichier pyproject.toml qui doit être placé à la racine de votre projet. Voici un exemple de ce fichier :
[tool.black]
line-length = 88
target-version = [‘py36’, ‘py37’, ‘py38’]
include = ‘\.pyi?$’
exclude = '''
/(
\.toml
|\.sh
|\.git
|\.ini
|Dockerfile
|Jenkinfile
)/
'''On peut par exemple choisir la longueur des lignes de code et définir les extensions de fichiers qui ne doivent pas être formatés.
Pylint
Pylint est un « outil d'analyse statique de code Python » qui évalue la qualité des scripts développés. Il guide l'équipe vers l'adoption des mêmes standards.
Pylint peut être installé via la ligne de commande suivante :
pip install pylintPour évaluer la qualité de votre code sur un script donné, vous pouvez exécuter :
pylint pathtofile.pyNB : pour pouvoir exécuter pylint sur l'ensemble de votre projet, votre repository doit contenir un fichier __init__.py.
Tout comme Black, Pylint peut également être configuré via le fichier .pylintrc placé lui aussi à la racine de votre projet
[MASTER]
jobs=4 #number of processes to use
[BASIC]
good-names=nameOfYourProject #names to be considered ok
[pre-commit-hook]
command=custom_pylint
disable=E0401, C0301Vous pouvez consulter cette page pour plus de détails sur la configuration.
Git Hooks
Les git hooks sont des scripts définis qui sont lancés lorsqu'une action git est exécutée.
Il existe deux types de hooks :
- Client-side hooks : exécutés après un commit ou un merge
- Server-side hooks : exécutés lors des opérations réseau, par exemple après un push de commits
Dans cet article, nous nous concentrerons sur les client-side hooks, dont le workflow peut être décrit par le graphe suivant :

Dans un repository git, les hooks sont placés dans .git/hooks/ . Ils peuvent être visualisés dans VSCode par exemple en ajoutant les lignes suivantes à vos settings JSON :
"files.exclude": {
"**/.git": false
}

Mise en place des pre-commits
Pour ajouter une action pre-commit à votre repository :
1. Créez le fichier pre-commit dans le dossier .git/hooks/. Vous ne devez inclure aucune extension dans son nom.
2. Ajoutez des commandes bash dans votre fichier. Dans notre cas :
#!/bin/sh
black .
python lint.py -p ../projectName/La première ligne applique le formatter black et la seconde applique le linting sur chaque fichier Python de votre projet. Vous pouvez télécharger le fichier lint.py depuis ce lien :
import argparse
import logging
from pylint.lint import Run
logging.getLogger().setLevel(logging.INFO)
parser = argparse.ArgumentParser(prog="LINT")
parser.add_argument('-p',
'--path',
help='path to directory you want to run pylint | '
'Default: %(default)s | '
'Type: %(type)s ',
default='./src',
type=str)
parser.add_argument('-t',
'--threshold',
help='score threshold to fail pylint runner | '
'Default: %(default)s | '
'Type: %(type)s ',
default=7,
type=float)
args = parser.parse_args()
path = str(args.path)
threshold = float(args.threshold)
logging.info('PyLint Starting | '
'Path: {} | '
'Threshold: {} '.format(path, threshold))
results = Run([path], do_exit=False)
final_score = results.linter.stats['global_note']
if final_score < threshold:
message = ('PyLint Failed | '
'Score: {} | '
'Threshold: {} '.format(final_score, threshold))
logging.error(message)
raise Exception(message)
else:
message = ('PyLint Passed | '
'Score: {} | '
'Threshold: {} '.format(final_score, threshold))
logging.info(message)
exit(0)
NB : un seuil de 7 est défini par défaut. Si le score pylint est inférieur à ce nombre, le commit échouera et vous devrez nettoyer votre script avant de pouvoir commiter à nouveau.
3. Rendez-le exécutable en exécutant la commande bash suivante :
chmod +x .git/hooks/pre-commitUne fois cela fait, chaque commit de votre projet sera précédé du formatage par black et du linting par pylint.
Nous considérons l'application suivante à titre d'illustration :

Lors du commit d'un nouveau changement, on obtient les résultats suivants :

Partager le linter & le formatter
Maintenant que vous avez configuré black comme formatter et pylint comme linter, vous souhaitez sans doute que votre équipe dispose de la même configuration.
Pour ce faire, vous pouvez utiliser le fichier bash suivant, qui sera placé à la racine de votre projet et exécuté par chaque membre de votre équipe après le clonage du repository :
#!/bin/bash
echo $"#/bin/sh\nblack .\npython lint.py -p ../projectName/'> .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
Conclusion
Les pre-commits sont des outils très puissants pour garder votre code bien formaté et pour standardiser les méthodes de développement entre les membres de votre équipe. De la même façon, ils peuvent inclure d'autres vérifications au-delà du linting et du formatage.
Dans cet article, nous avons utilisé une méthode classique qui fait appel à des librairies Python installées. Vous pouvez aussi utiliser le module Python pre-commit, qui inclut de nombreuses vérifications et fait appel à leurs repositories GitHub. Il est très efficace mais peut être difficile à mettre en place, en particulier face à des filtres proxy.
