From 5a89544a274b136c5b0364436bad3fd7156aa308 Mon Sep 17 00:00:00 2001 From: Axel Hocks Date: Fri, 7 Aug 2026 00:10:16 +0200 Subject: [PATCH] =?UTF-8?q?tools/check=5Fnfalle.py:=20Scan=20gegen=20die?= =?UTF-8?q?=20\n-Falle=20=E2=80=94=20Projekt=20ist=20sauber?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auftrag "ueberpruefe alle dateien auf \n-Falle". Die Falle: ueber ein Shell-Heredoc geschriebener Code macht aus einem gemeinten \n einen ECHTEN Zeilenumbruch mitten im String-Literal. Heute zweimal passiert (Telegram-Text in engine.py, Reminder-Eintrag in measurement_reminder.py). Der Scan prueft in beide Richtungen: A) kompiliert alle .py (161 Dateien) — sauber B) JS: unabgeschlossene String-Literale — app.js sauber C) UMGEKEHRT: literales \n als sichtbarer Text in HTML-title= und Markdown — alle sauber D) Signatur der beiden heutigen Faelle (Zeile endet offen, naechste besteht nur aus einem Quote) — keine ⚠ Fehlalarm im Scanner selbst gefunden und behoben: Quotes zu ZAEHLEN meldet '"' als offenes Literal (gueltiges JS mit einem Anfuehrungszeichen darin, real in app.js). Der Scan verfolgt jetzt zeichenweise den AKTIVEN Quote-Typ. Die betroffene app.js-Stelle trotzdem aufgeraeumt: schliessendes Anfuehrungszeichen jetzt als U+201C statt ueber eine Konkatenation mit '"'. Regel in CLAUDE.md ergaenzt: mehrzeilige Strings mit \n nicht per Heredoc schreiben, sondern ueber den Edit-Weg. Co-Authored-By: Claude Opus 5 --- tools/check_nfalle.py | 153 ++++++++++++++++++++++++++++++++++++++++++ web/app.js | 6 +- 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 tools/check_nfalle.py diff --git a/tools/check_nfalle.py b/tools/check_nfalle.py new file mode 100644 index 0000000..ce08b66 --- /dev/null +++ b/tools/check_nfalle.py @@ -0,0 +1,153 @@ +"""Sucht die \\n-Falle in allen Projektdateien — in BEIDE Richtungen. + +Die Falle: beim Schreiben ueber ein Shell-Heredoc wird aus einem gemeinten `\\n` +ein ECHTER Zeilenumbruch mitten im String-Literal. In Python bricht das den +Compile (zweimal heute passiert), in JS ebenso — aber nicht ueberall faellt es +sofort auf. + +Geprueft wird: + A) Python: kompiliert jede Datei. Ein zerrissenes Literal faellt hier auf. + B) JS: Zeilen mit ungerader Zahl UNMASKIERTER Anfuehrungszeichen ausserhalb + von Kommentaren = angefangenes, nicht geschlossenes String-Literal. + C) Umgekehrter Fall: literales `\\n` als TEXT dort, wo es niemand lesen will + (HTML-Attribute, Markdown-Fliesstext) — das entsteht, wenn ein `\\\\n` + zu viel escaped wurde. + D) Verdaechtige Zeilenpaare: eine Zeile endet mit oeffnendem Quote, die + naechste besteht nur aus einem Quote — die Signatur der beiden heutigen + Faelle. +""" +import pathlib +import py_compile +import re +import sys +import tempfile + +WURZEL = pathlib.Path(r"c:\Users\ah\Downloads\tradingrobot") +SKIP = {".git", "__pycache__", ".reminders", "node_modules", ".removed_backup"} +befunde = [] + + +def dateien(*endungen): + for p in WURZEL.rglob("*"): + if any(s in p.parts for s in SKIP): + continue + if p.is_file() and p.suffix in endungen: + yield p + + +# ── A) Python ──────────────────────────────────────────────────────────── +print("=" * 88) +print(" A) PYTHON — kompiliert jede Datei (ein zerrissenes Literal faellt hier auf)") +print("=" * 88) +npy = 0 +tmp = tempfile.mkdtemp() +for p in dateien(".py"): + npy += 1 + try: + py_compile.compile(str(p), cfile=f"{tmp}/x.pyc", doraise=True) + except py_compile.PyCompileError as e: + msg = str(e).strip().splitlines()[-1][:110] + befunde.append(f"PY {p.relative_to(WURZEL)}: {msg}") + print(f" FEHLER {p.relative_to(WURZEL)}") + print(f" {msg}") +print(f" {npy} Dateien geprueft.") + + +# ── B) JavaScript ──────────────────────────────────────────────────────── +def js_offene_strings(text: str): + """Zeilen mit angefangenem, nicht geschlossenem ' oder " (Template-Literale + duerfen mehrzeilig sein und werden uebersprungen).""" + treffer = [] + in_tmpl = False + for nr, z in enumerate(text.splitlines(), 1): + # Backticks zaehlen -> Template-Literal-Zustand fortschreiben + bt = len(re.findall(r"(? `${x.grund} ${pc(x.n)} %`).join(" · "); blkEl.textContent = `⛔ heute ${pc(bm.frei)} % ohne Block` + (top ? ` · ${top}` : ""); + // ⚠ Schliessendes Anführungszeichen als „ “ (U+201C), NICHT über eine + // Konkatenation mit '"' — die war valide, aber schwer lesbar und lässt + // jeden Quote-Zähler auflaufen (der \n-Scan hat sie als offenes Literal + // gemeldet). blkEl.title = "Welches Gate hält die Empfehlung gerade zurück — über den " + "heutigen Tag gezählt. Ohne diese Zeile war „warum gab es so wenige " - + "Empfehlungen?" + '"' + " nur per Datenbank-Abfrage zu beantworten."; + + "Empfehlungen?“ nur per Datenbank-Abfrage zu beantworten."; } else { blkEl.textContent = ""; }