11import ast
22import asttokens
33import os
4+ import json
5+ import re
46
57def extract_classes_and_functions (filepath , repo_saved_path ):
68 file_text = ""
@@ -41,6 +43,8 @@ class ClassVisitor(ast.NodeVisitor):
4143 def visit_ClassDef (self , node ):
4244 try :
4345 class_docstring = ast .get_docstring (node )
46+ if class_docstring is None :
47+ class_docstring = ""
4448 class_docstring_end = get_docstring_end_line (node )
4549 clazz = {
4650 "name" : node .name ,
@@ -51,9 +55,12 @@ def visit_ClassDef(self, node):
5155 "docstring" : class_docstring if class_docstring is not None else "" ,
5256 "methods" : []
5357 }
54- all_lines [prefix + ":" + node .name ] = {"line" : (clazz ["start_line" ], clazz ["end_line" ]), "docstring" : class_docstring , "docstring_end_line" : clazz ["docstring_end_line" ], "file" : prefix }
58+ all_lines [prefix + ":" + node .name ] = {"line" : (clazz ["start_line" ], clazz ["end_line" ]), "docstring" : class_docstring , "docstring_end_line" : clazz ["docstring_end_line" ], "file" : prefix , "class" : "" , "type" : "class" , "methods" : [], "parameters" : {}, "returns" : {}}
59+ all_methods = []
60+ all_parameters = {}
61+ all_returns = {}
5562 for item in node .body :
56- if isinstance (item , ast .FunctionDef ):
63+ if isinstance (item , ast .FunctionDef ) or isinstance ( item , ast . AsyncFunctionDef ) :
5764 returns = [
5865 ast .unparse (stmt .value ) if stmt .value else None
5966 for stmt in item .body if isinstance (stmt , ast .Return )
@@ -62,19 +69,29 @@ def visit_ClassDef(self, node):
6269 docstring_end_line = get_docstring_end_line (item )
6370 decorators = [ast .unparse (d ) for d in item .decorator_list ]
6471 body_text = atok .get_text (item )[item .body [0 ].col_offset :] if item .body else ""
72+ parmeters = {}
73+ for arg in item .args .args :
74+ annotation = ast .unparse (arg .annotation ) if arg .annotation else None
75+ parmeters [arg .arg ] = annotation
6576
6677 method = {
6778 "name" : item .name ,
6879 "start_line" : item .decorator_list [0 ].lineno if item .decorator_list else item .lineno ,
6980 "end_line" : item .end_lineno ,
7081 "docstring_end_line" : docstring_end_line ,
71- "parameters" : [ arg . arg for arg in item . args . args ] ,
82+ "parameters" : parmeters ,
7283 "returns" : returns if returns else None ,
7384 "docstring" : docstring if docstring is not None else "" ,
7485 "decorators" : decorators if decorators else None ,
7586 "body" : body_text .strip ()
7687 }
77- all_lines [prefix + ":" + node .name + "." + item .name ] = {"line" : (method ["start_line" ], method ["end_line" ]), "docstring" : method ["docstring" ], "docstring_end_line" : method ["docstring_end_line" ], "file" : prefix }
88+ all_lines [prefix + ":" + node .name + "." + item .name ] = {"line" : (method ["start_line" ], method ["end_line" ]), "docstring" : method ["docstring" ], "docstring_end_line" : method ["docstring_end_line" ], "file" : prefix , "class" : prefix + ":" + node .name , "type" : "method" , "methods" : [], "parameters" : method ["parameters" ], "returns" : method ["returns" ]}
89+ all_methods .append (prefix + ":" + node .name + "." + item .name )
90+ all_parameters [prefix + ":" + node .name + "." + item .name ] = method ["parameters" ]
91+ all_returns [prefix + ":" + node .name + "." + item .name ] = method ["returns" ]
92+ all_lines [prefix + ":" + node .name ]["methods" ] = all_methods
93+ all_lines [prefix + ":" + node .name ]["parameters" ] = all_parameters
94+ all_lines [prefix + ":" + node .name ]["returns" ] = all_returns
7895
7996 # classes.append(clazz)
8097 except Exception as e :
@@ -104,19 +121,22 @@ def visit_FunctionDef(self, node):
104121 docstring = ast .get_docstring (node )
105122 docstring_end_line = get_docstring_end_line (node )
106123 body_text = atok .get_text (node )[node .body [0 ].col_offset :] if node .body else ""
107-
124+ parmeters = {}
125+ for arg in node .args .args :
126+ annotation = ast .unparse (arg .annotation ) if arg .annotation else None
127+ parmeters [arg .arg ] = annotation
108128 function = {
109129 "name" : node .name ,
110130 "start_line" : node .decorator_list [0 ].lineno if node .decorator_list else node .lineno ,
111131 "end_line" : node .end_lineno ,
112132 "docstring_end_line" : docstring_end_line ,
113- "parameters" : [ arg . arg for arg in node . args . args ] ,
133+ "parameters" : parmeters ,
114134 "returns" : returns if returns else None ,
115135 "docstring" : docstring if docstring is not None else "" ,
116136 "decorators" : decorators if decorators else None ,
117137 "body" : body_text .strip ()
118138 }
119- all_lines [prefix + ":" + node .name ] = {"line" : (function ["start_line" ], function ["end_line" ]), "docstring" : function ["docstring" ], "docstring_end_line" : function ["docstring_end_line" ], "file" : prefix }
139+ all_lines [prefix + ":" + node .name ] = {"line" : (function ["start_line" ], function ["end_line" ]), "docstring" : function ["docstring" ], "docstring_end_line" : function ["docstring_end_line" ], "file" : prefix , "class" : "" , "type" : "function" , "methods" : [], "parameters" : { prefix + ":" + node . name : function [ "parameters" ]}, "returns" : { prefix + ":" + node . name : function [ "returns" ]} }
120140 except Exception as e :
121141 print (f"Error while processing function { node .name } : { e } " )
122142 finally :
@@ -140,4 +160,73 @@ def extract_classes_and_functions_from_repo(repo_saved_path):
140160 all_code_lines .update (file_lines )
141161 except Exception as e :
142162 print (f"Error processing file { filepath } : { e } " )
143- return all_code_lines
163+ return all_code_lines
164+
165+ def extract_import_lines (file_content : str ):
166+ """
167+ Extracts import lines from the given file content: import, import as, from, and from ... import.
168+ Use ast to parse the file content and extract import statements.
169+ """
170+ imports = []
171+ try :
172+ tree = ast .parse (file_content )
173+ except SyntaxError as e :
174+ import_pattern = re .compile (r'^\s*(import|from)\s+.*$' , re .MULTILINE )
175+ lines = file_content .split ("\n " )
176+ for line in lines :
177+ if import_pattern .match (line ):
178+ imports .append (line .strip ())
179+ return "\n " .join (imports )
180+
181+ # 遍历 AST 的顶层节点
182+ for node in ast .walk (tree ):
183+ if isinstance (node , ast .Import ):
184+ for alias in node .names :
185+ if alias .asname :
186+ imports .append (f"import { alias .name } as { alias .asname } " )
187+ else :
188+ imports .append (f"import { alias .name } " )
189+ elif isinstance (node , ast .ImportFrom ):
190+ module = node .module if node .module else "" # 'from . import ...' 的情况
191+ names = []
192+ for alias in node .names :
193+ if alias .asname :
194+ names .append (f"{ alias .name } as { alias .asname } " )
195+ else :
196+ names .append (alias .name )
197+ if node .level > 0 : # 处理相对导入,例如 from .module import name
198+ module_prefix = "." * node .level
199+ module = module_prefix + module
200+ imports .append (f"from { module } import { ', ' .join (names )} " )
201+ return "\n " .join (imports )
202+
203+ class Repo_file_container :
204+ def __init__ (self , repo_saved_path : str ):
205+ self .repo_saved_path = repo_saved_path
206+ self .file_dic = {}
207+ self .get_all_files ()
208+
209+ def get_all_files (self ):
210+ """
211+ Returns a list of all Python files in the dataset directory.
212+ """
213+ for root , _ , files in os .walk (self .repo_saved_path ):
214+ for file in files :
215+ if not file .endswith (".py" ):
216+ continue
217+ file_path = os .path .join (root , file )
218+ try :
219+ with open (file_path , "r" , encoding = "utf-8" , errors = "ignore" ) as f :
220+ content = f .read ()
221+ f .close ()
222+ except Exception as e :
223+ logging .info (f"Error reading { file_path } : { e } " )
224+ continue
225+ import_lines = extract_import_lines (content )
226+ prefix = file_path .replace (self .repo_saved_path , "" ).lstrip ("/" )
227+ self .file_dic [prefix ] = {"lines" : content .split ("\n " ), "imports" : import_lines }
228+ def get_file_content (self , file_key : str ):
229+ """
230+ Returns the content of a file given its key.
231+ """
232+ return self .file_dic [file_key ]
0 commit comments