#!/usr/bin/env python3 """Trace Contracts — Find Contract attribute mismatches (like GraphRagContract).""" from __future__ import annotations import ast, re, sys, json from pathlib import Path ROOT = Path(__file__).resolve().parent.parent.parent APP = ROOT / "app" def find_contracts() -> list[dict]: contracts = [] 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.ClassDef): if "Contract" in node.name or node.name.endswith("Contract"): attrs = [] for item in node.body: if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): attrs.append(item.target.id) elif isinstance(item, ast.Assign): for t in item.targets: if isinstance(t, ast.Name): attrs.append(t.id) contracts.append({"name": node.name, "file": rel, "attributes": attrs}) return contracts def find_contract_access() -> list[dict]: accesses = [] for py in APP.rglob("*.py"): try: content = py.read_text() except: continue rel = str(py.relative_to(ROOT)) for m in re.finditer(r'(\w+Contract)\.(\w+)', content): accesses.append({"contract": m.group(1), "attribute": m.group(2), "file": rel}) return accesses def main(): print("=" * 70) print("TRACE CONTRACTS — Contract Attribute Mismatches") print("=" * 70) contracts = find_contracts() accesses = find_contract_access() contract_attrs = {c["name"]: set(c["attributes"]) for c in contracts} mismatches = [] for acc in accesses: cname = acc["contract"] attr = acc["attribute"] if cname in contract_attrs and attr not in contract_attrs[cname]: mismatches.append(acc) print(f"\nContracts found: {len(contracts)}") print(f"Contract accesses: {len(accesses)}") print(f"\nMISMATCHES: {len(mismatches)}") if mismatches: print("\n--- Contract attribute mismatches ---") for m in mismatches: print(f" {m['contract']}.{m['attribute']} (in {m['file']})") results = {"contracts": contracts, "accesses": accesses, "mismatches": mismatches} with open(ROOT / "scripts" / "test_suite" / "results_trace_contracts.json", "w") as f: json.dump(results, f, indent=2, default=str) return len(mismatches) if __name__ == "__main__": sys.exit(main())