Recherche de site Web

Ruff : un Linter Python moderne pour un code sans erreur et maintenable


Le peluchage est essentiel pour écrire du code propre et lisible que vous pouvez partager avec d'autres. Un linter, comme Ruff, est un outil qui analyse votre code et recherche les erreurs, les problèmes de style et les constructions suspectes. Linting vous permet de résoudre les problèmes et d'améliorer la qualité de votre code avant de valider votre code et de le partager avec d'autres.

Ruff est un linter moderne extrêmement rapide et doté d'une interface simple, ce qui le rend simple à utiliser. Il vise également à remplacer de nombreux autres outils de peluchage et de formatage, tels que Flake8, isort et Black. Il devient rapidement l’un des linters Python les plus populaires.

Dans ce didacticiel, vous apprendrez à :

  • Installer Ruff
  • Vérifiez votre code Python pour détecter les erreurs
  • Corrigez automatiquement vos erreurs de peluchage
  • Utilisez Ruff pour formater votre code
  • Ajoutez des configurations facultatives pour booster votre peluchage

Pour tirer le meilleur parti de ce didacticiel, vous devez être familier avec les environnements virtuels, installer des modules tiers et être à l'aise avec l'utilisation du terminal.

Installation de Ruff

Maintenant que vous savez pourquoi le peluchage de votre code est important et en quoi Ruff est un outil puissant pour ce travail, il est temps de l'installer. Heureusement, Ruff fonctionne immédiatement, donc aucune instruction d'installation ou configuration compliquée n'est nécessaire pour commencer à l'utiliser.

En supposant que votre projet soit déjà configuré avec un environnement virtuel, vous pouvez installer Ruff des manières suivantes :

$ python -m pip install ruff

En plus de pip, vous pouvez également installer Ruff avec Homebrew si vous êtes sous macOS ou Linux :

$ brew install ruff

Les utilisateurs de Conda peuvent installer Ruff en utilisant conda-forge :

$ conda install -c conda-forge ruff

Si vous utilisez Arch, Alpine ou openSUSE Linux, vous pouvez également utiliser les référentiels de distribution officiels. Vous trouverez des instructions spécifiques sur la page d'installation de Ruff de la documentation officielle.

De plus, si vous souhaitez que Ruff soit disponible pour tous vos projets, vous souhaiterez peut-être installer Ruff avec pipx.

Vous pouvez vérifier que Ruff est correctement installé en utilisant la commande ruff version :

$ ruff version
ruff 0.4.7

Pour que la commande ruff apparaisse dans votre PATH, vous devrez peut-être fermer et rouvrir votre application de terminal ou démarrer une nouvelle session de terminal.

Linter votre code Python

Bien que le linting aide à garder votre code cohérent et sans erreur, il ne garantit pas que votre code sera sans bug. Il est préférable de trouver les bogues dans votre code avec un débogueur et des tests adéquats, qui ne seront pas abordés dans ce didacticiel. Dans les sections suivantes, vous apprendrez comment utiliser Ruff pour vérifier les erreurs et accélérer votre flux de travail.

Vérification des erreurs

Le code ci-dessous est un script simple appelé one_ring.py. Lorsque vous l'exécutez, il obtient un nom de personnage Seigneur des Anneaux aléatoire à partir d'un tuple et vous permet de savoir si ce personnage portait le fardeau de One Ring. Ce code n’a pas de réelle utilité pratique et est juste un peu amusant. Quelle que soit la taille de votre base de code, les étapes seront les mêmes :

import os
import random

CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")

def random_character():
    return random.choice(CHARACTERS)

def ring_bearer():
    return name in ("Frodo", "Sam")

if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

Maintenant, si vous avez les yeux d’aigle, vous avez peut-être déjà repéré quelques problèmes avec ce code. Sinon, ne vous inquiétez pas, vous pouvez utiliser Ruff pour tous les trouver.

La commande la plus basique de la CLI Ruff (interface de ligne de commande) est check. Par défaut, cette commande vérifiera tous les fichiers du répertoire actuel. Pour cet exemple, vous pouvez exécuter la commande check sans aucun argument. Lorsque vous exécutez check sur le code ci-dessus, il génère ce qui suit :

$ ruff check
one_ring.py:1:8: F401 [*] `os` imported but unused
one_ring.py:10:12: F821 Undefined name `name`
Found 2 errors.
[*] 1 fixable with the `--fix` option.

