diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md index acdac1b..5b742b3 100644 --- a/CODING_STANDARDS.md +++ b/CODING_STANDARDS.md @@ -191,22 +191,20 @@ def get_game_by_id(game_id): | Regel | Richtig | Falsch | |---|---|---| -| `Optional` statt `X \| None` (Konsistenz) | `Optional[str]` | `str \| None` | +| `X \| None` (PEP 604, idiomatisch für 3.13+) | `str \| None` | `Optional[str]` | | `list`/`dict` statt `List`/`Dict` (Python 3.9+) | `list[str]` | `List[str]` | -| `Any` nur in Ausnahmefällen | `dict[str, Any]` (API-Response) | `Any` als Rückgabetyp | +| Generics immer parametrisieren | `dict[str, Any]` | `dict` (implizites `dict[Any, Any]`) | | `-> None` bei Funktionen ohne Return | `def log(msg: str) -> None:` | `def log(msg: str):` | ```python -from typing import Optional, Any - # ✅ Richtig -def find_user(email: str) -> Optional[dict[str, Any]]: +def find_user(email: str) -> dict[str, object] | None: """Sucht einen User per E-Mail. Gibt None zurück, wenn nicht gefunden.""" ... # ✅ Richtig — Flask-Route @app.route("/api/games/") -def get_game(game_id: int) -> tuple[dict, int]: +def get_game(game_id: int) -> tuple[dict[str, object], int]: """Gibt ein einzelnes Spiel zurück.""" ... ``` @@ -269,8 +267,8 @@ def berechne_durchschnitt(werteliste: list[float]) -> float: def komplexe_funktion( name: str, werte: list[int], - optional: Optional[str] = None, -) -> dict[str, Any]: + optional: str | None = None, +) -> dict[str, object]: """Kurzbeschreibung (eine Zeile). Ausführliche Beschreibung. Kann mehrere Sätze umfassen. @@ -446,9 +444,9 @@ def search_games(): ### 8.1 Grundregeln -1. **Jede neue Funktion → Test** (TDD: erst Test, dann Code) -2. **80% Code Coverage** als Minimum -3. **pytest** als Test-Framework +1. **Jede neue Funktion → Test** (Empfehlung: TDD — erst Test, dann Code) +2. **80% Code Coverage** als harter Floor — unterschreiten blockiert den Commit +3. **pytest** als einziges Test-Framework 4. **Tests in `tests/`**, spiegelt die Projektstruktur ### 8.2 Test-Struktur @@ -584,7 +582,7 @@ Jeder Commit folgt dem **Conventional Commits**-Standard: feat(games): Spiele-Suche mit Volltext-Filter fix(auth): Login bricht bei leeren Feldern nicht mehr ab docs: CODING_STANDARDS.md hinzugefügt -chore(deps): Ruff auf 0.11.3 aktualisiert +chore(deps): Ruff auf 0.15.18 aktualisiert ``` ### 10.2 Commit-Frequenz @@ -608,17 +606,16 @@ pytest # Tests müssen grün sein | Tool | Zweck | Konfiguration | |---|---|---| -| **[Ruff](https://docs.astral.sh/ruff/)** | Linting + Formatierung | `pyproject.toml` → `[tool.ruff]` | +| **[Ruff](https://docs.astral.sh/ruff/)** | Linting + Formatierung + Sicherheit | `pyproject.toml` → `[tool.ruff]` | | **[mypy](https://mypy-lang.org/)** | Statische Typ-Prüfung | `pyproject.toml` → `[tool.mypy]` | | **[pre-commit](https://pre-commit.com/)** | Git-Hooks zur Durchsetzung | `.pre-commit-config.yaml` | -| **[pytest](https://pytest.org/)** | Testing | `pyproject.toml` → `[tool.pytest.ini_options]` | -| **[Bandit](https://bandit.readthedocs.io/)** | Sicherheits-Scans | `pyproject.toml` → `[tool.bandit]` | +| **[pytest](https://pytest.org/)** | Testing + Coverage | `pyproject.toml` → `[tool.pytest.ini_options]` | ### 11.2 Quick-Start (neues Projekt) ```bash # 1. Tools installieren -pip install ruff mypy pre-commit pytest bandit +pip install ruff mypy pre-commit pytest # 2. Pre-commit-Hooks aktivieren pre-commit install @@ -627,26 +624,24 @@ pre-commit install ruff check . --fix ruff format . mypy . -pytest +pytest --cov --cov-fail-under=80 -# 4. Vor jedem Commit automatisch +# 4. Vor jedem Commit automatisch (Ruff Lint + Format + Basic Checks) git commit -m "feat: ..." # → Ruff prüft + formatiert automatisch -# → mypy prüft die Typen -# → Bandit scannt auf Sicherheitslücken +# → Merge-Konflikte, Whitespace, Debug-Statements werden geprüft +# → mypy läuft nicht in pre-commit (isoliertes venv-Drift) — separat ausführen! ``` ### 11.3 CI-Setup (später) ```yaml # .github/workflows/quality.yml -- name: Lint +- name: Lint + Security run: ruff check . -- name: Type Check +- name: Type Check (im echten venv!) run: mypy . -- name: Security Scan - run: bandit -r src/ -c pyproject.toml -- name: Tests +- name: Tests + Coverage run: pytest --cov --cov-fail-under=80 ``` @@ -654,14 +649,17 @@ git commit -m "feat: ..." ## Anhang A: Checkliste für Code Reviews -- [ ] PEP 8 eingehalten? (Ruff-Check bestanden) +Bei jedem Code-Review durchgehen. 🤖 = automatisch von pre-commit erzwungen. + +- [ ] 🤖 PEP 8 eingehalten? (Ruff-Check bestanden) - [ ] Alle Funktionen haben Typ-Annotationen? (mypy bestanden) - [ ] Docstrings auf Deutsch für alle öffentlichen Funktionen? - [ ] Keine Secrets im Code? - [ ] User-Input validiert? -- [ ] Tests geschrieben und grün? -- [ ] Keine God-Files (>500 Zeilen)? -- [ ] Keine God-Funktionen (>50 Zeilen)? +- [ ] Tests geschrieben und grün? Coverage ≥ 80%? +- [ ] Keine God-Files (>500 Zeilen)? (manuell) +- [ ] Keine God-Funktionen (>50 Statements)? (🤖 Ruff PLR0915) +- [ ] McCabe-Komplexität ≤ 10? (🤖 Ruff C901) - [ ] Route enthält keine Business-Logik? - [ ] Commit folgt Conventional Commits? @@ -683,5 +681,13 @@ Jede Regel ist **automatisiert durchsetzbar**. Das bedeutet: Du musst sie nicht --- -> **Letzte Aktualisierung:** 21. Juni 2026 -> **Nächste Überprüfung:** Bei Projektstart oder nach 6 Monaten +> **Letzte Aktualisierung:** 21. Juni 2026 (extern reviewed ✅ — 6 Fixes eingearbeitet) +> **Nächste Überprüfung:** Bei Projektstart oder Dezember 2026 +> +> **Review-Fixes v1.0:** +> 1. Optional[X] → X | None (PEP 604, kein Ruff-Konflikt mehr) +> 2. B101 global entfernt — asserts nur in tests/ erlaubt +> 3. Bandit-Hook gestrichen — Ruffs S-Regeln reichen +> 4. C901 + PLR-Regeln aktiviert — God-Funktionen jetzt automatisch geprüft +> 5. mypy aus pre-commit entfernt (isoliertes venv-Drift) +> 6. TDD → Empfehlung, Typ-Fixes, Ruff 0.15.18, mypy 2.1.0