Zum Inhalt springen

cat posts/ruff-linter-und-formatter.md

Ruff: Linter und Formatter in einem

Ruff findet Fehler, modernisiert Python-Code, sortiert Imports und übernimmt die Formatierung – schnell, zentral konfiguriert und direkt in Editor, pre-commit und CI integrierbar.

In Teil 6 der Python-Serie ging es darum, Code möglichst klar und pythonic zu schreiben.

Im Alltag musst Du aber nicht jede Stilregel selbst im Kopf behalten. Dafür gibt es zwei Arten von Tools:

  • Ein Linter analysiert den Code und weist auf Fehler, verdächtige Stellen oder unerwünschte Schreibweisen hin.
  • Ein Formatter formatiert den Code automatisch nach einem einheitlichen Stil.

Lange Zeit wurden dafür mehrere Tools kombiniert: Flake8 für das Linting, Black für die Formatierung, isort für Imports, pyupgrade für modernere Syntax und verschiedene Flake8-Plugins für weitere Checks.

Ruff kann viele dieser Aufgaben in einem einzigen Werkzeug übernehmen. Es ist in Rust geschrieben, sehr schnell und lässt sich zentral über die pyproject.toml konfigurieren.

Linter und Formatter sind nicht dasselbe

Bevor wir Ruff installieren, sollten wir die beiden Aufgaben sauber voneinander trennen.

Betrachten wir diesen Code:

def status(name,hp,max_hp=100):
  if name == None:
      name='Unbekannt'
  return f'{name}|HP:{hp}/{max_hp}'

Ein Formatter kümmert sich um die äußere Form:

def status(name, hp, max_hp=100):
    if name == None:
        name = "Unbekannt"
    return f"{name}|HP:{hp}/{max_hp}"

Er korrigiert unter anderem:

  • Einrückungen
  • Leerzeichen
  • Zeilenumbrüche
  • Anführungszeichen
  • die Aufteilung langer Ausdrücke

Der Vergleich mit None ist aber weiterhin unpassend:

name == None

Darauf weist der Linter hin. Pythonic wäre:

name is None

Ein Formatter entscheidet also, wie Code dargestellt wird. Ein Linter untersucht stärker, was im Code steht und ob daran etwas verdächtig oder verbesserungswürdig ist.

Ruff stellt dafür zwei getrennte Kommandos bereit:

ruff check .
ruff format .

Was Ruff ersetzen kann

Der Ruff-Linter ist als Ersatz für mehrere verbreitete Tools und Plugins konzipiert, darunter:

  • Flake8
  • Pyflakes
  • isort
  • pyupgrade
  • pydocstyle
  • autoflake
  • flake8-bugbear
  • flake8-simplify
  • zahlreiche weitere Flake8-Plugins

Der Formatter ist als weitgehend kompatibler Ersatz für Black gedacht.

Das bedeutet nicht, dass Ruff jedes dieser Tools in jedem Detail exakt nachbildet. Besonders der Formatter besitzt einige bewusste Abweichungen von Black. Ein Projekt sollte deshalb nicht dauerhaft abwechselnd mit Black und Ruff formatiert werden.

Entscheide Dich für einen Formatter und verwende ihn im gesamten Projekt einheitlich.

Warum Ruff so schnell ist

Viele klassische Python-Tools sind selbst in Python geschrieben und analysieren den Code teilweise unabhängig voneinander.

Ruff ist dagegen in Rust implementiert und bündelt zahlreiche Checks in einem gemeinsamen Durchlauf. Außerdem verwendet es einen Cache, damit unveränderte Dateien nicht bei jedem Aufruf vollständig neu analysiert werden müssen.

Astral gibt für Ruff – abhängig vom Projekt und dem Vergleichswerkzeug – Geschwindigkeitsvorteile im Bereich von etwa 10- bis 100-fach an.

Für ein kleines Skript spielt der Unterschied kaum eine Rolle. In größeren Projekten, im Editor oder bei jedem Commit sorgt die kurze Laufzeit aber dafür, dass Ruff praktisch ohne störende Wartezeit ausgeführt werden kann.

Ruff installieren

Installiere Ruff möglichst innerhalb der Entwicklungsumgebung Deines Projekts.

Mit pip:

python -m pip install ruff

Verwendest Du uv, kannst Du Ruff als Development Dependency hinzufügen:

uv add --dev ruff

Anschließend prüfst Du die Installation:

ruff --version

Bei einem mit uv verwalteten Projekt kannst Du Ruff ohne aktivierte Virtual Environment über uv run starten:

