Pipeline: zwei neue Pruefstufen vor dem Commit (Typaritaet + JS-Render)
User: "baue alle und integriere in die pipeline vor dem commit". Beide Stufen
schliessen Luecken, die HEUTE real zugeschlagen haben.
A3) TYPPRUEFUNG (ty 0.0.73, Konfiguration in ty.toml)
Anlass: `paarweise(d)` wurde mit 1 von 2 Pflichtargumenten gerufen. py_compile
sah nichts (Syntax ok), ruff F821 auch nicht (der Name IST definiert), kein Test
deckte den Pfad ab - der Fehler schlug erst zur Laufzeit zu.
⚠ BEWUSST ENGES REGELSET: der volle ty-Lauf meldet 172 Befunde, davon 97
`unresolved-attribute` aus fehlenden MetaTrader5-Stubs. Eine Stufe mit
Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung (die Lehre vom
07.08.). Aktiv sind nur die Aritaets-Regeln. `call-non-callable` und
`not-iterable` sind AUS - beide melden hier ausschliesslich Falsch-Positive aus
heterogenen dicts (`c["have"]()`, `"up" in c`); geprueft, nicht angenommen.
⚠ Der dokumentierte Einwand "Pruefumgebung != Laufumgebung" greift NICHT: ein
Typchecker fuehrt nichts aus, er kann also nicht divergieren. Er braucht auch
kein MT5.
⚠ Nebenbefund: die CLAUDE.md-Begruendung gegen mypy ("kaum Annotationen
vorhanden") ist veraltet - gemessen sind 305 von 443 Funktionen annotiert (69 %).
B2) RENDER-PROBE (tools/render_check.mjs gegen tests/fixtures/snapshot.json)
Anlass: `node --check` prueft nur SYNTAX. Der ReferenceError von heute Abend
(RSI-Zeile griff in renderM15(s) auf d.market zu) war syntaktisch einwandfrei
und brach im Browser den GESAMTEN Render ab - sechs leere Zeilen und eine leere
Breakout-Karte. Fuer Python gibt es ruff F821, fuer JS gab es nichts.
⚠ Laeuft gegen einen GESPEICHERTEN Snapshot, nicht gegen den laufenden Server -
der Hook darf nicht davon abhaengen, ob gerade ein Server laeuft.
⚠ KEIN ESLint: das braeuchte npm und node_modules, gegen die dokumentierte
Entscheidung ("kein npm, keine package.json").
BEIDE RICHTUNGEN VERIFIZIERT, je Stufe:
A3 Aritaetsfehler eingebaut -> "FEHLER too-many-positional-arguments",
1 Befund; zurueck -> gruen
B2 d.market zurueckgebaut -> "❌ EXCEPTION: d is not defined",
1 Befund; zurueck -> gruen
Beide haengen an tools/check_nfalle.py und laufen damit im pre-commit-Hook mit.
Fehlt das Werkzeug (ty nicht installiert, node nicht im PATH, Fixture fehlt),
wird die Stufe uebersprungen statt zu blockieren - eine frische Maschine darf am
Hook nicht haengenbleiben. Ein WERKZEUGFEHLER (Exit != 0/1) gilt dagegen
ausdruecklich NICHT als sauber.
ty in requirements-dev.txt gepinnt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
95b22e2add
commit
0fe51dea48
@@ -19,3 +19,5 @@ numpy==2.2.6
|
|||||||
scipy==1.18.0 # `backtest_cost_gate.ci95` (Bootstrap-Konfidenzintervalle)
|
scipy==1.18.0 # `backtest_cost_gate.ci95` (Bootstrap-Konfidenzintervalle)
|
||||||
scikit-learn==1.9.0 # bislang ungenutzt; Kandidat fuer P(break)-Nachbau
|
scikit-learn==1.9.0 # bislang ungenutzt; Kandidat fuer P(break)-Nachbau
|
||||||
pandas==3.0.2 # bislang ungenutzt
|
pandas==3.0.2 # bislang ungenutzt
|
||||||
|
# Typpruefung (Aritaet) — Stufe A3 in tools/check_nfalle.py, seit 2026-08-19
|
||||||
|
ty==0.0.73
|
||||||
|
|||||||
Vendored
+1
File diff suppressed because one or more lines are too long
@@ -81,6 +81,91 @@ print(f" {npy} Dateien geprueft.")
|
|||||||
SCHWER = ("undefined name", "undefined local", "syntax error")
|
SCHWER = ("undefined name", "undefined local", "syntax error")
|
||||||
print()
|
print()
|
||||||
print("=" * 88)
|
print("=" * 88)
|
||||||
|
# ── A3) TYPPRUEFUNG: falsche Aufrufe ─────────────────────────────────────
|
||||||
|
# ⚠⚠ WARUM ES DAS BRAUCHT (2026-08-19): `paarweise(d)` wurde mit 1 von 2
|
||||||
|
# Pflichtargumenten gerufen. py_compile sah nichts (Syntax ok), ruff F821 auch
|
||||||
|
# nicht (der Name IST definiert), und kein Test deckte den Pfad ab. Der Fehler
|
||||||
|
# schlug erst zur Laufzeit zu. Genau diese Luecke schliesst `ty`.
|
||||||
|
# ⚠ BEWUSST ENGES REGELSET (`ty.toml`): der volle Lauf meldet 172 Befunde, davon
|
||||||
|
# 97 `unresolved-attribute` aus fehlenden MetaTrader5-Stubs. Eine Stufe mit
|
||||||
|
# Dauer-Treffern wird ignoriert, und mit ihr die eine echte Meldung. Aktiv
|
||||||
|
# sind nur die Aritaets-Regeln; `call-non-callable`/`not-iterable` sind AUS,
|
||||||
|
# weil sie hier nur Falsch-Positive aus heterogenen dicts liefern (geprueft).
|
||||||
|
# ⚠ EXIT-CODES wie bei ruff: 0 sauber · 1 Befunde · alles andere = Werkzeugfehler
|
||||||
|
# und darf NIEMALS als "sauber" durchgehen (die Lehre vom 07.08.).
|
||||||
|
def _stufe_typen():
|
||||||
|
print("")
|
||||||
|
print("=" * 88)
|
||||||
|
print(" A3) TYPPRUEFUNG — falsche Aufrufe (Aritaet)")
|
||||||
|
print("=" * 88)
|
||||||
|
cfg = WURZEL / "ty.toml"
|
||||||
|
if not cfg.exists():
|
||||||
|
print(" ty.toml fehlt — Stufe uebersprungen.")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
r = subprocess.run([sys.executable, "-m", "ty", "check",
|
||||||
|
"--config-file", str(cfg),
|
||||||
|
str(WURZEL / "core"), str(WURZEL / "server.py")],
|
||||||
|
capture_output=True, text=True, timeout=300)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(" `ty` nicht installiert — Stufe uebersprungen "
|
||||||
|
"(pip install ty). Eine frische Maschine darf hier nicht haengen.")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠ ty nicht ausfuehrbar ({e}) — Stufe uebersprungen.")
|
||||||
|
return
|
||||||
|
if r.returncode not in (0, 1):
|
||||||
|
print(f" ⚠ ty meldet einen WERKZEUGFEHLER (Exit {r.returncode}) — "
|
||||||
|
f"NICHT als sauber gewertet:")
|
||||||
|
for z in (r.stderr or r.stdout).strip().splitlines()[:3]:
|
||||||
|
print(f" {z}")
|
||||||
|
befunde.append("TY Werkzeugfehler")
|
||||||
|
return
|
||||||
|
treffer = [z for z in (r.stdout or "").splitlines() if z.startswith("error[")]
|
||||||
|
for z in treffer:
|
||||||
|
befunde.append(f"TY {z.strip()}")
|
||||||
|
print(f" FEHLER {z.strip()}")
|
||||||
|
if not treffer:
|
||||||
|
print(" keine falschen Aufrufe.")
|
||||||
|
|
||||||
|
|
||||||
|
# ── B2) RENDER-PROBE: JS-Laufzeitfehler im Dashboard ─────────────────────
|
||||||
|
# ⚠⚠ `node --check` prueft nur SYNTAX. Ein ReferenceError (falsche Variable im
|
||||||
|
# falschen Scope) ist syntaktisch einwandfrei und bricht im Browser den
|
||||||
|
# GESAMTEN Render ab — alles ab der Fehlerstelle bleibt auf "—" stehen.
|
||||||
|
# Real am 19.08.: die RSI-Zeile griff in `renderM15(s)` auf `d.market` zu;
|
||||||
|
# sechs leere Zeilen und eine komplett leere Breakout-Karte.
|
||||||
|
# ⚠ Laeuft gegen einen GESPEICHERTEN Snapshot (`tests/fixtures/snapshot.json`),
|
||||||
|
# NICHT gegen den laufenden Server — der Hook darf nicht davon abhaengen, ob
|
||||||
|
# gerade ein Server laeuft.
|
||||||
|
def _stufe_render():
|
||||||
|
print("")
|
||||||
|
print("=" * 88)
|
||||||
|
print(" B2) RENDER-PROBE — JS-Laufzeitfehler (was `node --check` nicht sieht)")
|
||||||
|
print("=" * 88)
|
||||||
|
werkzeug = WURZEL / "tools" / "render_check.mjs"
|
||||||
|
fixture = WURZEL / "tests" / "fixtures" / "snapshot.json"
|
||||||
|
if not werkzeug.exists() or not fixture.exists():
|
||||||
|
print(" Werkzeug oder Fixture fehlt — Stufe uebersprungen.")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["node", str(werkzeug), str(fixture)],
|
||||||
|
capture_output=True, text=True, timeout=120)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(" node nicht im PATH — Stufe uebersprungen.")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠ Render-Probe nicht ausfuehrbar ({e}) — uebersprungen.")
|
||||||
|
return
|
||||||
|
aus = (r.stdout or "") + (r.stderr or "")
|
||||||
|
if r.returncode != 0 or "EXCEPTION" in aus:
|
||||||
|
for z in aus.strip().splitlines()[:6]:
|
||||||
|
print(f" {z}")
|
||||||
|
befunde.append("RENDER " + aus.strip().splitlines()[0][:80])
|
||||||
|
else:
|
||||||
|
print(" alle Render-Funktionen laufen ohne Exception durch.")
|
||||||
|
|
||||||
|
|
||||||
print(" A2) LINTER — undefinierte Namen (was py_compile nicht sieht)")
|
print(" A2) LINTER — undefinierte Namen (was py_compile nicht sieht)")
|
||||||
print("=" * 88)
|
print("=" * 88)
|
||||||
# ⚠ RUFF statt pyflakes (2026-08-07, Web-Recherche zum 2026er-Standard): gleiche
|
# ⚠ RUFF statt pyflakes (2026-08-07, Web-Recherche zum 2026er-Standard): gleiche
|
||||||
@@ -335,6 +420,11 @@ print(" keine gefunden." if not sig else f" {sig} Stellen.")
|
|||||||
|
|
||||||
print()
|
print()
|
||||||
print("=" * 88)
|
print("=" * 88)
|
||||||
|
_stufe_typen()
|
||||||
|
_stufe_render()
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("=" * 88)
|
||||||
print(" ERGEBNIS: " + ("keine \\n-Falle gefunden" if not befunde
|
print(" ERGEBNIS: " + ("keine \\n-Falle gefunden" if not befunde
|
||||||
else f"{len(befunde)} Befund(e)"))
|
else f"{len(befunde)} Befund(e)"))
|
||||||
print("=" * 88)
|
print("=" * 88)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Typpruefung — BEWUSST ENGES Regelset (2026-08-19).
|
||||||
|
#
|
||||||
|
# ⚠⚠ Der volle Lauf meldet 172 Befunde, davon 97 `unresolved-attribute` aus
|
||||||
|
# fehlenden MetaTrader5-Stubs. Eine Stufe mit Dauer-Treffern wird ignoriert,
|
||||||
|
# und mit ihr die eine echte Meldung — das ist im Projekt dokumentiert
|
||||||
|
# (ruff-Schweregrad-Trennung, 07.08.). Deshalb blockieren nur die Regeln,
|
||||||
|
# die ECHTE Aufruffehler finden und hier nachweislich rauschfrei sind.
|
||||||
|
#
|
||||||
|
# Anlass ist ein realer Fehler vom 19.08.: `paarweise(d)` mit 1 von 2
|
||||||
|
# Pflichtargumenten. py_compile, ruff F821 und pytest liessen ihn durch; er
|
||||||
|
# schlug erst zur Laufzeit zu.
|
||||||
|
[src]
|
||||||
|
exclude = ["backtest_*.py", "analyze_*.py", "tests/**"]
|
||||||
|
|
||||||
|
[rules]
|
||||||
|
# ── AKTIV: echte Aufruffehler ────────────────────────────────────────────
|
||||||
|
missing-argument = "error"
|
||||||
|
too-many-positional-arguments = "error"
|
||||||
|
unknown-argument = "error"
|
||||||
|
# ⚠ call-non-callable / not-iterable BEWUSST AUS: beide melden hier nur
|
||||||
|
# Falsch-Positive aus heterogenen dicts (`c["have"]()` in den CHECKS,
|
||||||
|
# `"up" in c` in news.py). Geprueft, nicht angenommen.
|
||||||
|
call-non-callable = "ignore"
|
||||||
|
not-iterable = "ignore"
|
||||||
|
unresolved-import = "ignore"
|
||||||
|
# ── AUS: braucht vollstaendige Stubs/Annotationen, sonst Dauer-Rauschen ──
|
||||||
|
unresolved-attribute = "ignore"
|
||||||
|
unsupported-operator = "ignore"
|
||||||
|
invalid-argument-type = "ignore"
|
||||||
|
no-matching-overload = "ignore"
|
||||||
|
not-subscriptable = "ignore"
|
||||||
|
possibly-missing-attribute = "ignore"
|
||||||
|
invalid-assignment = "ignore"
|
||||||
|
invalid-return-type = "ignore"
|
||||||
Reference in New Issue
Block a user