Succès! Ruff a trouvé deux erreurs. Non seulement il affiche les numéros de fichier et de ligne des erreurs, mais il vous donne également des codes d'erreur et des messages. De plus, il vous permet de savoir que l’une des deux erreurs est réparable. Super!

Vous pouvez demander à Ruff de corriger les erreurs en appliquant l'indicateur --fix. Voici ce qui se passe lorsque vous suivez sa suggestion :

$ ruff check --fix
one_ring.py:9:12: F821 Undefined name `name`
Found 2 errors (1 fixed, 1 remaining).

L'importation inutilisée est maintenant corrigée et cette ligne de code a été supprimée de one_ring.py. La dernière de ces deux erreurs n’est pas automatiquement réparable. Le problème de la ligne 9 est peut-être évident pour vous, mais ce n'est peut-être pas le cas.

Heureusement, Ruff vous donne le code d'erreur et un moyen de le rechercher rapidement sans avoir à rechercher la documentation en ligne. Entrez la deuxième commande ruff : rule.

Puisque Ruff fournit le code d'erreur, vous pouvez le transmettre à la commande ruff Rule pour voir plus de détails sur le message d'erreur, y compris un exemple de code :

$ ruff rule F821

Lorsque vous exécutez cette commande, vous obtenez plus de détails au format Markdown dans votre terminal :

# undefined-name (F821)

Derived from the **PyFlakes** linter.

## What it does
Checks for uses of undefined names.

## Why is this bad?
An undefined name is likely to raise `NameError` at runtime.

## Example

```python
def double():
    return n * 2  # raises `NameError` if `n` is undefined when `double` is called
```

Use instead:

```python
def double(n):
    return n * 2
```

## References
- [Python documentation: Naming and binding](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)

Avec le contexte supplémentaire du code d'erreur, vous pouvez maintenant voir que l'exemple de code que vous avez vu précédemment a commis la même erreur. La variable name de la ligne 9 n'a pas été passée en argument à la signature de la fonction ring_bearer(). Oups !

Pour corriger cette erreur, vous pouvez modifier ring_bearer() pour prendre l'argument name :

# ...

def ring_bearer(name):
    return name in ("Frodo", "Sam")

Maintenant que vous avez apporté cette petite modification au code, vous pouvez exécuter à nouveau ruff check pour voir s'il réussit :

$ ruff check
All checks passed!

Super! Les deux erreurs sont désormais corrigées et votre code devrait ressembler à ceci :

import random

CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")

def random_character():
    return random.choice(CHARACTERS)

def ring_bearer(name):
    return name in ("Frodo", "Sam")

if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

Devoir exécuter ruff check chaque fois que vous modifiez votre code peut s'avérer peu pratique. Heureusement, Ruff a une solution. Dans la section suivante, vous verrez comment vérifier en permanence les erreurs dans votre code.

Accélérer votre flux de travail

Lorsque vous travaillez activement sur du code, Ruff peut simplifier encore plus votre flux de travail en vous informant des erreurs au fur et à mesure de votre développement. Cela accélérera le processus global et vous rendra plus productif. Pour avoir un peluchage continu pendant que vous codez, ouvrez une nouvelle fenêtre de terminal et transmettez l'indicateur --watch à la commande check :

$ ruff check --watch

Après avoir exécuté la commande ci-dessus, vous devriez voir quelque chose comme ceci dans votre terminal :

[14:04:01 PM] Starting linter in watch mode...
[14:04:01 PM] Found 0 errors. Watching for file changes.

Votre code est désormais exempt d'erreurs. Ou est-ce ? Dans la section suivante, vous découvrirez ce que Ruff n’a pas récupéré par défaut.

Trouver plus d'erreurs

Même si les erreurs trouvées par Ruff ont été corrigées, le code doit encore être nettoyé. Il y a quelques problèmes supplémentaires avec le fichier one_ring.py qui pourraient être résolus pour rendre ce code encore plus propre et plus lisible. Le problème le plus notable se trouve à la ligne 3. Le tuple CHARACTERS semble trop long et pourrait être rendu plus lisible.

Vous vous posez peut-être la question : pourquoi Ruff n’a-t-il pas compris cela ? C'est une question parfaitement valable. En fouillant dans la documentation, vous obtenez cette réponse :

Par défaut, Ruff active les règles F de Flake8, ainsi qu'un sous-ensemble des règles E , en omettant toutes les règles stylistiques qui chevauchent l'utilisation d'un formateur, comme ruff. format ou Noir. (Source)

