18 from Tool.tool
import getAst
47 obj=re.compile(self.
_pattern_pattern,re.DOTALL)
51 for index
in range(0,len(lst)):
52 if lst[index].find(
'\n')!=-1:
53 lst[index]=lst[index].replace(
'\n',
'')
54 lst[index]=lst[index].replace(
' ',
'')
70 import_visitor=Import()
72 import_visitor.visit(root_node)
73 except Exception
as e:
74 print(f
"import visit failed: {e}")
75 md_names=import_visitor.get_md_name()
78 assign_visitor=AssignVisitor()
79 assign_visitor.visit(root_node)
80 target_call=assign_visitor.get_target_call()
82 for key,val
in target_call.items():
83 name_parts=val.split(
'.')
84 if name_parts[0]
in target_call:
85 target_call[key]=target_call[name_parts[0]]+
'.'+
'.'.join(name_parts[1:])
87 for key,val
in target_call.items():
88 name_parts=val.split(
'.')
89 if name_parts[0]
in md_names:
90 target_call[key]=(md_names[name_parts[0]]+
'.'+
'.'.join(name_parts[1:])).rstrip(
'.')
105 absolutePath=[k
for k
in fileDict.keys()][0]
106 relativePath=[v
for v
in fileDict.values()][0]
107 norm_relative=relativePath.replace(
'\\',
'/')
108 norm_absolute=absolutePath.replace(
'\\',
'/')
109 pos1=norm_relative.rfind(
'/')
112 relativePath=relativePath[0:pos1]
113 pos2=norm_absolute.rfind(
'/')
114 absolutePath=absolutePath[0:pos2]
115 initPath=f
"{absolutePath}/__init__.py"
117 if os.path.exists(initPath):
118 currentLevel=relativePath.replace(
'\\',
'/').split(
'/')[-1]
119 cacheKey=(initPath,currentLevel)
120 if importCache
is not None and cacheKey
in importCache:
121 importDict=importCache[cacheKey]
125 except Exception
as e:
126 print(f
"shortenPath --> ast.parse failed: {e}")
128 obj=FromImport(currentLevel)
130 importDict=obj.importDict
131 if importCache
is not None:
132 importCache[cacheKey]=importDict
137 for key,value
in importDict.items():
148 api=api.replace(replaceKey2,replaceVal2)
150 api=api.replace(replaceKey1,replaceVal1)
152 shortenPath(lst,{absolutePath:relativePath},importCache)
165 def getClass(lst,root,prefix,fileDict, pyiFlag=0, importCache=None):
171 for n
in ast.iter_child_nodes(root):
174 if isinstance(n,(ast.FunctionDef, ast.AsyncFunctionDef)):
175 if 'overload' in ast.unparse(n.decorator_list)
and not pyiFlag:
178 arg=ast.unparse(n.args)
179 arg=arg.replace(
' ',
'')
181 ret=
'->'+ast.unparse(n.returns)
184 if funcName==
'__init__':
187 elif funcName==
'__new__':
190 elif funcName==
'__call__':
194 lst.append(f
"{prefix}.{className}.{funcName}({arg}){ret}")
196 apiPath=[f
"{prefix}.{className}.{funcName}"]
199 if apiPath[0]!=f
"{prefix}.{className}.{funcName}":
200 lst.append(f
"{apiPath[0]}({arg}){ret}")
204 lst.append(f
"{prefix}.{className}.__init__{para}")
207 lst.append(f
"{prefix}.{className}.__new__{para}")
210 lst.append(f
"{prefix}.{className}.__call__{para}")
212 pattern=fr
"class {re.escape(className)}(\(.*?):"
213 codeText=ast.unparse(root)
221 lst.append(f
"{prefix}.{className}{para}")
224 apiPath=[f
"{prefix}.{className}"]
226 if apiPath[0]!=f
"{prefix}.{className}":
227 lst.append(f
"{apiPath[0]}{para}")
230 prefix+=f
".{className}"
231 for n
in ast.iter_child_nodes(root):
232 if isinstance(n,ast.ClassDef):
233 getClass(lst,n,prefix,fileDict,pyiFlag,importCache)
246 def task(codeText,libApi,prefix,fileDict, pyiFlag=0, importCache=None):
248 rootNode=ast.parse(codeText,filename=
'<unknown>',mode=
'exec')
249 except Exception
as e:
250 file = list(fileDict.keys())[0]
251 print(f
"{file} ast.parse falied: {e}")
253 for node
in ast.iter_child_nodes(rootNode):
254 if isinstance(node, ast.ClassDef):
255 getClass(libApi,node,prefix,fileDict,pyiFlag,importCache)
259 if isinstance(node,(ast.FunctionDef, ast.AsyncFunctionDef)):
260 if 'overload' in ast.unparse(node.decorator_list)
and not pyiFlag:
263 arg=ast.unparse(node.args)
264 arg=arg.replace(
' ',
'')
266 ret=
'->'+ast.unparse(node.returns)
269 libApi.append(f
"{prefix}.{funcName}({arg}){ret}")
272 lst=[f
"{prefix}.{funcName}"]
274 if lst[0]!=f
"{prefix}.{funcName}":
275 libApi.append(f
"{lst[0]}({arg}){ret}")
286 if not sourceRoot
or not publicRoot:
288 sourcePrefix=sourceRoot+
'.'
289 publicPrefix=publicRoot+
'.'
290 assignSourcePrefix=
'A:'+sourcePrefix
291 assignPublicPrefix=
'A:'+publicPrefix
292 if line.startswith(sourcePrefix):
293 return publicPrefix+line[len(sourcePrefix):]
294 if line.startswith(assignSourcePrefix):
295 return assignPublicPrefix+line[len(assignSourcePrefix):]
308 fw.write(f
"{line}\n")
310 if aliasLine
and aliasLine!=line:
311 fw.write(f
"{aliasLine}\n")
320 libName, version, libPath=args
322 fileObj.getPath(libPath)
324 filePath=fileObj.path
325 if not os.path.exists(f
"LibAPIExtraction/{libName}"):
327 os.mkdir(f
"LibAPIExtraction/{libName}")
330 f=open(f
'LibAPIExtraction/{libName}/{libName}{version}',
'w',encoding=
'UTF-8')
337 if libName==
"tensorflow" and os.path.basename(os.path.normpath(libPath))==
"tensorflow_core":
338 publicAliasSource=
"tensorflow_core"
339 publicAliasTarget=
"tensorflow"
340 for file
in filePath:
343 def2format=Def2format()
344 def2format.toFormat(file)
345 prefix=def2format.prefix
346 relativePath=def2format.relativePath
347 fileDict={file:relativePath}
349 if file[-1]==
'y' and file
not in fileVisitLst:
350 fileVisitLst.append(file)
354 if file+
'i' not in fileVisitLst:
356 with open(file+
'i',
'r',encoding=
'UTF-8')
as fr:
358 task(code_text,pyiLst,prefix,fileDict, 1, importCache)
360 fileVisitLst.append(file+
'i')
361 except FileNotFoundError:
364 with open(file,
'r',encoding=
'UTF-8')
as fr:
367 except Exception
as e:
368 print(f
"{file} read failed: {e}")
371 root_node=ast.parse(code_text,filename=
'<unknown>',mode=
'exec')
372 except Exception
as e:
373 print(f
'{file} ast.parse failed: {e}')
376 f.write(
'\n'+
'-' * 40 + f
"{file}" +
'-' * 40+
'\n')
377 for key,val
in assignDict.items():
378 writeApiLine(f,f
'A:{prefix}.{key}->{val}',publicAliasSource,publicAliasTarget)
380 task(code_text,pyLst,prefix,fileDict,0,importCache)
390 if it2.split(
'(')[0]==it1.split(
'(')[0]:
391 removeLst.append(it1)
398 f.write(
'\n'+
'-' * 40 + f
"{file}"+
'i' +
'-' * 40+
'\n')
403 elif file[-1]==
'i' and file
not in fileVisitLst:
404 fileVisitLst.append(file)
405 if file.rstrip(
'i')
not in fileVisitLst:
407 with open(file.rstrip(
'i'),
'r',encoding=
'UTF-8')
as fr:
409 task(code_text,pyLst,prefix,fileDict,0,importCache)
410 fileVisitLst.append(file.rstrip(
'i'))
411 root_node=ast.parse(code_text,filename=
'<unknown>',mode=
'exec')
413 f.write(
'\n'+
'-' * 40 + f
"{file.rstrip('i')}" +
'-' * 40+
'\n')
414 for key,value
in assignDict.items():
415 writeApiLine(f,f
'A:{prefix}.{key}->{value}',publicAliasSource,publicAliasTarget)
420 except FileNotFoundError:
423 with open(file,
'r',encoding=
'UTF-8')
as fr:
425 task(code_text,pyiLst,prefix,fileDict,1,importCache)
430 if it2.split(
'(')[0]==it1.split(
'(')[0]:
431 removeLst.append(it1)
436 f.write(
'\n'+
'-' * 40 + f
"{file}" +
'-' * 40+
'\n')
Regular expression match class 正则表达式匹配类
def regex_match(self)
Perform the regular expression match 执行正则表达式匹配
def __init__(self, code_text, pattern)
The constructor 构造函数
def get_result(self)
Return the match result 返回匹配结果
def getAssign(root_node)
Extract all assign node from a .py file's AST 通过AST获取.py文件的Assign语句
def writeApiLine(fw, line, sourceRoot='', publicRoot='')
Write one lib API line and its public alias if needed 写出一行库API,并按需写出公开路径别名
def getClass(lst, root, prefix, fileDict, pyiFlag=0, importCache=None)
Extract class method definitions from a give class.
def shortenPath(lst, fileDict, importCache=None)
Shorten the API path based on init.py and import alias 通过解析__init__.py和import别名,把源码中的部分API路径缩短
def task(codeText, libApi, prefix, fileDict, pyiFlag=0, importCache=None)
Extract all lib API definitions from a source file 抽取库源码API定义任务
def getDefFunction(args)
Extract all lib API definitions from a specified version 抽取给定版本的库API定义
def getPublicAliasLine(line, sourceRoot, publicRoot)
Return a public alias line for source-root API lines 为源码根路径API行生成公开路径别名