Files
troupe-standup/wizard-work/forge/_scan_dry.py
T

69 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Scan tools/*.py for DRY smells: repeated sum(1 for ...)/list-comprehension patterns,
duplicated function bodies, and other refactor candidates."""
import ast, os, hashlib, collections, sys
REPO = "/root/troupe-standup"
TOOLS = os.path.join(REPO, "tools")
def scan_file(path):
src = open(path, encoding="utf-8").read()
lines = src.splitlines()
info = collections.Counter()
# sum(1 for ... patterns - potential DRY
info["sum_gen"] = sum(1 for l in lines if "sum(1 for" in l)
# len([...]) vs len([...]) patterns
info["len_listcomp"] = sum(1 for l in lines if "len([" in l or "len([" in l)
# list comprehensions that filter
info["listcomp_filter"] = sum(1 for l in lines if "if r[" in l)
# count() calls on lists
info["count_method"] = sum(1 for l in lines if ".count(" in l)
# manual counting loops
info["manual_count"] = sum(1 for l in lines if "+= 1" in l)
# Find repeated string literals / patterns
# Look for duplicated .strip() calls, split() calls etc.
# Check for duplicate function bodies using AST
try:
tree = ast.parse(src)
funcs = [(node.name, node.body) for node in ast.walk(tree) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
# hash each function body
body_hashes = {}
for name, body in funcs:
h = hashlib.sha256(ast.unparse(body).encode()).hexdigest()[:12]
body_hashes.setdefault(h, []).append(name)
dup_funcs = {h: names for h, names in body_hashes.items() if len(names) > 1}
except Exception as e:
dup_funcs = {}
funcs = []
# Find repeated string patterns (lines that appear 3+ times)
line_counts = collections.Counter(l.strip() for l in lines if l.strip())
repeated_lines = [(l, c) for l, c in line_counts.items() if c >= 3 and len(l) > 15]
return info, dup_funcs, repeated_lines, len(lines), funcs
for f in sorted(os.listdir(TOOLS)):
if not f.endswith(".py") or f.startswith("_"):
continue
path = os.path.join(TOOLS, f)
info, dup_funcs, repeated, nlines, funcs = scan_file(path)
if info["sum_gen"] >= 2 or dup_funcs or repeated:
print(f"\n=== {f} ({nlines} lines, {len(funcs)} funcs) ===")
if info["sum_gen"]:
print(f" sum(1 for ...) x{info['sum_gen']}")
if info["len_listcomp"]:
print(f" len([...]) x{info['len_listcomp']}")
if info["listcomp_filter"]:
print(f" listcomp with if-filter x{info['listcomp_filter']}")
if info["count_method"]:
print(f" .count() calls x{info['count_method']}")
if info["manual_count"]:
print(f" manual +=1 count x{info['manual_count']}")
if dup_funcs:
print(f" DUPLICATE function bodies: {dup_funcs}")
for l, c in repeated[:5]:
print(f" repeated x{c}: {l[:90]}")