"""Godot 4 parser audit. Run from any directory with Python 3.""" from pathlib import Path import concurrent.futures, json, re, subprocess ROOT = Path(__file__).resolve().parents[1] ENGINE = ROOT / "tools/godot/Godot_v4.5-stable_win64_console.exe" FILES = sorted(p for p in ROOT.rglob("*.gd") if not set(p.relative_to(ROOT).parts) & {".godot", ".git", "tools", "dist"}) def check(path): rel = path.relative_to(ROOT).as_posix() result = subprocess.run([str(ENGINE), "--headless", "--path", str(ROOT), "--script", rel, "--check-only", "--log-file", str(ROOT / "logs" / ("audit-" + rel.replace("/", "_") + ".log"))], cwd=ROOT, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=45) output = result.stdout + result.stderr source = path.read_text(encoding="utf-8-sig") untyped = [i for i,line in enumerate(source.splitlines(),1) if re.search(r"\bvar\s+\w+\s*(?:=|$)",line.split("#",1)[0])] errors = [line for line in output.splitlines() if "SCRIPT ERROR" in line or "Parse Error" in line or "Compile Error" in line] return {"file":rel,"exit":result.returncode,"errors":errors,"untyped_declarations":untyped,"explicit_variant_count":len(re.findall(r"\bvar\s+\w+\s*:\s*Variant",source))} with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: results = list(pool.map(check, FILES)) report = {"engine":"Godot 4.5", "files":len(results), "passed":sum(x["exit"]==0 and not x["errors"] and not x["untyped_declarations"] for x in results),"results":results} (ROOT / "logs/gdscript-audit.json").write_text(json.dumps(report,indent=2),encoding="utf-8") print("AUDIT",report["passed"],"/",report["files"]) for item in results: if item["exit"] or item["errors"] or item["untyped_declarations"]: print(json.dumps(item)) raise SystemExit(0 if report["passed"] == report["files"] else 1)