Ruff prêt à l'emploi n'applique pas la règle pour vérifier la longueur des lignes. Vous pouvez cependant lui indiquer les règles supplémentaires que vous souhaitez inclure ou exclure. Vous pouvez lui demander d'inclure toutes les règles E ou une règle spécifique avec le flag --select :

$ ruff check --select E
one_ring.py:4:89: E501 Line too long (122 > 88)
Found 1 error.

$ ruff check --select E501
one_ring.py:4:89: E501 Line too long (122 > 88)
Found 1 error.

Ah, vous avez trouvé l'erreur supplémentaire. Cependant, vous remarquerez peut-être qu'il n'y a aucune suggestion pour vous informer que la longueur de la ligne peut être automatiquement corrigée avec l'indicateur --fix. Ne vous inquiétez pas car il existe un moyen de corriger les erreurs de formatage dans Ruff avec une nouvelle commande. Dans la section suivante, vous découvrirez le format ruff.

Formater votre code Python

Par défaut, Ruff a des règles de formatage sensées et a été conçu pour être un remplacement immédiat du noir. La commande format est disponible depuis la version 0.1.2 de Ruff.

Tout comme la commande check, la commande format prend des arguments facultatifs pour un chemin vers un seul fichier ou répertoire. Étant donné que le code que vous avez dans cet exemple de didacticiel est un fichier unique, vous pouvez l'utiliser sans aucun argument :

$ ruff format
1 file reformatted

Votre fichier one_ring.py devrait désormais paraître plus lisible et avoir un formatage cohérent :

import random

CHARACTERS = (
    "Frodo",
    "Sam",
    "Merry",
    "Pippin",
    "Aragorn",
    "Legolas",
    "Gimli",
    "Boromir",
    "Gandalf",
    "Saruman",
    "Sauron",
)


def random_character():
    return random.choice(CHARACTERS)


def ring_bearer(name):
    return name in ("Frodo", "Sam")


if __name__ == "__main__":
    character = random_character()
    if ring_bearer(character):
        print(f"{character} is a ring bearer")
    else:
        print(f"{character} is not a ring bearer")

Comme vous pouvez le constater, l'erreur de longueur de ligne précédente dans la ligne 3 a été corrigée. Et même si le tuple prend plus de lignes, il est beaucoup plus facile d’analyser et de lire la liste des noms de caractères. Cela permet également aux réviseurs de code d'examiner plus facilement les modifications, car la plupart des outils et des plates-formes afficheront uniquement ce qui a exactement changé dans le diff et non dans l'ensemble de la structure des données.

Le prochain changement apporté est que l'espacement entre les fonctions est désormais cohérent et conforme à la PEP 8, avec les deux espaces recommandés entre les fonctions.

Le dernier changement, même s'il peut paraître insignifiant, est que Ruff a ajouté la nouvelle ligne manquante à la fin du fichier.

Il s’agit d’un court morceau de code simple à formater. Des bases de code plus longues peuvent nécessiter de nombreuses modifications, ce qui pourrait potentiellement interrompre certaines fonctionnalités, bien que cela soit rare car les formateurs font toujours preuve de prudence. Pour en savoir plus sur les correctifs dangereux dans Ruff, reportez-vous à la section relative à la sécurité des correctifs dans la documentation de Ruff.

Si vous souhaitez voir quelles modifications seront apportées lorsque vous exécuterez ruff format, vous pouvez l'exécuter avec l'indicateur --diff pour voir les modifications proposées avant d'effectuer eux. Si vous aviez exécuté l'indicateur --diff avant d'exécuter le ruff format, vous auriez vu ce résultat :

--- one_ring.py
+++ one_ring.py
@@ -1,16 +1,31 @@
 import random


-CHARACTERS = ("Frodo", "Sam", "Merry", "Pippin", "Aragorn", "Legolas", "Gimli", "Boromir", "Gandalf", "Saruman", "Sauron")
+CHARACTERS = (
+    "Frodo",
+    "Sam",
+    "Merry",
+    "Pippin",
+    "Aragorn",
+    "Legolas",
+    "Gimli",
+    "Boromir",
+    "Gandalf",
+    "Saruman",
+    "Sauron",
+)
+

 def random_character():
     return random.choice(CHARACTERS)

+
 def ring_bearer(name):
     return name in ("Frodo", "Sam")

+
 if __name__ == "__main__":
     character = random_character()
     if ring_bearer(character):
         print(f"{character} is a ring bearer")
     else:
-        print(f"{character} is not a ring bearer")
\ No newline at end of file
+        print(f"{character} is not a ring bearer")

