Files
work/notes/nextcloud/Notes/IT-Know-How/python/ruff.md
T

540 lines
13 KiB
Markdown
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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).