Définition
Un projet Python grossit vite, et de petits défauts s'accumulent sans que personne les remarque : un import qui ne sert plus depuis un refactor, une variable jamais relue, une ligne qui s'écarte de ce que la PEP 8 recommande. Rien de cela ne fait planter le programme, mais le fichier finit par devenir difficile à relire pour quelqu'un d'autre que son auteur.
C'est ce que ruff est censé rattraper : il relit le code sans l'exécuter et signale ce qui cloche. Ce travail porte un nom, le lint, et il existait bien avant ruff. La nouveauté tient à deux choses : écrit en Rust, il traverse un projet en une fraction de seconde là où ses prédécesseurs demandaient plusieurs minutes, et il regroupe en un seul binaire ce qui demandait quatre ou cinq programmes.
Deux commandes suffisent, depuis la racine du projet.
pip install ruff
ruff check .La commande parcourt tous les fichiers du dossier et affiche une ligne par problème, avec le fichier, la position et un code de règle. Rien n'est modifié tant que vous ne le demandez pas. L'installation passe par pip, dans l'environnement virtuel du projet.
Ce qu'il voit, et ce qu'il ne verra jamais
Un exemple vaut mieux qu'une explication abstraite. Voici un fichier qui tourne sans planter, et qui contient pourtant trois défauts que ruff va repérer.
import os
import json
def charger(chemin):
donnees = json.load(open(chemin))
total = 0
return donneesruff relève l'import os inutile (règle F401), la variable total calculée pour personne (F841), et le fichier jamais refermé : trois défauts invisibles à l'exécution, qui coûtent en lisibilité et en ressources laissées ouvertes.
Une question vient alors naturellement : ruff attrape-t-il tout ce qui peut aller mal ? Non : son analyse est statique, elle lit le texte du programme sans jamais l'exécuter. Le tableau sépare ce qu'il peut repérer de ce qui lui échappe.
| Il le repère | Il ne peut pas le savoir |
|---|---|
| Un import inutilisé ou écrit deux fois | Qu'une liste sera vide et lèvera une IndexError |
| Un nom employé avant d'exister, futur NameError | Qu'une clé manquera dans un dictionnaire au moment de la lire |
| Une comparaison écrite à l'envers | Que votre calcul de remise applique le mauvais pourcentage |
Cette limite découle de ce que ruff est censé faire vite. Elle explique pourquoi il ne remplace ni pytest, qui vérifie ce que le code produit une fois lancé, ni mypy, qui confronte les annotation de type entre elles. Ces trois outils répondent à des questions différentes.
Le formateur, face à black
Repérer les défauts n'est que la moitié du travail de ruff. Sous une commande distincte, ruff format, il réécrit la mise en forme, sans toucher au sens du programme. Ce terrain est celui de black, dont il reprend le style presque au caractère près.
Faut-il basculer, pour qui utilise déjà black ? Sur un projet existant, ruff format ne change pas l'apparence du code, seulement la vitesse et le nombre d'outils à maintenir. Sur un projet neuf, ruff seul évite deux réglages à synchroniser. Sur une équipe attachée à une option précise, un essai sur une branche s'impose avant de trancher.
Retenez la distinction : ruff check répond à « qu'est-ce qui coûte cher ? », ruff format à « à quoi doit ressembler le code ? ». Une indentation irrégulière n'est pas un bug, un import mort ne se règle pas en déplaçant des espaces.
Le réglage qui décide de tout
Une erreur classique consiste à activer d'un coup toutes les règles disponibles, puis à lancer l'outil sur un projet déjà en place. Le terminal renvoie des milliers d'avertissements, en majorité des préférences stylistiques que personne n'a réclamées, et l'outil finit désactivé avant la fin de la semaine.
La marche qui fonctionne : garder le jeu de règles par défaut, qui ne signale que les vrais problèmes, puis ajouter une famille à la fois, en corrigeant ce qu'elle remonte.
| Famille | Ce qu'elle apporte |
|---|---|
F | Les défauts réels : imports morts, variables inutiles, noms inconnus |
E | Les écarts de mise en forme hérités de la norme de style officielle |
I | Le tri et le regroupement des imports en tête de fichier |
UP | Les tournures devenues obsolètes sur les versions récentes de Python |
Le réglage vit dans pyproject.toml, sous [tool.ruff.lint]. La configuration devient commune à toute l'équipe et à l'intégration continue. Un fichier versionné vaut toujours mieux qu'une option tapée à la main, que la moitié des postes oublieront.
Questions fréquentes
Faut-il garder black si on utilise déjà ruff ?
Non, ce n'est plus nécessaire : ruff embarque son propre formateur, et faire tourner les deux revient à réécrire le même fichier deux fois pour un résultat presque identique. Garder l'ancien outil ne se justifie que si la configuration s'appuie sur une option que ruff n'expose pas encore.
Comment réagir face aux centaines d'erreurs affichées au premier passage ?
Surtout pas en les corrigeant toutes le même jour : un commit qui touche cinq cents fichiers rend l'historique illisible et noie les vraies modifications. La méthode qui fonctionne consiste à lancer ruff check --fix, qui règle la part mécanique, puis à restreindre les règles à ce que l'équipe est prête à corriger maintenant.
La correction automatique peut-elle casser du code qui fonctionnait ?
Les corrections par défaut sont dites sûres : elles ne changent jamais le comportement du programme. D'autres, marquées comme risquées, ne s'appliquent qu'avec une option supplémentaire, et peuvent retirer un commentaire ou déplacer une valeur par défaut. Ne jamais lancer une correction automatique sur un dossier de travail non enregistré, pour pouvoir revenir en arrière d'un geste.