#!/usr/bin/env python3 """MAINT-20: prove a comment/docstring cut changed no code, and show the ratio. python3 goodwe_controller/tools/maint20_same_code.py [BASE_REF] [FILE ...] For each FILE (default: every goodwe_controller/app/*.py that differs from BASE_REF, default release/1.0) the ast.dump() of the tree with docstrings removed must be identical at BASE_REF and in the working tree. Also prints code lines vs comment-only + docstring lines (whole lines, via tokenize) before and after. Run from the repository root. Exit 1 on any code difference. """ import ast import io import subprocess import sys import tokenize SCOPES = (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef) def docstring(n): if isinstance(n, SCOPES) and n.body: e = n.body[0] if (isinstance(e, ast.Expr) and isinstance(e.value, ast.Constant) and isinstance(e.value.value, str)): return e return None def dump(src): tree = ast.parse(src) for n in ast.walk(tree): if docstring(n) is not None: n.body = n.body[1:] or [ast.Pass()] return ast.dump(tree) def counts(src): doc = set() for n in ast.walk(ast.parse(src)): e = docstring(n) if e is not None: doc.update(range(e.lineno, e.end_lineno + 1)) skip = (tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT, tokenize.ENDMARKER, tokenize.COMMENT) code, com = set(), set() for tk in tokenize.generate_tokens(io.StringIO(src).readline): if tk.type == tokenize.COMMENT: com.add(tk.start[0]) elif tk.type not in skip: code.update(range(tk.start[0], tk.end[0] + 1)) code -= doc return len(code), len(doc) + len(com - code - doc) def git(*a): return subprocess.run(["git", *a], check=True, capture_output=True, text=True).stdout def main(argv): base = argv[0] if argv else "release/1.0" files = argv[1:] or [ f for f in git("diff", "--name-only", base, "--", "goodwe_controller/app").split() if f.endswith(".py")] bad = 0 print(f"base {base} = {git('rev-parse', base).strip()}") for f in files: old = git("show", f"{base}:{f}") new = open(f, encoding="utf-8").read() same = dump(old) == dump(new) (c0, m0), (c1, m1) = counts(old), counts(new) print(f"{'SAME CODE' if same else 'CODE DIFFERS'} {f} " f"code {c0}->{c1} comment+doc {m0}->{m1} " f"ratio {m0 / c0:.2f}->{m1 / c1:.2f}") bad += not same print("OK: no code change" if not bad else f"FAIL: {bad} file(s) differ") return 1 if bad else 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))