Nach Web-Recherche zum 2026er-Python-Standard umgesetzt, aber gefiltert: von
uv/Ruff/pyproject/CI passt hier nur, was zu einer Einzelmaschine mit
GUI-gebundenem MT5 und Live-Geld passt.
(1) requirements.txt (Laufzeit) + requirements-dev.txt (Werkzeuge/Messung),
Versionen gepinnt. Bis dahin gab es GAR KEINE Liste — nach einem
Maschinenausfall waere die Rekonstruktion Raten gewesen. Per AST ueber
core/, server.py, tools/ ermittelt statt geraten.
(2) tools/pre_commit.py faehrt vor jedem Commit check_nfalle + pytest (~5 s).
Einrichten: python tools/pre_commit.py --install. Notausgang
'git commit --no-verify' ist Absicht.
Bewusst OHNE das pre-commit-Framework: dessen Nutzen ist
Werkzeug-Versionierung ueber mehrere Maschinen. Hier gibt es eine, und es
wuerde eine ZWEITE ruff-Version in einer isolierten Umgebung installieren —
genau die Divergenz 'Pruefumgebung != Laufumgebung', die heute schon einmal
zugeschlagen hat.
(3) Ruff statt pyflakes (--select F821), ganzer Scan in 2,1 s. KEIN
ruff format — das wuerde 161 Dateien umformatieren, darunter alle
Backtests, und die Migrations-Regel verletzen.
⚠ VIERTER Fall der Klasse 'Erfolg melden, wo nichts geprueft wurde', und nur
durch den Test aufgefallen: --select F821,E999 laesst ruff 0.16 komplett
abbrechen (E999 wurde entfernt, Exit 2); die Meldung geht nach stderr, und weil
ich den Rueckgabecode nicht pruefte, meldete die Stufe 'keine undefinierten
Namen' — ohne irgendetwas geprueft zu haben. Ein absichtlich eingebauter
F821-Fehler kam glatt durch den Hook. Behoben: nur F821, und Exit 2 loest jetzt
den pyflakes-Rueckfall aus.
Beide Richtungen verifiziert: mit Fehler stoppt der Hook den Commit, ohne ist
er gruen. Der Test-Commit wurde zurueckgenommen (war nur lokal, ahead 1).
Nicht umgesetzt: Gitea Actions (Runner auf derselben Maschine wie der Live-Bot,
kein Mehrwert bei 5 s Pruefzeit), Container/Blue-Green (MT5 braucht eine
Desktop-Sitzung, EINE Broker-Verbindung), mypy/ty (kaum Annotationen).
Struktureller Fund: core/engine.py importiert backtest_breakout_squeeze — ein
Backtest ist Teil des LIVE-Abhaengigkeitsgraphen.
90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
||
"""Pre-Commit-Pruefung: faehrt die Stufe-1-Checks automatisch vor jedem Commit.
|
||
|
||
⚠ WOZU. Am 2026-08-07 habe ich `py_compile`, `pytest` und `check_nfalle` rund
|
||
fuenfzehnmal von Hand gestartet — und trotzdem gingen zwei Fehler durch, weil
|
||
ich sie im falschen Moment aufrief: die `\\n`-Falle fiel erst beim naechsten
|
||
Aufruf auf, die beiden `log.`-Fehler in `core/trader.py` lagen MONATE drin
|
||
(einer seit dem Initial-Commit). Ein Hook vergisst das nicht.
|
||
|
||
WAS GEPRUEFT WIRD (~3–4 s):
|
||
1. `tools/check_nfalle.py` — kompiliert alle .py, ruff (F821/E999),
|
||
JS-String-Literale, die \\n-Falle in beide Richtungen, .ps1-BOM
|
||
2. `pytest` — die Szenario-Suite
|
||
|
||
⚠ NOTAUSGANG: `git commit --no-verify` ueberspringt den Hook. Das ist Absicht —
|
||
eine Pruefung, die einen dringenden Fix blockiert, wird sonst dauerhaft
|
||
deinstalliert. Wer ihn nutzt, sollte die Pruefung danach nachholen.
|
||
|
||
⚠ BEWUSST OHNE das `pre-commit`-Framework: dessen Nutzen ist die Verwaltung von
|
||
Werkzeug-Versionen ueber mehrere Entwickler und Maschinen hinweg. Hier gibt es
|
||
eine Maschine und einen Entwickler; ein eigenes, versioniertes Skript ist
|
||
transparenter und braucht keine isolierten Zusatz-Umgebungen (die dann eine
|
||
ANDERE ruff-Version haetten als die Kommandozeile — genau die Art Divergenz,
|
||
die dieses Projekt schon oft genug hatte).
|
||
|
||
Aufruf:
|
||
python tools/pre_commit.py # Pruefung von Hand
|
||
python tools/pre_commit.py --install # als Git-Hook einrichten
|
||
"""
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
from pathlib import Path
|
||
|
||
WURZEL = Path(__file__).resolve().parents[1]
|
||
PY = sys.executable
|
||
|
||
|
||
def lauf(titel: str, argv: list[str]) -> bool:
|
||
t0 = time.time()
|
||
r = subprocess.run(argv, cwd=WURZEL, capture_output=True, text=True)
|
||
dauer = time.time() - t0
|
||
if r.returncode == 0:
|
||
print(f" ✅ {titel:<28} {dauer:.1f}s")
|
||
return True
|
||
print(f" ❌ {titel:<28} {dauer:.1f}s")
|
||
# Nur die letzten Zeilen — der Hook soll die Ursache zeigen, nicht fluten.
|
||
for z in (r.stdout + r.stderr).strip().splitlines()[-25:]:
|
||
print(f" {z}")
|
||
return False
|
||
|
||
|
||
def installieren() -> int:
|
||
hooks = WURZEL / ".git" / "hooks"
|
||
if not hooks.is_dir():
|
||
print(" Kein .git/hooks-Verzeichnis gefunden."); return 1
|
||
ziel = hooks / "pre-commit"
|
||
# ⚠ Git fuer Windows fuehrt Hooks ueber `sh` aus, nicht ueber cmd — deshalb
|
||
# ein sh-Shim mit Vorwaertsschraegstrichen.
|
||
ziel.write_text(
|
||
"#!/bin/sh\n"
|
||
"# Erzeugt von tools/pre_commit.py --install\n"
|
||
f'exec "{Path(PY).as_posix()}" -X utf8 '
|
||
f'"{(WURZEL / "tools" / "pre_commit.py").as_posix()}"\n',
|
||
encoding="utf-8", newline="\n")
|
||
print(f" Hook installiert: {ziel}")
|
||
print(" Ueberspringen im Notfall: git commit --no-verify")
|
||
return 0
|
||
|
||
|
||
def main() -> int:
|
||
if "--install" in sys.argv:
|
||
return installieren()
|
||
|
||
print("── Pre-Commit ─────────────────────────────────────")
|
||
ok = True
|
||
ok &= lauf("Syntax · Linter · Scanner",
|
||
[PY, "-X", "utf8", str(WURZEL / "tools" / "check_nfalle.py")])
|
||
ok &= lauf("Szenario-Tests (pytest)", [PY, "-X", "utf8", "-m", "pytest", "-q"])
|
||
if ok:
|
||
print("── alles gruen ────────────────────────────────────")
|
||
return 0
|
||
print("── COMMIT GESTOPPT ────────────────────────────────")
|
||
print(" Beheben — oder bewusst uebergehen: git commit --no-verify")
|
||
return 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|