uv run ruff --version

In den folgenden Beispielen verwende ich direkt den Befehl ruff. Falls Du Ruff über uv ausführst, setzt Du entsprechend uv run davor.

Ein erstes Beispiel

Lege eine Datei namens beispiel.py an:

import os
import sys


def status(name,hp,max_hp=100):
  debug = "temporär"
  if name == None:
      name='Unbekannt'
  return f'{name}|HP:{hp}/{max_hp}'

Lass Ruff die Datei prüfen:

ruff check beispiel.py

Je nach Ruff-Version und Konfiguration erscheinen Meldungen wie:

F401 `os` imported but unused
F401 `sys` imported but unused
F841 Local variable `debug` is assigned to but never used
E711 Comparison to `None` should be `cond is None`

Jede Meldung besitzt einen Rule Code wie F401 oder E711.

Der Buchstabe beziehungsweise das Präfix bezeichnet die Rule Family. Die Ziffern identifizieren die konkrete Regel.

Zu einer Regel kannst Du Dir direkt im Terminal weitere Informationen anzeigen lassen:

ruff rule F401

Ruff erklärt dann:

  • was die Regel prüft,
  • warum die gefundene Stelle problematisch sein kann,
  • wie eine passende Korrektur aussieht,
  • ob ein automatischer Fix verfügbar ist.

Automatische Fixes

Viele Lint-Verstöße kann Ruff automatisch beheben:

ruff check --fix beispiel.py

Ruff entfernt beispielsweise ungenutzte Imports, modernisiert bestimmte Schreibweisen oder korrigiert einfache Vergleiche.

Standardmäßig wendet --fix nur Korrekturen an, die Ruff als safe einstuft. Solche Fixes sollen das Laufzeitverhalten des Programms nicht verändern.

Mögliche, aber nicht automatisch angewendete Fixes werden weiterhin gemeldet.

Unsafe Fixes

Einige Änderungen können das Verhalten des Programms verändern oder Kommentare entfernen. Ruff kennzeichnet sie deshalb als unsafe.

Du kannst sie anzeigen lassen mit:

ruff check --unsafe-fixes .

Und ausdrücklich anwenden mit:

ruff check --fix --unsafe-fixes .

Diese Option sollte bewusst verwendet werden. Prüfe die Änderungen anschließend über Git oder einen anderen Diff-Viewer.

Für den normalen Workflow reicht meistens:

ruff check --fix .

Code formatieren

Nach den Lint-Fixes formatierst Du die Datei:

ruff format beispiel.py

Das Ergebnis sieht ungefähr so aus:

def status(name, hp, max_hp=100):
    if name is None:
        name = "Unbekannt"

    return f"{name}|HP:{hp}/{max_hp}"

Der Formatter arbeitet direkt in der Datei.

Ein gesamtes Projekt formatierst Du mit:

ruff format .

Der Punkt steht für das aktuelle Verzeichnis. Ruff sucht darin rekursiv nach Python-Dateien.

Nur prüfen, ohne Dateien zu verändern

In einer CI-Pipeline soll Ruff normalerweise keine Dateien umschreiben. Stattdessen soll der Build fehlschlagen, wenn Code nicht korrekt formatiert ist.

Dafür gibt es:

ruff format --check .

Das Kommando verändert keine Dateien. Es liefert aber einen Fehlerstatus zurück, wenn der Formatter Änderungen vornehmen würde.

Beim Linter genügt:

ruff check .

Auch dieses Kommando verändert ohne --fix keine Dateien.

Die beiden typischen CI-Kommandos lauten deshalb:

ruff check .
ruff format --check .

Der normale lokale Workflow

Für die lokale Entwicklung ist diese Reihenfolge sinnvoll:

ruff check --fix .
ruff format .

Der Linter läuft zuerst, weil seine Fixes den Code verändern können. Danach bringt der Formatter das Ergebnis in die endgültige Form.

Möchtest Du anschließend sicherstellen, dass keine nicht automatisch behebbaren Probleme übrig sind, führst Du zusätzlich aus:

ruff check .

Der vollständige Ablauf lautet dann:

ruff check --fix .
ruff format .
ruff check .

Der Formatter sortiert keine Imports

Ein häufiger Irrtum ist, dass ruff format gleichzeitig die Imports sortiert.

Das tut der Formatter bewusst nicht.

Für die Import-Sortierung ist die Rule Family I zuständig. Sie ist eine Neuimplementierung der isort-Regeln innerhalb des Ruff-Linters.

