64 lines
2.6 KiB
Python
64 lines
2.6 KiB
Python
|
|
#!/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())
|