@@ -92,6 +92,27 @@ def __init__(
9292 self .tests_root = tests_root
9393 self .inserted_decorator = False
9494
95+ # Precompute decorator components to avoid reconstructing on every node visit
96+ # Only the `function_name` field changes per class
97+ self ._base_decorator_keywords = [
98+ ast .keyword (arg = "tmp_dir_path" , value = ast .Constant (value = self .tmp_dir_path )),
99+ ast .keyword (arg = "tests_root" , value = ast .Constant (value = self .tests_root .as_posix ())),
100+ ast .keyword (arg = "is_fto" , value = ast .Constant (value = self .is_fto )),
101+ ]
102+ self ._base_decorator_func = ast .Name (id = "codeflash_capture" , ctx = ast .Load ())
103+
104+ # Preconstruct starred/kwargs for super init injection for perf
105+ self ._super_starred = ast .Starred (value = ast .Name (id = "args" , ctx = ast .Load ()))
106+ self ._super_kwarg = ast .keyword (arg = None , value = ast .Name (id = "kwargs" , ctx = ast .Load ()))
107+ self ._super_func = ast .Attribute (
108+ value = ast .Call (func = ast .Name (id = "super" , ctx = ast .Load ()), args = [], keywords = []),
109+ attr = "__init__" ,
110+ ctx = ast .Load (),
111+ )
112+ self ._init_vararg = ast .arg (arg = "args" )
113+ self ._init_kwarg = ast .arg (arg = "kwargs" )
114+ self ._init_self_arg = ast .arg (arg = "self" , annotation = None )
115+
95116 def visit_ImportFrom (self , node : ast .ImportFrom ) -> ast .ImportFrom :
96117 # Check if our import already exists
97118 if node .module == "codeflash.verification.codeflash_capture" and any (
@@ -114,21 +135,18 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef:
114135 if node .name not in self .target_classes :
115136 return node
116137
117- # Look for __init__ method
118138 has_init = False
119-
120- # Create the decorator
139+ # Build decorator node ONCE for each class, not per loop iteration
121140 decorator = ast .Call (
122- func = ast . Name ( id = "codeflash_capture" , ctx = ast . Load ()) ,
141+ func = self . _base_decorator_func ,
123142 args = [],
124143 keywords = [
125144 ast .keyword (arg = "function_name" , value = ast .Constant (value = f"{ node .name } .__init__" )),
126- ast .keyword (arg = "tmp_dir_path" , value = ast .Constant (value = self .tmp_dir_path )),
127- ast .keyword (arg = "tests_root" , value = ast .Constant (value = self .tests_root .as_posix ())),
128- ast .keyword (arg = "is_fto" , value = ast .Constant (value = self .is_fto )),
145+ * self ._base_decorator_keywords ,
129146 ],
130147 )
131148
149+ # Only scan node.body once for both __init__ and decorator check
132150 for item in node .body :
133151 if (
134152 isinstance (item , ast .FunctionDef )
@@ -139,35 +157,28 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef:
139157 ):
140158 has_init = True
141159
142- # Add decorator at the start of the list if not already present
143- if not any (
144- isinstance (d , ast .Call ) and isinstance (d .func , ast .Name ) and d .func .id == "codeflash_capture"
145- for d in item .decorator_list
146- ):
160+ # Check for existing decorator in-place, stop after finding one
161+ for d in item .decorator_list :
162+ if isinstance (d , ast .Call ) and isinstance (d .func , ast .Name ) and d .func .id == "codeflash_capture" :
163+ break
164+ else :
165+ # No decorator found
147166 item .decorator_list .insert (0 , decorator )
148167 self .inserted_decorator = True
149168
150169 if not has_init :
151- # Create super().__init__(*args, **kwargs) call
170+ # Create super().__init__(*args, **kwargs) call (use prebuilt AST fragments)
152171 super_call = ast .Expr (
153- value = ast .Call (
154- func = ast .Attribute (
155- value = ast .Call (func = ast .Name (id = "super" , ctx = ast .Load ()), args = [], keywords = []),
156- attr = "__init__" ,
157- ctx = ast .Load (),
158- ),
159- args = [ast .Starred (value = ast .Name (id = "args" , ctx = ast .Load ()))],
160- keywords = [ast .keyword (arg = None , value = ast .Name (id = "kwargs" , ctx = ast .Load ()))],
161- )
172+ value = ast .Call (func = self ._super_func , args = [self ._super_starred ], keywords = [self ._super_kwarg ])
162173 )
163- # Create function arguments: self, *args, **kwargs
174+ # Create function arguments: self, *args, **kwargs (reuse arg nodes)
164175 arguments = ast .arguments (
165176 posonlyargs = [],
166- args = [ast . arg ( arg = " self" , annotation = None ) ],
167- vararg = ast . arg ( arg = "args" ) ,
177+ args = [self . _init_self_arg ],
178+ vararg = self . _init_vararg ,
168179 kwonlyargs = [],
169180 kw_defaults = [],
170- kwarg = ast . arg ( arg = "kwargs" ) ,
181+ kwarg = self . _init_kwarg ,
171182 defaults = [],
172183 )
173184
0 commit comments