Coverage for src / renaissance / recipes / python_refactoring.py: 98%

44 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-09-09 14:04 +0000

1import importlib 

2from collections.abc import Sequence 

3from pathlib import Path 

4from typing import cast 

5 

6from termcolor import colored 

7 

8from renaissance.integrations.python.ast.factory import PythonFactory, PythonPatternFactory 

9from renaissance.integrations.python.ast.rst_node import PythonRstNode 

10from renaissance.integrations.python.ast.util import to_str 

11from renaissance.syntax_tree import ASTProcessor 

12from renaissance.syntax_tree.match_finder import match_pattern 

13from renaissance.utils.text_utils import snake_case 

14 

15 

16class PythonRefactoring(ASTProcessor): 

17 def __init__(self, file): 

18 factory = PythonFactory(PythonRstNode) 

19 atu = factory.create(file) 

20 super().__init__(atu, factory, False) 

21 self.pattern_factory = PythonPatternFactory(self.factory) 

22 self.black_list_pattern = ".git" 

23 self.white_list_pattern = "" 

24 

25 def replace_stmt(self, find, repl): 

26 pattern = self.pattern_factory.create_statements(find) 

27 for match in match_pattern(self.root.children, pattern): 

28 replacement = repl 

29 for exp in match.expansions: 

30 arg_str = ", ".join([to_str(node) for node in match.expansions[exp]]) 

31 replacement = replacement.replace(exp, arg_str) 

32 

33 replacement = replacement.replace(" ,)", ")").replace(", )", ")") 

34 self.replace(replacement, match.nodes, False, False) 

35 

36 @staticmethod 

37 def process(class_name, file): 

38 """Return a subclass by name using importlib, like Java's Class.forName().""" 

39 snake = snake_case(class_name) 

40 module = importlib.import_module(f"renaissance.recipes.{snake}") 

41 cls = getattr(module, class_name) 

42 refactor = cls(file) 

43 if refactor.black_list_pattern in refactor.filename or refactor.white_list_pattern not in refactor.filename: 

44 print(f"skipping: {Path(refactor.filename).resolve()}") 

45 return 

46 

47 print(colored(f"refactor {Path(refactor.filename).resolve()}", "green", attrs=["bold"])) 

48 refactor.run() 

49 

50 @property 

51 def body(self) -> Sequence[PythonRstNode]: 

52 return cast("PythonRstNode", cast("object", self.root)).body 

53 

54 def run(self): 

55 pass