Ruff
Linter et formatter Python ultra-rapide, écrit en Rust par Astral (les mêmes que UV). Un seul outil pour remplacer flake8, black, isort, pyupgrade et une dizaine d'autres — avec une vitesse qui change vraiment la façon de coder.
À quoi ça sert
Deux jobs très complémentaires :
- Linter — trouver les problèmes dans ton code avant l'exécution : variables non utilisées, imports manquants, mauvais idioms, fautes de logique, anti-patterns de sécurité, code mort…
- Formatter — réécrire ton code dans un style standard (espaces, sauts de ligne, longueur des lignes), pour que toute l'équipe livre le même format sans débat.
Historiquement il fallait plusieurs outils : flake8 + plugins pour linter, black pour formatter, isort pour trier les imports, pyupgrade pour moderniser la syntaxe… Ruff fait tout ça, configuré en un seul endroit, et 10 à 100× plus rapide.
flake8 sur un gros projet prend 20-30 secondes. Black 5-10 secondes. Avec Ruff, c'est quelques dizaines de millisecondes. Du coup tu peux lancer le linter à chaque sauvegarde dans VS Code sans ralentissement perceptible — ce qui change complètement le rapport au code : tu vois les erreurs en direct au lieu de les découvrir en CI.
Le concept : règles et codes
Ruff agrège des centaines de règles, chacune identifiée par
un code de la forme F401, E501, UP007.
Le préfixe correspond au plugin d'origine (avant Ruff existait) :
E/W— pycodestyle (PEP 8)F— pyflakes (erreurs de base)I— isort (ordre des imports)UP— pyupgrade (moderniser la syntaxe)B— flake8-bugbear (bugs courants)SIM— flake8-simplify (refacto évidentes)N— pep8-naming (conventions de noms)D— pydocstyle (docstrings)
Tu actives les familles qui t'intéressent dans la config, et tu désactives
les règles qui te gênent. Par défaut, Ruff active un sous-ensemble
raisonnable (E, F).
Pour beaucoup de règles, Ruff sait réparer tout seul :
supprimer un import inutile, trier les imports, moderniser
List[int] en list[int]… Une commande
(ruff check --fix) et le code est nettoyé, sans risque
car les fixes sont conservateurs.
Un exemple d'usage
Code volontairement « sale » :
import os
import sys
from typing import List, Optional
def greet(names: List[str], prefix: Optional[str]=None) -> None:
for n in names:
print(f"hello {n}")
ruff check --fix --select ALL fichier.py
ruff format fichier.py
def greet(names: list[str], prefix: str | None = None) -> None:
for n in names:
print(f"hello {n}")
Imports inutiles supprimés, types modernisés (Python 3.10+), formatage standardisé. Le tout en quelques millisecondes.
How-to : installer et utiliser Ruff
-
Installer Ruff en dépendance dev
Ruff est un outil de développement uniquement (jamais nécessaire à l'exécution), donc en dépendance dev avec UV :
bashuv add --dev ruff -
Linter le projet
bashuv run ruff check . # scan tout le projet uv run ruff check src/ # un sous-dossier uv run ruff check --fix . # corriger ce qui peut l'êtreChaque problème affiche le code de règle, le fichier, la ligne et un extrait :
textsrc/app.py:3:1: F401 [*] `os` imported but unused src/app.py:8:80: E501 Line too long (95 > 88) Found 2 errors. [*] 1 fixable with the `--fix` option. -
Formater le code
Ruff embarque un formatter compatible Black (mêmes règles, mêmes choix), mais beaucoup plus rapide :
bashuv run ruff format . # formate en place uv run ruff format --check . # vérifie sans modifier (CI) uv run ruff format --diff . # affiche les changements proposés -
Configurer dans
pyproject.tomlToute la config tient dans une section
[tool.ruff]de tonpyproject.toml. Bonne base de départ pour un projet débutant :toml (pyproject.toml)[tool.ruff] line-length = 100 target-version = "py312" [tool.ruff.lint] # Familles de règles à activer select = [ "E", # pycodestyle errors "F", # pyflakes "I", # isort "UP", # pyupgrade "B", # bugbear "SIM", # simplify ] ignore = ["E501"] # laisser le formatter gérer la longueur [tool.ruff.format] quote-style = "double" # comme blackOn peut commencer avec
select = ["E", "F"]seulement, puis ajouter des familles au fil du temps quand on est à l'aise. -
Ignorer une règle ponctuellement
Sur une ligne précise, on ajoute un commentaire
# noqa: CODE:pythonimport os # noqa: F401 <- import volontairement non utilisé password = "hardcoded" # noqa: S105Pour ignorer une règle dans un fichier entier, on utilise la section
per-file-ignoresdupyproject.toml— pratique pour exempter par exemple les tests d'une règle. -
Intégrer dans VS Code
L'extension officielle Ruff ajoute le lint et le format au save. Réglages clés dans
.vscode/settings.json:json{ "[python]": { "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.ruff": "explicit", "source.organizeImports.ruff": "explicit" } } }À chaque Ctrl+S : le code est formaté, les imports triés, les erreurs fixables corrigées. Boucle de feedback parfaite.
-
Brancher Ruff en CI
En CI, on lance
ruff check(sans--fix!) etruff format --check. Si l'un échoue, la build casse.yaml (.github/workflows/lint.yml)name: Lint on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v3 - run: uv sync --dev - run: uv run ruff check . - run: uv run ruff format --check . -
Pre-commit (optionnel mais conseillé)
pre-commit lance Ruff avant chaque commit local : impossible de commiter du code mal formaté. Plus de cassures de CI sur des broutilles.
yaml (.pre-commit-config.yaml)repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.7.0 hooks: - id: ruff args: [--fix] - id: ruff-formatbashuv add --dev pre-commit uv run pre-commit install
Aide-mémoire
ruff check . # linter tout le projet
ruff check --fix . # corriger automatiquement
ruff check --watch . # mode re-run au changement
ruff format . # formater
ruff format --check . # CI : vérifier sans modifier
ruff format --diff . # aperçu des changements
ruff rule F401 # doc d'une règle
ruff linter # lister toutes les familles
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]
ignore = ["E501"]
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # autoriser assert dans les tests
x = 1 # noqa: F841 (ignore une règle)
y = 2 # noqa (ignore toutes les règles)
Ruff et le reste de l'écosystème
-
UV — même éditeur (Astral), même
philosophie (Rust, vitesse extrême). Le combo
uv add --dev ruffest standard sur tout projet Python moderne. - pytest — Ruff et pytest se complètent : Ruff vérifie le style et les anti-patterns, pytest vérifie le comportement. Les deux tournent ensemble en CI.
- VS Code / IntelliJ — Ruff a des plugins officiels pour les deux. Format-on-save + fix-on-save = la config gagnante.
-
CI/CD — pattern classique :
un job « lint » qui lance
ruff checketruff format --checksur chaque PR. -
Docstring — la famille
de règles
D(pydocstyle) permet à Ruff de vérifier la présence et le format des docstrings selon Google, NumPy ou PEP 257.
Pour aller plus loin
- Site officiel : docs.astral.sh/ruff
- Dépôt GitHub : github.com/astral-sh/ruff
- Liste des règles : docs.astral.sh/ruff/rules
- Playground en ligne : play.ruff.rs