#!/usr/bin/env python3 """Trace Imports — Find broken/missing imports in backend.""" from __future__ import annotations import ast, sys, json from pathlib import Path ROOT = Path(__file__).resolve().parent.parent.parent APP = ROOT / "app" def find_imports() -> list[dict]: imports = [] for py in APP.rglob("*.py"): try: content = py.read_text(); tree = ast.parse(content) except: continue rel = str(py.relative_to(ROOT)) for node in ast.walk(tree): if isinstance(node, ast.ImportFrom): if node.module and node.module.startswith("app."): for alias in node.names: imports.append({"file": rel, "module": node.module, "name": alias.name, "line": node.lineno}) return imports def check_imports_exist(imports: list[dict]) -> list[dict]: broken = [] for imp in imports: module_path = imp["module"].replace(".", "/") + ".py" full_path = ROOT / module_path if not full_path.exists(): # Try as package pkg_path = ROOT / module_path.replace(".py", "/__init__.py") if not pkg_path.exists(): broken.append({**imp, "issue": "Module not found"}) continue # Check if name exists in module try: content = full_path.read_text() if imp["name"] not in content and imp["name"] != "__init__": # Could be imported via __init__.py if not full_path.name == "__init__.py": broken.append({**imp, "issue": f"Name '{imp['name']}' not found in module"}) except: pass return broken def main(): print("=" * 70) print("TRACE IMPORTS — Broken/Missing Imports") print("=" * 70) imports = find_imports() broken = check_imports_exist(imports) print(f"\nTotal imports: {len(imports)}") print(f"Broken imports: {len(broken)}") if broken: print("\n--- Broken imports ---") for b in broken[:30]: print(f" {b['file']}:{b['line']} — {b['module']}.{b['name']} — {b['issue']}") results = {"total_imports": len(imports), "broken": broken} with open(ROOT / "scripts" / "test_suite" / "results_trace_imports.json", "w") as f: json.dump(results, f, indent=2, default=str) return len(broken) if __name__ == "__main__": sys.exit(main())