Définition
Deux personnes écrivent la même fonction. L'une met des espaces autour du signe égal, l'autre non : le programme se comporte pareil, seule l'allure du fichier change. Les débats commencent là.
black existe pour les couper court. C'est un formateur de code Python : il relit un fichier, en reconstruit la structure, puis le réécrit selon un style unique. Il ne corrige aucun bug et ne change jamais ce que fait le programme, seulement la façon dont il est écrit.
Surtout, il ne se règle presque pas, et c'est voulu : un style dont on peut discuter redevient un sujet de discussion.
pip install black
black mon_fichier.py
black .Installé avec pip, il travaille en une commande, directement dans le fichier. Son nom vient de la Ford T, disponible dans toutes les couleurs pourvu qu'elle soit noire, et il applique la PEP 8 pour clore la discussion sur le style, pas pour l'alimenter.
Le problème qu'il résout vraiment
Un style irrégulier ne fait planter aucun programme, et c'est pour cela qu'on le laisse s'installer. Le coût est ailleurs, à deux endroits.
En revue de code d'abord : quand rien n'impose la mise en forme, la moitié des remarques portent sur des espaces, et le relecteur survole ce qui comptait.
Dans l'historique ensuite : réindenter un fichier au passage produit quarante lignes modifiées pour un changement qui en concernait une, et le vrai changement disparaît dans le lot.
black efface les deux d'un coup, non parce que son style serait supérieur, mais parce qu'il est le même pour tout le monde et qu'il ne se négocie pas. Deux fichiers écrits de deux façons ressortent identiques, au caractère près.
Ce qu'il décide à votre place
Le premier passage sur un fichier existant surprend toujours. Voici une fonction écrite à la main, puis la même après black.
# Avant : espacement au hasard
def facture(client,lignes,remise = 0,devise='EUR'):
total=sum( l['prix'] for l in lignes )
return {'client':client,'total':total*(1-remise),'devise':devise}
# Après black : un seul style possible
def facture(client, lignes, remise=0, devise="EUR"):
total = sum(l["prix"] for l in lignes)
return {"client": client, "total": total * (1 - remise), "devise": devise}Les guillemets simples sont passés en doubles, les espaces autour des opérateurs normalisés, ceux qui traînaient dans les parenthèses supprimés. Une ligne de plus de 88 caractères aurait été repliée, et aucune de ces décisions n'est un réglage que vous pourriez inverser.
Reste-t-il quelque chose sur quoi garder la main ? Un point, un seul. Une virgule laissée après le dernier élément d'une liste ou d'un appel de fonction vaut consigne, et black met alors un élément par ligne. Retirez-la, il rassemble tout dès qu'il y a la place.
Sans ce mécanisme en tête, l'outil paraît trancher au hasard : le même appel s'étale sur six lignes, puis tient sur une seule.
Là où la mise en forme porte du sens, une matrice alignée à la main par exemple, # fmt: off suspend black et # fmt: on le remet en marche.
Ce qu'il ne fait pas, et qui s'en charge
L'erreur la plus fréquente consiste à en attendre un contrôle qualité. black ne signale ni un import inutilisé, ni une variable jamais lue, ni un appel sur le mauvais type : il déplace du texte, il ne juge pas le code.
Il pose même une condition : devant une SyntaxError, il s'arrête sans rien réécrire, faute de pouvoir analyser la structure qu'il devrait reconstruire.
Chaque vérification dont il se dispense revient à un autre outil, et voici ce que chacun vous rend en retour.
| Besoin | Outil | Ce qu'il produit |
|---|---|---|
| Mise en forme | black | Un fichier réécrit |
| Défauts et scories | ruff | Une liste d'avertissements |
| Cohérence des types | mypy | Des erreurs d'annotation de type |
| Comportement réel | pytest | Des tests qui passent ou non |
La comparaison honnête ne se fait plus avec les anciens formateurs, mais avec ruff format, qui reproduit ce style à quelques cas près et bien plus vite. Si ruff est déjà là pour l'analyse, black fait double emploi.
L'erreur classique de l'adoption
Lancer black . sur un dépôt vivant réécrit trois cents fichiers d'un coup. Le programme fonctionne toujours, mais chaque ligne porte désormais votre nom et la date du jour : l'outil qui retrouve l'auteur d'une décision ne remonte plus qu'à vous.
Pire, un commit qui mêle un correctif et le reformatage du dossier entier est illisible : le relecteur cherche trois lignes utiles parmi mille, puis approuve sans rien vérifier.
La méthode propre tient en trois gestes. Formatez dans un commit qui ne contient que du formatage. Enregistrez son empreinte dans .git-blame-ignore-revs, que l'outil de suivi traversera comme s'il n'existait pas. Verrouillez enfin la suite en intégration continue, où la vérification refuse un fichier mal formaté.
black --check --diff .
git config blame.ignoreRevsFile .git-blame-ignore-revsQuestions fréquentes
Peut-on modifier le style de black ?
Très peu, et c'est délibéré : la longueur de ligne se change, la normalisation des guillemets se désactive, tout le reste est figé. Ces réglages tiennent dans le pyproject.toml, le fichier qu'utilise déjà poetry. Chaque option de plus rouvrirait la discussion que black existe pour fermer.
Faut-il installer black dans chaque projet ?
Oui, dans l'environnement virtuel du projet et avec une version fixée. Le style évolue légèrement d'une version majeure à l'autre : deux machines mal accordées reformatent le fichier chacune à leur façon, et la guerre des diffs reprend.
black peut-il casser mon code ?
C'est très improbable : il compare la structure du fichier réécrit à celle de l'original et refuse le résultat au moindre écart. Une nuance existe pourtant : il nettoie les espaces de fin de ligne dans une docstring, ce qui peut faire échouer un test qui compare cette chaîne au caractère près.