from __future__ import annotations import ast import sys import os import re def transform_file(file_path: str) -> None: """ Refactors App._render_xxx methods to module-level functions for hot-reloading. Now correctly renames 'self' to 'app' inside function bodies. """ with open(file_path, "r", encoding="utf-8") as f: content = f.read() tree = ast.parse(content) class VariableRenamer(ast.NodeTransformer): def visit_Name(self, node: ast.Name) -> ast.Name: if node.id == "self": node.id = "app" return node class RenderTransformer(ast.NodeTransformer): def __init__(self): self.new_functions = [] def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef: if node.name != "App": return node new_body = [] for item in node.body: if isinstance(item, ast.FunctionDef) and item.name.startswith("_render_") and item.name != "_render_window_if_open": func_name = item.name.lstrip("_") # Transform body: rename 'self' to 'app' renamer = VariableRenamer() new_body_nodes = [renamer.visit(b) for b in item.body] new_func = ast.FunctionDef( name=func_name, args=item.args, body=new_body_nodes, decorator_list=item.decorator_list, returns=item.returns ) if new_func.args.args and new_func.args.args[0].arg == "self": new_func.args.args[0].arg = "app" self.new_functions.append(new_func) # Create delegation wrapper in class wrapper_body = [ ast.Expr( value=ast.Call( func=ast.Name(id=func_name, ctx=ast.Load()), args=[ast.Name(id="self", ctx=ast.Load())] + [ast.Name(id=a.arg, ctx=ast.Load()) for a in item.args.args[1:]], keywords=[ast.keyword(arg=kw.arg, value=ast.Name(id=kw.arg, ctx=ast.Load())) for kw in item.args.kwonlyargs] ) ) ] item.body = wrapper_body new_body.append(item) else: new_body.append(item) node.body = new_body return node transformer = RenderTransformer() new_tree = transformer.visit(tree) for func in transformer.new_functions: new_tree.body.append(func) ast.fix_missing_locations(new_tree) if hasattr(ast, "unparse"): raw_output = ast.unparse(new_tree) lines = raw_output.splitlines() new_lines = [] for line in lines: match = re.match(r"^( +)(.*)", line) if match: spaces, rest = match.groups() new_indent = " " * (len(spaces) // 4) new_lines.append(new_indent + rest) else: new_lines.append(line) final_content = "\n".join(new_lines) with open(file_path, "w", encoding="utf-8") as f: f.write(final_content) print(f"Successfully transformed {file_path} with 1-space indentation and 'self'->'app' migration.") else: print("ast.unparse not available.") if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python scripts/transform_render_methods.py ") else: transform_file(sys.argv[1])