540 lines
13 KiB
Markdown
Executable File
540 lines
13 KiB
Markdown
Executable File
Ruff ist ein relativ neues, sehr schnelles Tool zur Verbesserung der Code-Qualität in Python. Es kombiniert Funktionen, für die man früher mehrere Werkzeuge brauchte (Linter, Formatter, Import-Sortierer, teilweise Sicherheits-Checks), in einem Programm.
|
||
|
||
Ich gehe Schritt für Schritt durch:
|
||
|
||
1. Grundidee und Zielgruppe
|
||
2. Was macht ruff konkret?
|
||
3. Abgrenzung zu ähnlichen Tools
|
||
4. Welche Probleme löst ruff?
|
||
5. Typische Herausforderungen im Einsatz
|
||
6. Praxisnahe Beispiele (inkl. Konfiguration)
|
||
|
||
---
|
||
|
||
## 1. Grundidee von ruff – in einfachen Worten
|
||
|
||
Stell dir vor, mehrere Entwickler:innen arbeiten an einem Python-Projekt. Ohne Regeln sieht der Code schnell sehr unterschiedlich aus:
|
||
|
||
- Mal sind Leerzeichen anders gesetzt,
|
||
- mal werden nicht genutzte Variablen liegen gelassen,
|
||
- vielleicht wird an manchen Stellen unsicherer Code geschrieben,
|
||
- im schlimmsten Fall fallen bestimmte Fehler erst spät auf.
|
||
|
||
Bisher nutzte man typischerweise mehrere Tools:
|
||
|
||
- ein **Linter** (z. B. *flake8* oder *pylint*),
|
||
- einen **Formatter** (z. B. *black*),
|
||
- einen **Import-Sortierer** (z. B. *isort*),
|
||
- evtl. ein **Security-Tool** (z. B. *bandit*),
|
||
- plus diverse Plugins.
|
||
|
||
**Ruff will das bündeln:**
|
||
|
||
- Ein Tool für viele Code-Qualitätsaspekte
|
||
- Sehr schnell (geschrieben in Rust, nicht in Python)
|
||
- Einfache Konfiguration (alles typischerweise in `pyproject.toml`)
|
||
- Viele Checks, die sich an bekannten Tools orientieren (flake8-Plugins, isort-Regeln, bandit-Regeln usw.)
|
||
|
||
---
|
||
|
||
## 2. Was macht ruff konkret?
|
||
|
||
### 2.1. Linting (Fehler und Stilprobleme finden)
|
||
|
||
Ruff untersucht deinen Python-Code und meldet z. B.:
|
||
|
||
- Syntax-Fehler
|
||
- Ungenutzte Variablen oder Importe
|
||
- Logische Stolperfallen (z. B. `== None` statt `is None`)
|
||
- Stil-Regeln (z. B. maximale Zeilenlänge)
|
||
- Potenziell unsichere Konstruktionen
|
||
|
||
Beispiel:
|
||
|
||
```python
|
||
def calculate(a, b):
|
||
result = a + b
|
||
unused = 42
|
||
if a == None:
|
||
print("a is None")
|
||
return result
|
||
```
|
||
|
||
Ruff würde in etwa melden (vereinfacht):
|
||
|
||
- `unused` wird nie verwendet
|
||
- `a == None` – besser `a is None`
|
||
|
||
Viele dieser Probleme kann ruff auch **automatisch beheben** (`--fix`).
|
||
|
||
---
|
||
|
||
### 2.2. Formatierung (Code automatisch „schön“ machen)
|
||
|
||
Ruff hat inzwischen einen **eigenen Formatter**, der ähnlich wie *black* funktioniert:
|
||
|
||
- Einheitliche Einrückungen und Zeilenumbrüche
|
||
- Klammer-Formatierung
|
||
- Konsistente Verwendung von Anführungszeichen (je nach Einstellung)
|
||
- Entfernung überflüssiger Leerzeilen usw.
|
||
|
||
Beispiel (unformatierter Code):
|
||
|
||
```python
|
||
def foo( x:int,y:int )->int:
|
||
return x+y
|
||
```
|
||
|
||
Nach `ruff format` könnte das so aussehen:
|
||
|
||
```python
|
||
def foo(x: int, y: int) -> int:
|
||
return x + y
|
||
```
|
||
|
||
---
|
||
|
||
### 2.3. Imports sortieren und aufräumen
|
||
|
||
Ruff kann ähnlich wie *isort*:
|
||
|
||
- Importe sortieren (alphabetisch und nach Gruppen: Standardbibliothek, Drittanbieter, Projektcode)
|
||
- Unbenutzte Importe entfernen
|
||
|
||
Beispiel:
|
||
|
||
```python
|
||
import myproject.utils
|
||
import os
|
||
import sys
|
||
import requests
|
||
|
||
from math import sqrt
|
||
from math import ceil
|
||
```
|
||
|
||
Nach ruff (vereinfacht):
|
||
|
||
```python
|
||
import os
|
||
import sys
|
||
from math import ceil, sqrt
|
||
|
||
import requests
|
||
|
||
import myproject.utils
|
||
```
|
||
|
||
Und wenn `sys` gar nicht verwendet wird, kann ruff es auch entfernen.
|
||
|
||
---
|
||
|
||
### 2.4. Ein Tool – mehrere Regel-Sammlungen
|
||
|
||
Ruff bringt rule sets mit, die vielen bekannten Tools entsprechen, z. B.:
|
||
|
||
- **E/F/W**: Pycodestyle/Pyflakes-ähnlich (via flake8)
|
||
- **I**: isort-Regeln
|
||
- **N**: pep8-naming (Namenskonventionen)
|
||
- **S**: bandit (Sicherheitsregeln)
|
||
- **UP**: pyupgrade (veraltete Syntax, modernisieren)
|
||
- u. v. m.
|
||
|
||
Du kannst über die Konfiguration steuern, welche Regel-Gruppen du aktivierst oder deaktivierst.
|
||
|
||
---
|
||
|
||
## 3. Abgrenzung zu ähnlichen Tools
|
||
|
||
### 3.1. Ruff vs. flake8 (+ Plugins)
|
||
|
||
**flake8** ist ein Linter; für viele Extras braucht man Plugins:
|
||
|
||
- z. B. `flake8-bugbear`, `flake8-import-order`, `pep8-naming` usw.
|
||
|
||
**Ruff:**
|
||
|
||
- Bietet die Funktionalität vieler flake8-Plugins „eingebaut“.
|
||
- Ist deutlich **schneller** (insbesondere bei großen Projekten).
|
||
- Wird oft als Drop-in-Ersatz für flake8 verwendet.
|
||
|
||
Aber:
|
||
|
||
- flake8 ist schon lange etabliert; manche Teams haben stark angepasste flake8-Setups, die man nicht 1:1 nach ruff übertragen kann.
|
||
- Einige Spezial-Plugins existieren ggf. nur für flake8.
|
||
|
||
---
|
||
|
||
### 3.2. Ruff vs. pylint
|
||
|
||
**pylint**:
|
||
|
||
- Sehr umfangreicher Linter (viele komplexe Regeln, u. a. über Projektstruktur, OOP-Patterns usw.)
|
||
- Langsam im Vergleich zu ruff
|
||
- Detaillierte Reports und Scores
|
||
|
||
**Ruff**:
|
||
|
||
- Fokus auf **Geschwindigkeit** und auf Regeln, die sich gut automatisieren/auto-fixen lassen.
|
||
- Viele „klassische“ Lint-Regeln, Naming, Imports, Security-Basics, aber nicht alle tiefgehenden Analysen von pylint.
|
||
- Für komplexe Architektur-Regeln wird weiterhin oft pylint oder andere Tools genutzt.
|
||
|
||
---
|
||
|
||
### 3.3. Ruff vs. black
|
||
|
||
**black**:
|
||
|
||
- Reiner **Formatter** – macht nur Formatierung, keine Lint-Fehler (bis auf ganz wenige Ausnahmen).
|
||
- Sehr stabile, strikte Formatierung (Meinung: „The uncompromising code formatter“).
|
||
|
||
**Ruff**:
|
||
|
||
- Eigenständiger Formatter, der in vielen Projekten Black ersetzen kann.
|
||
- Zusätzlich: Linting, Import-Sortierung, Security-Regeln usw.
|
||
- Du kannst:
|
||
- nur ruff als Formatter nutzen, oder
|
||
- ruff als Linter + black als Formatter (dann `ruff format` nicht verwenden), wenn dein Team bereits stark auf black setzt.
|
||
|
||
---
|
||
|
||
### 3.4. Ruff vs. isort
|
||
|
||
**isort**:
|
||
|
||
- Spezialisiert auf das Sortieren von Imports.
|
||
|
||
**Ruff**:
|
||
|
||
- Hat eine integrierte Import-Sortierung (Regelgruppe `I`).
|
||
- Für die meisten Fälle reicht ruff völlig aus.
|
||
- Wenn ihr sehr spezielle Import-Sortierregeln braucht, ist isort manchmal noch flexibler, aber das wird immer weniger relevant.
|
||
|
||
---
|
||
|
||
### 3.5. Ruff vs. mypy/pyright (Typprüfung)
|
||
|
||
**mypy/pyright**:
|
||
|
||
- Statische Typprüfer: sie prüfen, ob die Typannotationen sinnvoll zusammenpassen.
|
||
- Finden z. B. Fehler wie: „Funktion gibt laut Typ `str` zurück, tatsächlich aber `int`“.
|
||
|
||
**Ruff**:
|
||
|
||
- Enthält Regeln, die mit Typannotationen arbeiten (z. B. Style, Safety), aber **kein vollwertiger Typprüfer**.
|
||
- Typfehler (im Sinne von mypy) sollten weiterhin mit mypy oder pyright geprüft werden.
|
||
|
||
---
|
||
|
||
### 3.6. Ruff vs. bandit (Security)
|
||
|
||
**bandit**:
|
||
|
||
- Spezialisiertes Security-Tool für Python.
|
||
|
||
**Ruff**:
|
||
|
||
- Hat viele bandit-Regeln integriert (Regelgruppe `S`).
|
||
- Deckt gängige Sicherheitsfallen ab (z. B. `eval` auf untrusted Input, hartkodierte Passwörter etc.).
|
||
- Für tiefgehende Security-Audits kann ein spezialisiertes Tool trotzdem sinnvoll sein.
|
||
|
||
---
|
||
|
||
## 4. Welche Probleme löst ruff?
|
||
|
||
### 4.1. Performance-Probleme in großen Projekten
|
||
|
||
Früher:
|
||
|
||
- flake8 + black + isort + bandit + mypy
|
||
→ viele Tools, mehrfaches Einlesen des Codes, CI dauert lange.
|
||
|
||
Mit ruff:
|
||
|
||
- Ein Tool übernimmt Linting, Formatierung, Importe, einen großen Teil der Security-Regeln.
|
||
- Deutlich weniger Laufzeit, besonders in CI-Pipelines oder bei großen Repositories.
|
||
|
||
---
|
||
|
||
### 4.2. Zu viele Tools, komplizierte Konfiguration
|
||
|
||
Problem:
|
||
|
||
- Unterschiedliche Konfigurationsdateien (`.flake8`, `pyproject.toml`, `setup.cfg`, `.isort.cfg`, `pyproject.toml` für black…)
|
||
- Mehr Aufwand beim Onboarding neuer Teammitglieder.
|
||
|
||
Ruff:
|
||
|
||
- Typischerweise alles in **einer** Konfiguration (`pyproject.toml`).
|
||
- Weniger bewegliche Teile, einfachere Wartung.
|
||
|
||
---
|
||
|
||
### 4.3. Inkonsequente Codequalität im Team
|
||
|
||
Ohne einheitliche Tools:
|
||
|
||
- Jede:r schreibt etwas anders.
|
||
- Diskussionen in Code-Reviews drehen sich um Stil statt Inhalte.
|
||
- Fehler (z. B. ungenutzte Variablen, potentielle Bugs) werden erst spät bemerkt.
|
||
|
||
Mit ruff:
|
||
|
||
- Gemeinsame, automatisierte Regeln.
|
||
- Automatische Fixes für vieles (z. B. im Editor oder Pre-Commit-Hooks).
|
||
- Code-Reviews können sich auf Architektur und Logik konzentrieren.
|
||
|
||
---
|
||
|
||
### 4.4. Technische Schulden reduzieren
|
||
|
||
In älteren Projekten:
|
||
|
||
- Viele kleine Stil- und Qualitätsprobleme haben sich angesammelt.
|
||
- Niemand möchte „alles mal eben aufräumen“, weil Tools zu langsam sind oder es zu viel ist.
|
||
|
||
Mit ruff:
|
||
|
||
- Durch die Geschwindigkeit kann man auch große Codebasen lintern.
|
||
- Schrittweise Verbesserung möglich: z. B. zunächst nur wichtige Regelgruppen aktivieren, später mehr.
|
||
|
||
---
|
||
|
||
## 5. Herausforderungen bei der Nutzung von ruff
|
||
|
||
### 5.1. Zu viele Meldungen am Anfang
|
||
|
||
Wenn du ruff das erste Mal auf ein älteres Projekt loslässt, bekommst du oft hunderte oder tausende Meldungen.
|
||
|
||
Strategie:
|
||
|
||
- Nur einen Teil der Regeln aktivieren (z. B. nur „kritische“ oder klar hilfreiche).
|
||
- Bestehende Verstöße einmalig ignorieren (per `--ignore` oder `--per-file-ignores`) und neue Verstöße blocken.
|
||
- Nach und nach alte Stellen aufräumen.
|
||
|
||
---
|
||
|
||
### 5.2. Regeln verstehen und anpassen
|
||
|
||
Ruff hat sehr viele Regeln. Nicht alle passen zu jedem Projekt.
|
||
|
||
- Manche Regeln sind sehr streng (z. B. bestimmte Naming- oder Docstring-Regeln).
|
||
- Du musst überlegen: Welche Regeln sind für unser Team sinnvoll?
|
||
|
||
Lösung:
|
||
|
||
- Regeln gezielt aktivieren/deaktivieren.
|
||
- Dokumentieren, warum bestimmte Regeln aktiv oder abgeschaltet sind.
|
||
|
||
---
|
||
|
||
### 5.3. Wechsel von bestehender Tool-Landschaft
|
||
|
||
Wenn ihr schon flake8, black, isort etc. nutzt:
|
||
|
||
- Müssen Einstellungen in ruff nachgebaut werden.
|
||
- Manche Teams werden black nicht sofort durch ruff format ersetzen wollen.
|
||
|
||
Ein pragmatischer Weg:
|
||
|
||
1. Zuerst ruff als **Linter** einführen (Formatierung bleibt bei black).
|
||
2. Wenn gewünscht, später ruff format testen und ggf. black ersetzen.
|
||
|
||
---
|
||
|
||
### 5.4. Auto-Fixes mit Vorsicht genießen
|
||
|
||
Ruff kann sehr viel automatisch reparieren:
|
||
|
||
- In der Regel gut, aber:
|
||
- Bei manchen Regeln sollte man prüfen, ob die Änderung wirklich die Absicht trifft.
|
||
- In kritischen Bereichen (z. B. Security-sensible Logik) ggf. Auto-Fixes nicht blind akzeptieren.
|
||
|
||
Empfehlung:
|
||
|
||
- Auto-Fixes lokal ausführen, dann diff anschauen.
|
||
- In CI eher nur prüfen, nicht fixen.
|
||
|
||
---
|
||
|
||
### 5.5. Editor-Integration
|
||
|
||
Ruff hat gute Unterstützung in vielen Editoren (VS Code, PyCharm, Neovim usw.), aber:
|
||
|
||
- Man muss oft ein Plugin oder eine Extension installieren.
|
||
- Manchmal überschneidet sich das mit vorhandenen Tools (z. B. Black-Extension vs. Ruff-Formatter); das muss sauber konfiguriert werden.
|
||
|
||
---
|
||
|
||
## 6. Praxisnahe Beispiele
|
||
|
||
### 6.1. Installation von ruff
|
||
|
||
Typisch über `pip`:
|
||
|
||
```bash
|
||
pip install ruff
|
||
```
|
||
|
||
Oder als dev-Abhängigkeit in `pyproject.toml` (Poetry/uv/pip-tools etc.).
|
||
|
||
---
|
||
|
||
### 6.2. Einfacher Aufruf
|
||
|
||
Linting:
|
||
|
||
```bash
|
||
ruff check .
|
||
```
|
||
|
||
Mit automatischen Fixes:
|
||
|
||
```bash
|
||
ruff check . --fix
|
||
```
|
||
|
||
Formatierung:
|
||
|
||
```bash
|
||
ruff format .
|
||
```
|
||
|
||
---
|
||
|
||
### 6.3. Minimale Konfiguration in `pyproject.toml`
|
||
|
||
```toml
|
||
[tool.ruff]
|
||
target-version = "py311" # welche Python-Version ihr nutzt
|
||
line-length = 88 # maximale Zeilenlänge
|
||
exclude = ["venv", ".venv", "build", "dist"]
|
||
|
||
[tool.ruff.lint]
|
||
select = ["E", "F", "I", "B", "UP"] # Regelgruppen: Errors, Flake, Imports, Bugbear, pyupgrade
|
||
ignore = ["E501"] # Beispiel: lange Zeilen ignoriere ich (wenn ich black nutze)
|
||
|
||
[tool.ruff.format]
|
||
# Beispiel: wenn ihr black-kompatible Einstellungen wollt
|
||
quote-style = "double"
|
||
indent-style = "space"
|
||
line-ending = "auto"
|
||
```
|
||
|
||
- `select` gibt an, welche Regelgruppen aktiv sind.
|
||
- `ignore` hebt einzelne Regelcodes auf (z. B. `E501` für Zeilenlänge).
|
||
|
||
---
|
||
|
||
### 6.4. Beispiel: Unbenutzte Variable und schlechtes `None`-Checking
|
||
|
||
Code:
|
||
|
||
```python
|
||
def process(items):
|
||
count = len(items)
|
||
unused = 0
|
||
|
||
if items == None:
|
||
print("No items")
|
||
return count
|
||
```
|
||
|
||
Ruff meldet u. a.:
|
||
|
||
- `F841` (unused variable `unused`)
|
||
- `E711` (Vergleich mit `None` mit `==` statt `is`)
|
||
|
||
Mit `ruff check . --fix` könnte daraus werden:
|
||
|
||
```python
|
||
def process(items):
|
||
count = len(items)
|
||
|
||
if items is None:
|
||
print("No items")
|
||
return count
|
||
```
|
||
|
||
---
|
||
|
||
### 6.5. Beispiel: Imports aufräumen
|
||
|
||
Code:
|
||
|
||
```python
|
||
import os
|
||
import sys
|
||
import json
|
||
import requests
|
||
|
||
def get_data(url):
|
||
r = requests.get(url)
|
||
return r.json()
|
||
```
|
||
|
||
Hier wird `os`, `sys` und `json` nicht genutzt.
|
||
|
||
Ruff meldet:
|
||
|
||
- `F401` (unused imports: os, sys, json)
|
||
|
||
Nach `ruff check . --fix`:
|
||
|
||
```python
|
||
import requests
|
||
|
||
|
||
def get_data(url):
|
||
r = requests.get(url)
|
||
return r.json()
|
||
```
|
||
|
||
---
|
||
|
||
### 6.6. Beispiel: Einsatz mit pre-commit
|
||
|
||
Viele Teams nutzen [pre-commit](https://pre-commit.com/), damit Checks automatisch vor jedem Git-Commit laufen.
|
||
|
||
`.pre-commit-config.yaml`:
|
||
|
||
```yaml
|
||
repos:
|
||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||
rev: v0.6.5 # Beispielversion, ggf. aktualisieren
|
||
hooks:
|
||
- id: ruff
|
||
args: [--fix]
|
||
- id: ruff-format
|
||
```
|
||
|
||
Damit werden:
|
||
|
||
- Linting + Auto-Fix vor jedem Commit ausgeführt,
|
||
- Formatierung automatisch angewendet.
|
||
|
||
---
|
||
|
||
## 7. Zusammenfassung
|
||
|
||
- **Ruff** ist ein modernes, sehr schnelles Tool zur Verbesserung der Code-Qualität in Python.
|
||
- Es kombiniert:
|
||
- Linting (Fehler und Stil),
|
||
- Formatierung,
|
||
- Import-Sortierung,
|
||
- viele Sicherheits- und Modernisierungsregeln.
|
||
- Es ersetzt in vielen Projekten:
|
||
- flake8 (+ viele Plugins),
|
||
- isort,
|
||
- teilweise black,
|
||
- teilweise bandit.
|
||
- Es löst Probleme wie:
|
||
- lange Tool-Laufzeiten,
|
||
- fragmentierte Konfiguration,
|
||
- inkonsistente Codequalität.
|
||
- Herausforderungen sind v. a.:
|
||
- Auswahl und Anpassung der Regeln,
|
||
- Umstieg von bestehenden Tools,
|
||
- Umgang mit großen Mengen an Anfangs-Warnungen.
|
||
|
||
Wenn du möchtest, kann ich dir im nächsten Schritt eine **konkrete Beispielkonfiguration für ein kleines oder ein großes Projekt** vorschlagen (inkl. welche Regelgruppen sinnvoll sind und welche man am Anfang lieber auslässt). |