1 file would be reformatted

C’est peut-être tout ce dont vous avez besoin pour formater votre code. Cependant, il peut arriver que vous préfériez une longueur de ligne différente ou que vous souhaitiez inclure ou exclure certaines règles. Dans ces situations, cela peut prendre beaucoup de temps de répertorier toutes vos règles requises sur la ligne de commande chaque fois que vous souhaitez linter votre code. Il doit y avoir une meilleure façon !

Il y a. Bien que cela ne soit pas obligatoire, Ruff peut être hautement configurable. Dans la section suivante, vous aurez un bref aperçu de quelques bases de configuration.

Configuration de Ruff

Si vous lavez une base de code plus grande, avez plusieurs committers ou souhaitez personnaliser votre expérience, Ruff vous permet de stocker votre configuration dans un fichier TOML. Plus précisément, un fichier ruff.toml, .ruff.toml ou votre fichier pyproject.toml existant.

Comme mentionné précédemment, ruff a des valeurs par défaut raisonnables. Ces configurations sont documentées sur la page de configuration de Ruff pour que vous puissiez les lire. La liste complète des paramètres disponibles pour votre configuration est bien documentée. Voici un exemple de configuration simple ruff.toml que vous pouvez ajouter à votre projet :

line-length = 88

[lint]
select = ["E501", "I"]

[format]
docstring-code-format = true
docstring-code-line-length = 72

Et voici le même exemple au format pyproject.toml. Le seul changement est que vous devez inclure un préfixe tool.ruff dans chaque en-tête de tableau :

[tool.ruff]
line-length = 88

[tool.ruff.lint]
select = ["E501", "I"]

[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 72

Dans ces exemples, vous remarquerez quelques nouvelles règles. Tout comme vous l'avez fait précédemment, vous avez spécifié que vous souhaitez inclure la règle E501 lors du peluchage avec ruff, qui renverra une erreur lorsque la longueur de la ligne est supérieure à la par défaut 88 caractères.

En plus d'ajouter la règle E501 à la configuration du linting, vous avez également demandé à Ruff d'ajouter toutes les règles I. Les règles I sont uniques à isort, un autre package que vous avez peut-être déjà utilisé pour lint et formater vos instructions Python import. Avec cette configuration, vous n'avez plus besoin d'isort et de Black pour formater votre code. Cela signifie moins d’outils à gérer et moins de dépendances des développeurs.

Dans les lignes 6 à 8, vous verrez que Ruff formatera désormais vos docstrings sur une longueur de 72 caractères. Ce numéro peut être tout ce que vous voulez, et beaucoup peuvent choisir 88 caractères pour correspondre à la longueur de la ligne de code. Gardez à l’esprit que par défaut, Ruff ne formate pas les docstrings.

De nombreux paramètres de peluchage et de formatage sont disponibles, c'est donc une bonne idée de faire défiler la liste des paramètres pour voir ceux que vous souhaitez ajouter à votre configuration Ruff.

Si vous avez déjà de l'expérience avec un linter, n'hésitez pas à partager vos règles et personnalisations préférées dans les commentaires ci-dessous.

Prochaines étapes

Maintenant que vous avez appris pourquoi vous devriez utiliser un linter et comment Ruff est un excellent outil pour vous aider à obtenir un code propre, lisible et sans erreur, vous devriez essayer Ruff.

Comme mentionné ci-dessus, il existe une multitude de configurations que vous pouvez utiliser pour faire passer votre peluchage au niveau supérieur. Il existe également quelques intégrations qui peuvent accélérer votre flux de travail, telles que l'extension VS Code, le plugin PyCharm, le hook de pré-commit et les actions GitHub.

Conclusion

Ruff est un linter et un formateur de code Python extrêmement rapide qui peut vous aider à améliorer la qualité et la maintenabilité de votre code. Ce didacticiel explique comment démarrer avec Ruff, présente ses principales fonctionnalités et démontre à quel point il peut être puissant.

Dans ce didacticiel, vous avez appris à :

  • Installer Ruff
  • Vérifiez votre code Python pour détecter les erreurs
  • Corrigez automatiquement vos erreurs de peluchage
  • Utilisez Ruff pour formater votre code
  • Ajoutez des configurations facultatives pour booster votre peluchage

Avec ce nouvel outil dans votre boîte à outils, vous pourrez faire passer votre code au niveau supérieur et vous assurer qu'il a un aspect professionnel et, plus important encore, qu'il est sans erreur.