Imports sortierst Du gezielt mit:

ruff check --select I --fix .

Ist I bereits in Deiner Konfiguration aktiviert, genügt:

ruff check --fix .

Danach folgt wieder der Formatter:

ruff format .

Genau deshalb sollte der Linter mit Fixes vor dem Formatter laufen.

Konfiguration in pyproject.toml

Ruff kann über folgende Dateien konfiguriert werden:

  • pyproject.toml
  • ruff.toml
  • .ruff.toml

Für ein normales Python-Projekt bietet sich die bereits vorhandene pyproject.toml an.

Eine sinnvolle Ausgangskonfiguration für unsere Python-Serie sieht so aus:

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = [
    "E4",
    "E7",
    "E9",
    "F",
    "I",
    "UP",
    "B",
    "SIM",
]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"

line-length

line-length = 88

Diese Einstellung legt die gewünschte maximale Zeilenlänge fest. Der Wert 88 entspricht dem verbreiteten Default von Black und Ruff.

Der Formatter versucht, Code passend umzubrechen. Er kann die Grenze aber nicht in jedem Fall einhalten. Lange URLs, Kommentare oder einzelne Strings lassen sich nicht immer sinnvoll aufteilen.

target-version

target-version = "py312"

Ruff geht dadurch davon aus, dass Dein Code mindestens Python 3.12 verwenden darf.

Diese Information beeinflusst unter anderem:

  • welche Syntax als gültig gilt,
  • welche Modernisierungen Ruff vorschlagen kann,
  • wie bestimmte f-Strings formatiert werden,
  • welche versionsabhängigen Regeln aktiv werden.

Verwendet Dein Projekt bereits:

[project]
requires-python = ">=3.12"

kann Ruff die minimale Python-Version in vielen Fällen daraus ableiten. Eine explizite target-version bleibt dennoch hilfreich, wenn die gewünschte Zielversion besonders deutlich dokumentiert werden soll.

Die ausgewählten Rule Families

Unsere Beispielkonfiguration aktiviert diese Gruppen:

Präfix Ursprung beziehungsweise Zweck
E4, E7, E9 grundlegende pycodestyle-Regeln
F Pyflakes, etwa ungenutzte Imports oder unbekannte Namen
I Import-Sortierung nach isort-Art
UP Modernisierung veralteter Python-Syntax
B flake8-bugbear für häufige Fehler und problematische Konstrukte
SIM flake8-simplify für unnötig komplizierten Code

Einige Beispiele:

F401

Ein Import wird nicht verwendet.

F821

Ein Name ist nicht definiert.

I001

Ein Import-Block ist nicht korrekt sortiert.

UP015

Ein überflüssiges Mode-Argument bei open() (etwa "r") kann entfernt werden.

B006

Eine Funktion verwendet einen mutablen Default-Wert.

Gerade B006 passt zu einem Problem aus Teil 5:

def fuege_hinzu(gegenstand, inventar=[]):
    inventar.append(gegenstand)

Ruff weist darauf hin, dass dieselbe Liste über mehrere Funktionsaufrufe hinweg wiederverwendet wird.

Nicht sofort alle Regeln aktivieren

Ruff bietet den besonderen Selector ALL:

[tool.ruff.lint]
select = ["ALL"]

Damit werden nahezu alle verfügbaren Regeln aktiviert.

Für ein neues Lernprojekt klingt das zunächst attraktiv, erzeugt aber schnell sehr viele Warnungen. Manche Regeln bilden alternative Stilentscheidungen ab, andere setzen umfangreiche Docstrings, Type Annotations oder besonders strenge Konventionen voraus.

Außerdem können bei einem Ruff-Update neue Regeln hinzukommen und dadurch plötzlich neue Fehler im Projekt erscheinen.

Beginne deshalb mit einer überschaubaren Auswahl und erweitere sie schrittweise.

Eine mögliche Entwicklung wäre:

select = ["E4", "E7", "E9", "F"]

Dann:

select = ["E4", "E7", "E9", "F", "I", "UP"]

Und später:

select = ["E4", "E7", "E9", "F", "I", "UP", "B", "SIM"]

So kannst Du nachvollziehen, welche neue Rule Family welche Meldungen erzeugt.

Regeln ignorieren

Nicht jede Ruff-Meldung ist in jedem Kontext sinnvoll. Eine Regel sollte aber möglichst gezielt und mit erkennbarem Grund unterdrückt werden.

Eine einzelne Zeile ignorieren

Mit # noqa kannst Du eine Meldung unterdrücken:

import optionales_modul  # noqa

Besser ist es, den konkreten Rule Code anzugeben:

import optionales_modul  # noqa: F401

Ein Kommentar ohne Code unterdrückt alle Ruff-Meldungen der Zeile:

import optionales_modul  # noqa

Diese breite Form solltest Du nur verwenden, wenn Du tatsächlich alle Regeln an dieser Stelle ignorieren möchtest.

Eine Regel projektweit ignorieren

In der pyproject.toml:

[tool.ruff.lint]
ignore = ["E501"]

Damit wird die Regel E501 im gesamten Projekt deaktiviert.

Projektweite Ausnahmen sollten sparsam eingesetzt werden. Prüfe zunächst, ob die Regel grundsätzlich unerwünscht ist oder nur an wenigen besonderen Stellen stört.

Ausnahmen für bestimmte Dateien

Manche Regeln sollen nur in bestimmten Dateien nicht gelten:

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]

Das kann sinnvoll sein, wenn __init__.py bewusst Namen importiert, um sie als öffentliche API eines Packages bereitzustellen.

Noch klarer ist häufig ein expliziter Re-Export:

from .spieler import Spieler as Spieler

Durch die Wiederholung des Namens erkennt Ruff, dass der Import absichtlich öffentlich angeboten wird.

Formatter-Konfiguration

Der Ruff-Formatter ist bewusst wenig konfigurierbar. Ein Formatter soll Diskussionen über Stil möglichst beenden, statt für jedes Teammitglied einen anderen Stil abzubilden.

Einige Einstellungen sind dennoch möglich:

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"

Anführungszeichen

quote-style = "double"

Ruff bevorzugt doppelte Anführungszeichen:

name = "Karl"

Mit

quote-style = "single"

würde es einfache Anführungszeichen bevorzugen:

name = 'Karl'

Ruff kann davon abweichen, wenn die andere Variante unnötige Escape-Sequenzen vermeidet oder für die gewählte Python-Version syntaktisch erforderlich ist.

Einrückung

indent-style = "space"

verwendet Leerzeichen.

Alternativ unterstützt Ruff:

indent-style = "tab"

Für Python-Projekte sind vier Leerzeichen die verbreitete und empfohlene Schreibweise.

Code in Docstrings formatieren

Ruff kann Python-Beispiele innerhalb von Docstrings formatieren:

[tool.ruff.format]
docstring-code-format = true

Das betrifft unter anderem:

  • Doctest-Beispiele
  • Markdown-Codeblöcke innerhalb eines Docstrings
  • passende reStructuredText-Codeblöcke

Die Funktion ist optional und standardmäßig nicht zwingend erforderlich.

Formatierung gezielt deaktivieren

In seltenen Fällen soll ein bestimmter Codeblock seine manuelle Formatierung behalten.

Dafür kannst Du # fmt: off und # fmt: on verwenden:

# fmt: off
werte = [
    1,     2,     3,
    10,    20,    30,
    100,   200,   300,
]
# fmt: on

Für einzelne geeignete Statements gibt es außerdem:

werte = [1,2,3,4,5]  # fmt: skip

Solche Ausnahmen sollten selten bleiben. Je häufiger Formatierung deaktiviert wird, desto weniger erfüllt der Formatter seinen Zweck.

Editor-Integration

Ruff besitzt einen eigenen, in Rust geschriebenen Language Server:

ruff server

Du startest ihn normalerweise nicht selbst. Eine Editor-Erweiterung übernimmt das im Hintergrund.

Der Language Server kann unter anderem:

  • Ruff-Diagnosen direkt im Editor anzeigen,
  • Quick Fixes anbieten,
  • automatisch behebbare Verstöße korrigieren,
  • Imports organisieren,
  • den eingebauten Formatter verwenden.

Für Visual Studio Code gibt es eine offizielle Ruff-Erweiterung. Auch viele andere Editoren können Ruff über das Language Server Protocol oder eine direkte Integration verwenden.

Ruff konzentriert sich dabei auf Linting und Formatierung. Für Funktionen wie vollständige Autocompletion, Navigation zu Definitionen und Type Checking wird es typischerweise zusammen mit einem weiteren Python Language Server verwendet.

Eine sinnvolle Editor-Konfiguration führt beim Speichern in dieser Reihenfolge aus:

  1. sichere Ruff-Fixes anwenden,
  2. Imports organisieren,
  3. Code formatieren.

