@@ -145,6 +145,7 @@ def parse(self, path: Path, is_dependency: bool = False, is_notebook: bool = Fal
145145 classes = self ._find_classes (root_node )
146146 imports = self ._find_imports (root_node )
147147 function_calls = self ._find_calls (root_node )
148+ self ._attach_module_context (functions , function_calls , root_node , index_source )
148149 variables = self ._find_variables (root_node )
149150
150151 return {
@@ -165,6 +166,41 @@ def parse(self, path: Path, is_dependency: bool = False, is_notebook: bool = Fal
165166 os .remove (temp_py_file )
166167 info_logger (f"Removed temporary file: { temp_py_file } " )
167168
169+ def _attach_module_context (self , functions , function_calls , root_node , index_source : bool = False ):
170+ """Represent module-level executable code as Python's <module> frame."""
171+ module_level_calls = [
172+ call for call in function_calls
173+ if not call .get ("context" ) or call ["context" ][0 ] is None
174+ ]
175+ if not module_level_calls :
176+ return
177+
178+ has_module_frame = any (
179+ func .get ("name" ) == "<module>" and func .get ("line_number" ) == 1
180+ for func in functions
181+ )
182+ if not has_module_frame :
183+ module_func = {
184+ "name" : "<module>" ,
185+ "line_number" : 1 ,
186+ "end_line" : root_node .end_point [0 ] + 1 ,
187+ "args" : [],
188+ "cyclomatic_complexity" : 1 ,
189+ "context" : None ,
190+ "context_type" : "module" ,
191+ "class_context" : None ,
192+ "decorators" : [],
193+ "lang" : self .language_name ,
194+ "is_dependency" : False ,
195+ }
196+ if index_source :
197+ module_func ["source" ] = self ._get_node_text (root_node )
198+ module_func ["docstring" ] = self ._get_docstring (root_node )
199+ functions .append (module_func )
200+
201+ for call in module_level_calls :
202+ call ["context" ] = ("<module>" , "module" , 1 )
203+
168204 def _find_lambda_assignments (self , root_node , index_source : bool = False ):
169205 functions = []
170206 query_str = PY_QUERIES .get ('lambda_assignments' )
@@ -573,4 +609,4 @@ def pre_scan_python(files: list[Path], parser_wrapper) -> dict:
573609 finally :
574610 if temp_py_file and temp_py_file .exists ():
575611 os .remove (temp_py_file )
576- return imports_map
612+ return imports_map
0 commit comments