Die genaue Konfiguration hängt vom verwendeten Editor ab. Die eigentlichen Regeln sollten trotzdem in der pyproject.toml liegen, damit Editor, Kommandozeile und CI dieselben Einstellungen verwenden.

Ruff mit pre-commit

Mit pre-commit kannst Du Ruff automatisch vor jedem Git-Commit ausführen.

Lege im Projekt eine Datei namens .pre-commit-config.yaml an:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.20
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

Die angegebene Ruff-Version ist ein Beispiel und sollte regelmäßig aktualisiert werden.

Aktiviere die Hooks:

pre-commit install

Teste sie anschließend für alle vorhandenen Dateien:

pre-commit run --all-files

Die Reihenfolge der Hooks ist wichtig:

  1. ruff-check korrigiert Lint-Verstöße und sortiert gegebenenfalls Imports.
  2. ruff-format formatiert das danach entstandene Ergebnis.

Verändert ein Hook Dateien, wird der Commit zunächst abgebrochen. Du kannst die Änderungen prüfen, erneut mit git add aufnehmen und danach noch einmal committen.

Ruff in der CI

In einer CI-Pipeline sollte Ruff nur prüfen und keine Dateien verändern:

ruff check .
ruff format --check .

Mit uv:

uv run ruff check .
uv run ruff format --check .

Findet der Linter einen Verstoß oder wäre eine Datei neu zu formatieren, beendet sich Ruff mit einem Fehlerstatus. Die Pipeline schlägt dadurch fehl.

Das verhindert, dass unformatierter oder gegen die vereinbarten Regeln verstoßender Code in den Hauptbranch gelangt.

Ruff in ein bestehendes Projekt einführen

Bei einem älteren Projekt solltest Du Ruff schrittweise einführen.

1. Aktuellen Stand committen

Bevor automatische Fixes oder Formatierungen über viele Dateien laufen, sollte das Git Working Tree sauber sein.

git status

So kannst Du alle Änderungen anschließend eindeutig Ruff zuordnen.

2. Mit wenigen Regeln beginnen

Beginne beispielsweise mit:

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]

Führe dann aus:

ruff check .

3. Sichere Fixes anwenden

ruff check --fix .

Prüfe anschließend die Änderungen:

git diff

4. Separat formatieren

ruff format .

Eine projektweite Neuformatierung erzeugt häufig einen großen Diff. Deshalb sollte sie möglichst in einem eigenen Commit erfolgen.

So bleiben spätere fachliche Änderungen leichter nachvollziehbar.

5. Weitere Rule Families ergänzen

Aktiviere danach beispielsweise nacheinander:

"I"
"UP"
"B"
"SIM"

Behebe oder begründe die neu gefundenen Verstöße, bevor Du die nächste Gruppe aktivierst.

6. Alte Tools erst danach entfernen

Entferne Black, Flake8, isort oder andere Tools erst, wenn Ruff mit der gewünschten Konfiguration dieselben Aufgaben im Projekt tatsächlich übernimmt.

Prüfe dabei auch:

  • Editor-Einstellungen
  • pre-commit Hooks
  • CI-Konfiguration
  • Development Dependencies
  • Konfigurationsdateien
  • Dokumentation für andere Entwickler

Ruff-Versionen festlegen

Ruff verwendet vor Version 1.0 ein eigenes Versionierungsschema. Eine neue Minor-Version kann auch Änderungen enthalten, die neue Lint-Meldungen oder ein verändertes stabiles Format erzeugen.

Für reproduzierbare Ergebnisse sollte ein Projekt die verwendete Ruff-Version deshalb über einen Lockfile, eine genaue Dependency oder den rev-Wert von pre-commit festlegen.

Mit uv wird die aufgelöste Version im Lockfile gespeichert.

Bei einer klassischen Requirements-Datei wäre beispielsweise eine genaue Version möglich:

ruff==0.15.20

Die konkrete Version solltest Du passend zum Projekt wählen und kontrolliert aktualisieren.

Der Ruff-Cache

Ruff speichert Analyseergebnisse standardmäßig in:

.ruff_cache/

Das Verzeichnis sollte normalerweise nicht ins Git-Repository aufgenommen werden.

Ergänze deshalb Deine .gitignore:

.ruff_cache/

Der Cache kann jederzeit gelöscht werden. Ruff erstellt ihn bei Bedarf neu.

Was Ruff nicht übernimmt

Ruff deckt viele Aufgaben ab, ersetzt aber nicht jedes Werkzeug eines Python-Projekts.

Kein vollständiger Type Checker

Ruff erkennt einige Probleme rund um Type Annotations, führt aber kein vollständiges statisches Type Checking durch.

Dafür gibt es eigenständige Tools wie:

  • ty
  • mypy
  • Pyright

Astral entwickelt mit ty einen separaten Type Checker und Language Server. Ruff und ty bleiben unterschiedliche Werkzeuge.

Keine Tests

Ruff prüft Code statisch. Es führt nicht automatisch Deine Programmlogik aus und ersetzt keine Tests mit pytest oder unittest.

Ein Programm kann vollständig Ruff-konform sein und trotzdem fachlich falsche Ergebnisse liefern.

Keine Garantie für fehlerfreien Code

Ein Linter erkennt nur die Muster, für die Regeln vorhanden und aktiviert sind.

Dieser Code ist syntaktisch sauber und wird vom Formatter korrekt formatiert:

def addiere(a, b):
    return a - b

Die Funktion subtrahiert trotzdem, obwohl ihr Name eine Addition verspricht.

Diesen Fehler kann nur ein Test oder eine fachliche Prüfung zuverlässig erkennen.

Stolperfallen

  • ruff check und ruff format verwechseln: Der Linter und der Formatter sind getrennte Kommandos. Für einen vollständigen Workflow benötigst Du beide.

  • Import-Sortierung vom Formatter erwarten: ruff format sortiert keine Imports. Aktiviere dafür die Rule Family I und führe ruff check --fix aus.

  • Black und Ruff abwechselnd verwenden: Beide Formatter sind weitgehend kompatibel, aber nicht identisch. Verwende innerhalb eines Projekts nur einen davon.

  • ALL ungeprüft aktivieren: Dadurch können sehr viele oder untereinander alternative Regeln aktiv werden. Beginne mit einer kleineren Auswahl.

  • --unsafe-fixes unbesehen verwenden: Diese Fixes dürfen das Laufzeitverhalten ändern oder Kommentare entfernen.

  • --fix mit Formatierung verwechseln: Lint-Fixes ersetzen nicht den Formatter. Nach ruff check --fix sollte weiterhin ruff format laufen.

  • Eine falsche target-version setzen: Ruff kann sonst Syntax vorschlagen, die auf der tatsächlich unterstützten Python-Version nicht läuft.

  • line-length als harte Garantie verstehen: Der Formatter kann nicht jede lange Zeile sinnvoll umbrechen.

  • Regeln ohne Begründung ignorieren: Ein breit eingesetztes # noqa versteckt möglicherweise auch zukünftige Probleme. Gib möglichst den konkreten Rule Code an.

  • Ruff nur global installieren: Andere Entwickler und die CI verwenden dann möglicherweise eine andere Version. Lege Ruff als Development Dependency des Projekts fest.

  • Eine große Neuformatierung mit fachlichen Änderungen mischen: Getrennte Commits machen Reviews und spätere Fehlersuche deutlich einfacher.

Ein sinnvoller Minimal-Workflow

Für ein neues Python-Projekt reichen zunächst diese vier Bestandteile.

pyproject.toml

[tool.ruff]
line-length = 88
target-version = "py312"

[tool.ruff.lint]
select = [
    "E4",
    "E7",
    "E9",
    "F",
    "I",
    "UP",
    "B",
    "SIM",
]

Lokal korrigieren und formatieren

ruff check --fix .
ruff format .

Vor dem Commit kontrollieren

ruff check .
ruff format --check .

In der CI prüfen

ruff check .
ruff format --check .

Damit verwenden lokale Entwicklung, Editor und CI dieselben Regeln und denselben Formatierungsstil.

Fazit

Ruff vereint einen schnellen Linter, einen Formatter und zahlreiche Neuimplementierungen bekannter Python-Tools in einer gemeinsamen CLI.

Die wichtigsten Kommandos sind:

ruff check .
ruff check --fix .
ruff format .
ruff format --check .

Der Linter findet Probleme, modernisiert Syntax und kann über die I-Regeln Imports sortieren. Der Formatter sorgt separat für einen einheitlichen Stil.

Seine eigentliche Stärke zeigt Ruff aber erst durch die gemeinsame Konfiguration in der pyproject.toml und die Integration in Editor, pre-commit und CI. Dadurch gelten überall dieselben Regeln, und Fragen zu Leerzeichen, Import-Reihenfolge oder Zeilenumbrüchen müssen nicht mehr in jedem Code Review neu diskutiert werden.

Weiterlesen

0 Kommentare

Noch keine Kommentare. Sei der/die Erste!