PCART  v1.4
Automated Repair of Python API Parameter Compatibility Issues
getDef.py
Go to the documentation of this file.
1 
10 
11 
12 
13 import ast
14 import io
15 import os
16 import re
17 import tokenize
18 from Path.getPath import *
19 from Extract.extractDef import *
20 from Extract.extractCall import *
21 
22 
23 
28 def parseLibrarySource(source,filename='<unknown>'):
29  replacements={}
30  docstrings=[]
31  python2Tried=False
32  while True:
33  try:
34  try:
35  # Use the memory source for error positions instead of the disk file.
36  # 按内存源码定位语法错误,避免Python 3.9回读磁盘原文。
37  root=ast.parse(source,filename='<unknown>',mode='exec')
38  except SyntaxError:
39  # Use the old grammar to preserve async/await identifiers.
40  # 使用旧语法保留async/await名称,不替换真正的关键字。
41  root=ast.parse(source,filename='<unknown>',mode='exec',feature_version=(3,6))
42  break
43  except SyntaxError as error:
44  error.filename=filename
45  lines=io.StringIO(source).readlines()
46  try:
47  tokens=list(tokenize.generate_tokens(io.StringIO(source).readline))
48  except (tokenize.TokenError,IndentationError):
49  raise error
50 
51  if error.lineno and error.offset and error.lineno<=len(lines):
52  line=lines[error.lineno-1]
53  offset=error.offset-1
54  token=next((it for it in tokens if it.type==tokenize.NAME and
55  it.start==(error.lineno,offset)),None)
56  if token and token.string in ('True','False'):
57  placeholder=f'__pcart_keyword_{len(replacements)}__'
58  while placeholder in source:
59  placeholder+='_'
60  replacements[placeholder]=token.string
61  lines[error.lineno-1]=line[:offset]+placeholder+line[token.end[1]:]
62  source=''.join(lines)
63  continue
64 
65  if isinstance(error,TabError):
66  # Expand code indentation using Python 2's eight-column rule.
67  # 按Python 2的八列规则展开代码缩进,保留多行字符串内容。
68  stringRows={row for token in tokens if token.type==tokenize.STRING
69  for row in range(token.start[0]+1,token.end[0]+1)}
70  for row,line in enumerate(lines,1):
71  if row not in stringRows:
72  prefix=re.match(r'[ \t]*',line).group()
73  lines[row-1]=prefix.expandtabs(8)+line[len(prefix):]
74  converted=''.join(lines)
75  if converted!=source:
76  source=converted
77  continue
78 
79  if 'unicodeescape' in error.msg:
80  # Plain Python 2 docstrings leave Unicode-only escapes literal.
81  # 仅处理Python 2普通文档字符串中的Unicode专用转义。
82  statement=[]
83  for token in tokens:
84  if token.type in (tokenize.NL,tokenize.COMMENT,tokenize.INDENT,tokenize.DEDENT):
85  continue
86  if token.type==tokenize.NEWLINE or token.string==';':
87  if statement[:3]==['from','__future__','import'] and 'unicode_literals' in statement:
88  raise error
89  statement=[]
90  else:
91  statement.append(token.string)
92  for i,token in enumerate(tokens):
93  if (token.type!=tokenize.STRING or not token.string.startswith(('"',"'")) or
94  not token.start[0]<=error.lineno<=token.end[0]):
95  continue
96  previous=i-1
97  while previous>=0 and tokens[previous].type in (tokenize.NL,tokenize.COMMENT):
98  previous-=1
99  following=i+1
100  while following<len(tokens) and tokens[following].type in (tokenize.NL,tokenize.COMMENT):
101  following+=1
102  if ((previous>=0 and tokens[previous].type not in (tokenize.NEWLINE,tokenize.INDENT) and
103  tokens[previous].string!=':') or following>=len(tokens) or
104  tokens[following].type not in (tokenize.NEWLINE,tokenize.ENDMARKER)):
105  continue
106  converted=re.sub(r'(\\+)([uUN])',
107  lambda it: it[1]+('\\' if len(it[1])%2 else '')+it[2],token.string)
108  if converted==token.string:
109  continue
110  startRow,startColumn=token.start
111  endRow,endColumn=token.end
112  source=(''.join(lines[:startRow-1])+lines[startRow-1][:startColumn]+
113  converted+lines[endRow-1][endColumn:]+''.join(lines[endRow:]))
114  docstrings.append((startRow,converted,error))
115  break
116  else:
117  raise error
118  continue
119 
120  if error.msg=='Generator expression must be parenthesized' and error.lineno:
121  brackets=[]
122  for i,token in enumerate(tokens):
123  if token.type==tokenize.NAME and token.string=='for' and brackets:
124  brackets[-1][1]=True
125  if token.type!=tokenize.OP:
126  continue
127  if token.string in '([{':
128  brackets.append([token.string,False,token.start[0]])
129  elif token.string in ')]}':
130  if brackets:
131  brackets.pop()
132  elif (token.string==',' and brackets and brackets[-1][:2]==['(',True] and
133  brackets[-1][2]<=error.lineno<=token.start[0]):
134  nextToken=i+1
135  while nextToken<len(tokens) and tokens[nextToken].type in (tokenize.NL,tokenize.COMMENT):
136  nextToken+=1
137  if nextToken<len(tokens) and tokens[nextToken].string==')':
138  row,column=token.start
139  lines[row-1]=lines[row-1][:column]+lines[row-1][column+1:]
140  source=''.join(lines)
141  break
142  else:
143  raise error
144  continue
145 
146  # Only run the three syntax fixers needed by old library sources.
147  # 仅使用旧库源码所需的三种Python 2语法转换。
148  python2Pattern=r'(?m)^\s*(?:print\s+(?!\‍()|exec\s+(?!\‍()|except\s+[^:\n]+,\s*\w+\s*:)'
149  if not python2Tried and re.search(python2Pattern,source):
150  python2Tried=True
151  try:
152  from lib2to3.refactor import RefactoringTool
153  fixers=['lib2to3.fixes.fix_print','lib2to3.fixes.fix_except','lib2to3.fixes.fix_exec']
154  text=source if source.endswith('\n') else source+'\n'
155  converted=str(RefactoringTool(fixers).refactor_string(text,filename))
156  except Exception:
157  raise error
158  if converted!=source:
159  source=converted
160  continue
161  raise error
162 
163  # Check that every repaired string is a real module, class or function docstring.
164  # 确认修复的字符串确实是模块、类或函数的文档字符串。
165  positions=set()
166  for node in ast.walk(root) if docstrings else ():
167  if isinstance(node,(ast.Module,ast.ClassDef,ast.FunctionDef,ast.AsyncFunctionDef)) and node.body:
168  first=node.body[0]
169  if isinstance(first,ast.Expr) and isinstance(first.value,ast.Constant) and isinstance(first.value.value,str):
170  positions.add((first.value.lineno,ast.get_source_segment(source,first.value)))
171  for row,text,error in docstrings:
172  if (row,text) not in positions:
173  raise error
174 
175  if not replacements:
176  return root
177 
178  # Restore original names so extracted API paths and signatures are unchanged.
179  for node in ast.walk(root):
180  for field,value in ast.iter_fields(node):
181  if isinstance(value,str):
182  for placeholder,original in replacements.items():
183  value=value.replace(placeholder,original)
184  setattr(node,field,value)
185  elif isinstance(value,list):
186  for i,item in enumerate(value):
187  if isinstance(item,str):
188  for placeholder,original in replacements.items():
189  item=item.replace(placeholder,original)
190  value[i]=item
191  return root
192 
193 
194 
195 
200 
201 
205  def __init__(self,code_text,pattern):
206  self._code_text_code_text=code_text
207  self._pattern_pattern=pattern
208  self._result_result=[]
209 
210 
213  def get_result(self):
214  return self._result_result
215 
216 
219  def regex_match(self):
220  obj=re.compile(self._pattern_pattern,re.DOTALL)
221  lst=obj.findall(self._code_text_code_text)
222  if len(lst)>0:
223  #对找到的所有参数字符串进行处理
224  for index in range(0,len(lst)):
225  if lst[index].find('\n')!=-1: #若参数字符串含有换行符'\n'
226  lst[index]=lst[index].replace('\n','') #去掉所有的换行符
227  lst[index]=lst[index].replace(' ','') #去掉所有的空格
228 
229  self._result_result=lst
230  return 1
231  else:
232  self._result_result=[]
233  return 0
234 
235 
236 
237 
242 def getAssign(root_node,filePath=None):
243  #找出树中所有的模块名
244  import_visitor=Import()
245  try:
246  # Preserve NodeVisitor's depth-first order without recursive calls.
247  nodes=[root_node]
248  while nodes:
249  node=nodes.pop()
250  if isinstance(node,(ast.Import,ast.ImportFrom)):
251  import_visitor.visit(node)
252  nodes.extend(reversed(list(ast.iter_child_nodes(node))))
253  except Exception as e:
254  print(f"{filePath or '<unknown>'} import visit failed: {e}")
255  md_names=import_visitor.get_md_name() #dict
256 
257  #找出所有的Assign节点
258  assign_visitor=AssignVisitor()
259  nodes=[root_node]
260  while nodes:
261  node=nodes.pop()
262  if isinstance(node,ast.Assign):
263  assign_visitor.visit(node)
264  nodes.extend(reversed(list(ast.iter_child_nodes(node))))
265  target_call=assign_visitor.get_target_call()
266 
267  for key,val in target_call.items():
268  name_parts=val.split('.')
269  if name_parts[0] in target_call:
270  target_call[key]=target_call[name_parts[0]]+'.'+'.'.join(name_parts[1:])
271 
272  for key,val in target_call.items():
273  name_parts=val.split('.')
274  if name_parts[0] in md_names:
275  target_call[key]=(md_names[name_parts[0]]+'.'+'.'.join(name_parts[1:])).rstrip('.')
276 
277  return target_call
278 
279 
280 
281 
289 def shortenPath(lst,fileDict,importCache=None): #lst是传入传出参数,保存修正之后的API路径
290  absolutePath=[k for k in fileDict.keys()][0] #/home/zhang/pkg/file.py
291  relativePath=[v for v in fileDict.values()][0] #pkg/file.py
292  norm_relative=relativePath.replace('\\','/')
293  norm_absolute=absolutePath.replace('\\','/')
294  pos1=norm_relative.rfind('/')
295  if pos1==-1:
296  return
297  relativePath=relativePath[0:pos1] #更新relativatePath
298  pos2=norm_absolute.rfind('/')
299  absolutePath=absolutePath[0:pos2] #更新absolutePath,使其和relativatePath保持一致
300  initPath=f"{absolutePath}/__init__.py"
301  api=lst[0]
302  if os.path.exists(initPath): #判断当前目录中是否有__init__.py
303  currentLevel=relativePath.replace('\\','/').split('/')[-1]
304  cacheKey=(initPath,currentLevel)
305  if importCache is not None and cacheKey in importCache:
306  importDict=importCache[cacheKey]
307  else:
308  try:
309  with tokenize.open(initPath) as f:
310  root=parseLibrarySource(f.read(),initPath)
311  except Exception as e:
312  print(f"shortenPath --> ast.parse failed: {e}")
313  return
314  obj=FromImport(currentLevel)
315  obj.visit(root)
316  importDict=obj.importDict
317  if importCache is not None:
318  importCache[cacheKey]=importDict
319  replaceKey1=''
320  replaceVal1=''
321  replaceKey2=''
322  replaceVal2=''
323  packagePrefix=relativePath.replace('\\','/').replace('/','.')+'.'
324  for key,value in importDict.items():
325  if key[-1]=='*':
326  key=packagePrefix+key[:-1]
327  if api.startswith(key):
328  replaceKey1=key
329  replaceVal1=packagePrefix
330  else:
331  key=packagePrefix+key
332  #只替换当前包下完整的导出路径,避免匹配同名前缀或其他模块
333  if api==key or api.startswith(key+'.'):
334  replaceKey2=key
335  replaceVal2=packagePrefix+value
336  if replaceKey2: #优先使用第二种替换方式
337  api=replaceVal2+api[len(replaceKey2):]
338  elif replaceKey1:
339  api=replaceVal1+api[len(replaceKey1):]
340  lst[0]=api
341  shortenPath(lst,{absolutePath:relativePath},importCache)
342 
343 
344 
345 
354 def getExportMap(filePath):
355  exportMap={}
356  for file in filePath:
357  #只处理包初始化文件,函数内部导入和普通源码文件不属于包重导出
358  if os.path.basename(file)!='__init__.py':
359  continue
360 
361  #根据__init__.py路径得到当前导出包名
362  def2format=Def2format()
363  def2format.toFormat(file)
364  package=def2format.prefix.rsplit('.__init__',1)[0]
365  packageParts=package.split('.')
366  try:
367  with tokenize.open(file) as f:
368  root=parseLibrarySource(f.read(),file)
369  except Exception as e:
370  print(f"getExportMap --> ast.parse failed: {e}")
371  continue
372 
373  #只访问模块顶层节点,避免把函数或类中的局部导入识别为包导出
374  for node in root.body:
375  if not isinstance(node,ast.ImportFrom):
376  continue
377 
378  #将绝对导入和相对导入统一还原为完整源码路径
379  if node.level==0:
380  module=node.module or ''
381  else:
382  parentCount=node.level-1
383  if parentCount>len(packageParts):
384  continue
385  moduleParts=packageParts[:len(packageParts)-parentCount]
386  if node.module:
387  moduleParts.extend(node.module.split('.'))
388  module='.'.join(moduleParts)
389 
390  for name in node.names:
391  #星号导入无法静态确定具体导出对象,暂不处理
392  if name.name=='*':
393  continue
394  sourcePath='.'.join(it for it in (module,name.name) if it)
395  publicPath=f"{package}.{name.asname or name.name}"
396  if sourcePath and sourcePath!=publicPath:
397  exportMap.setdefault(sourcePath,set()).add(publicPath)
398  return exportMap
399 
400 
401 
402 
414 def getApiPaths(apiPath,fileDict,importCache=None,exportMap=None):
415  paths=[apiPath]
416  #保留现有父目录__init__.py路径缩短结果
417  shortenedPath=[apiPath]
418  shortenPath(shortenedPath,fileDict,importCache)
419  if shortenedPath[0] not in paths:
420  paths.append(shortenedPath[0])
421 
422  #按需展开多级重导出,paths同时记录已访问路径以防止循环
423  index=0
424  while index<len(paths):
425  currentPath=paths[index]
426  for publicPath in sorted((exportMap or {}).get(currentPath,set())):
427  if publicPath not in paths:
428  paths.append(publicPath)
429  index+=1
430  return paths
431 
432 
433 
434 
444 def getClass(lst,root,prefix,fileDict, pyiFlag=0, importCache=None, exportMap=None): #lst是传入传出参数
445  className=root.name
446  classPath=f"{prefix}.{className}"
447  #类的导出路径同样适用于其构造方法和普通方法
448  classPaths=getApiPaths(classPath,fileDict,importCache,exportMap)
449  flagInit=0
450  flagNew=0
451  flagCall=0
452  #首先抽取当前类中的所有函数
453  for n in ast.iter_child_nodes(root):
454  #if isinstance(n,ast.FunctionDef):
455  # Add the support of extracting AsyncFunctionDef type node -- 2025/5/19
456  if isinstance(n,(ast.FunctionDef, ast.AsyncFunctionDef)):
457  if 'overload' in ast.unparse(n.decorator_list) and not pyiFlag: #非pyi文件中,遇到带有overload装饰器的就跳过
458  continue
459  funcName=n.name
460  arg=ast.unparse(n.args) #从args节点出解析出函数参数
461  arg=arg.replace(' ','') #去掉字符串中的空格
462  try:
463  ret='->'+ast.unparse(n.returns)
464  except:
465  ret=''
466  if funcName=='__init__': #一个class中也可能存在多个init,即class API的重载
467  init=arg
468  flagInit=1
469  elif funcName=='__new__':
470  new=arg
471  flagNew=1
472  elif funcName=='__call__':
473  call=arg
474  flagCall=1
475  else:
476  methodPath=f"{classPath}.{funcName}"
477  methodPaths=getApiPaths(methodPath,fileDict,importCache,exportMap)
478  for currentClassPath in classPaths:
479  publicMethodPath=f"{currentClassPath}.{funcName}"
480  if publicMethodPath not in methodPaths:
481  methodPaths.append(publicMethodPath)
482  for currentMethodPath in methodPaths:
483  lst.append(f"{currentMethodPath}({arg}){ret}")
484 
485  if flagInit==1:
486  para=f"({init})"
487  specialMethod='__init__'
488  elif flagNew==1:#若class中不含init,再看是否有new
489  para=f"({new})"
490  specialMethod='__new__'
491  elif flagCall==1:
492  para=f"({call})"
493  specialMethod='__call__'
494  else: #若类中不含init,new,call,就将类的继承作为类的参数
495  specialMethod=''
496  pattern=fr"class {re.escape(className)}(\‍(.*?):"
497  codeText=ast.unparse(root)
498  R=RegexMatch(codeText,pattern)
499  flag=R.regex_match()
500  if flag==1:
501  args=R.get_result()
502  else:
503  args=['']
504  para=args[0]
505  for currentClassPath in classPaths:
506  if specialMethod:
507  lst.append(f"{currentClassPath}.{specialMethod}{para}")
508  lst.append(f"{currentClassPath}{para}")
509 
510  #然后再判断当前类节点下是否还有嵌套类,有的话就往下递归
511  prefix+=f".{className}" #更新前缀
512  for n in ast.iter_child_nodes(root):
513  if isinstance(n,ast.ClassDef):
514  getClass(lst,n,prefix,fileDict,pyiFlag,importCache,exportMap)
515 
516 
517 
518 
528 def task(codeText,libApi,prefix,fileDict, pyiFlag=0, importCache=None, exportMap=None): #这里的prefix只到文件名
529  try:
530  rootNode=parseLibrarySource(codeText,list(fileDict.keys())[0])
531  except Exception as e:
532  file = list(fileDict.keys())[0]
533  print(f"{file} ast.parse falied: {e}")
534  return
535  for node in ast.iter_child_nodes(rootNode):
536  if isinstance(node, ast.ClassDef): #抽取类内API
537  getClass(libApi,node,prefix,fileDict,pyiFlag,importCache,exportMap)
538 
539  #if isinstance(node,ast.FunctionDef): #再抽取类外的API
540  # Add the support of extracting AsyncFunctionDef type node -- 2025/5/19
541  if isinstance(node,(ast.FunctionDef, ast.AsyncFunctionDef)):
542  if 'overload' in ast.unparse(node.decorator_list) and not pyiFlag: #遇到含overload装饰器的就跳过
543  continue
544  funcName=node.name
545  arg=ast.unparse(node.args)
546  arg=arg.replace(' ','')
547  try:
548  ret='->'+ast.unparse(node.returns)
549  except:
550  ret=''
551  apiPath=f"{prefix}.{funcName}"
552  for currentPath in getApiPaths(apiPath,fileDict,importCache,exportMap):
553  libApi.append(f"{currentPath}({arg}){ret}")
554 
555 
556 
557 
563 def getPublicAliasLine(line,sourceRoot,publicRoot):
564  if not sourceRoot or not publicRoot:
565  return None
566  sourcePrefix=sourceRoot+'.'
567  publicPrefix=publicRoot+'.'
568  assignSourcePrefix='A:'+sourcePrefix
569  assignPublicPrefix='A:'+publicPrefix
570  if line.startswith(sourcePrefix):
571  return publicPrefix+line[len(sourcePrefix):]
572  if line.startswith(assignSourcePrefix):
573  return assignPublicPrefix+line[len(assignSourcePrefix):]
574  return None
575 
576 
577 
578 
585 def writeApiLine(fw,line,sourceRoot='',publicRoot=''):
586  fw.write(f"{line}\n")
587  aliasLine=getPublicAliasLine(line,sourceRoot,publicRoot)
588  if aliasLine and aliasLine!=line:
589  fw.write(f"{aliasLine}\n")
590 
591 
592 
593 
597 def getDefFunction(args):
598  libName, version, libPath=args
599  fileObj=Path('DF')
600  fileObj.getPath(libPath)
601  #filePath是库下所有文件对应的路径
602  filePath=fileObj.path
603  if not os.path.exists(f"LibAPIExtraction/{libName}"):
604  try:
605  os.mkdir(f"LibAPIExtraction/{libName}") #多进程可能同时执行这句,所以这个结构需要修改
606  except:
607  pass
608  f=open(f'LibAPIExtraction/{libName}/{libName}{version}','w',encoding='UTF-8')
609 
610 
611  fileVisitLst=[]
612  importCache={}
613  #在逐文件抽取定义前统一构建重导出映射,避免对每个API重复扫描
614  exportMap=getExportMap(filePath)
615  publicAliasSource=''
616  publicAliasTarget=''
617  if libName=="tensorflow" and os.path.basename(os.path.normpath(libPath))=="tensorflow_core":
618  publicAliasSource="tensorflow_core"
619  publicAliasTarget="tensorflow"
620  for file in filePath:
621  pyLst=[] #保存每个.py文件中的API
622  pyiLst=[] #保存每个.pyi文件中的API
623  def2format=Def2format()
624  def2format.toFormat(file)
625  prefix=def2format.prefix #前缀,包名.文件名
626  relativePath=def2format.relativePath #相对路径,只从包名开始
627  fileDict={file:relativePath}
628  #对于每个file,首先判断一下他是.py文件还是.pyi文件
629  if file[-1]=='y' and file not in fileVisitLst:
630  fileVisitLst.append(file)
631  #对于每一个.py文件,首先看它有没有.pyi,若无,则直接以.py中的API定义为准
632  #若有,则再抽取.pyi中的API,最后保留.pyi和.py的差集
633  pyiFlag=0
634  if file+'i' not in fileVisitLst: #判断.pyi之前是否访问过
635  try:
636  with tokenize.open(file+'i') as fr:
637  code_text=fr.read()
638  task(code_text,pyiLst,prefix,fileDict, 1, importCache,exportMap) #抽取.pyi中的API
639  pyiFlag=1
640  fileVisitLst.append(file+'i')
641  except FileNotFoundError:
642  pass
643 
644  with tokenize.open(file) as fr:
645  try:
646  code_text=fr.read()
647  except Exception as e:
648  print(f"{file} read failed: {e}")
649  continue
650  try:
651  root_node=parseLibrarySource(code_text,file)
652  except Exception as e:
653  print(f'{file} ast.parse failed: {e}')
654  continue
655  assignDict=getAssign(root_node,file) #抽取.py中的所有Assign Node
656  f.write('\n'+'-' * 40 + f"{file}" + '-' * 40+'\n')
657  for key,val in assignDict.items():
658  writeApiLine(f,f'A:{prefix}.{key}->{val}',publicAliasSource,publicAliasTarget)
659  #抽取.py中的Definition Node
660  task(code_text,pyLst,prefix,fileDict,0,importCache,exportMap)
661  pyLst=sorted(set(pyLst)) #按完整定义去重,保留不同签名
662  for it in pyLst:
663  writeApiLine(f,it,publicAliasSource,publicAliasTarget)
664  f.write('\n')
665 
666  if pyiFlag:
667  removeLst=[]
668  for it1 in pyiLst:
669  for it2 in pyLst:
670  if it2.split('(')[0]==it1.split('(')[0]:
671  removeLst.append(it1)
672  break
673  for it in removeLst:
674  pyiLst.remove(it)
675 
676  #此时.pyi中保存的都是内置的API注释
677  pyiLst=sorted(set(pyiLst))
678  f.write('\n'+'-' * 40 + f"{file}"+'i' + '-' * 40+'\n')
679  for it in pyiLst:
680  writeApiLine(f,it,publicAliasSource,publicAliasTarget)
681  f.write('\n')
682 
683  elif file[-1]=='i' and file not in fileVisitLst:
684  fileVisitLst.append(file)
685  if file.rstrip('i') not in fileVisitLst:
686  try:
687  with tokenize.open(file.rstrip('i')) as fr:
688  code_text=fr.read()
689  task(code_text,pyLst,prefix,fileDict,0,importCache,exportMap) #抽取.py中的API
690  fileVisitLst.append(file.rstrip('i'))
691  root_node=parseLibrarySource(code_text,file.rstrip('i'))
692  assignDict=getAssign(root_node,file.rstrip('i'))
693  f.write('\n'+'-' * 40 + f"{file.rstrip('i')}" + '-' * 40+'\n')
694  for key,value in assignDict.items():
695  writeApiLine(f,f'A:{prefix}.{key}->{value}',publicAliasSource,publicAliasTarget)
696  pyLst=sorted(set(pyLst))
697  for it in pyLst:
698  writeApiLine(f,it,publicAliasSource,publicAliasTarget)
699  f.write('\n')
700  except FileNotFoundError:
701  pass
702 
703  with tokenize.open(file) as fr:
704  code_text=fr.read()
705  task(code_text,pyiLst,prefix,fileDict,1,importCache,exportMap) #抽取.pyi中的API
706  removeLst=[]
707  pyiLst=sorted(set(pyiLst))
708  for it1 in pyiLst:
709  for it2 in pyLst:
710  if it2.split('(')[0]==it1.split('(')[0]:
711  removeLst.append(it1)
712  break
713  for it in removeLst:
714  pyiLst.remove(it)
715 
716  f.write('\n'+'-' * 40 + f"{file}" + '-' * 40+'\n')
717  for it in pyiLst:
718  writeApiLine(f,it,publicAliasSource,publicAliasTarget)
719  f.write('\n')
720 
721  f.close()
Regular expression match class 正则表达式匹配类
Definition: getDef.py:199
def regex_match(self)
Perform the regular expression match 执行正则表达式匹配
Definition: getDef.py:219
def __init__(self, code_text, pattern)
The constructor 构造函数
Definition: getDef.py:205
def get_result(self)
Return the match result 返回匹配结果
Definition: getDef.py:213
def writeApiLine(fw, line, sourceRoot='', publicRoot='')
Write one lib API line and its public alias if needed 写出一行库API,并按需写出公开路径别名
Definition: getDef.py:585
def parseLibrarySource(source, filename='< unknown >')
Parse library source with supported legacy syntax in the Python 3.9 runtime 在Python 3....
Definition: getDef.py:28
def getExportMap(filePath)
Build the re-export mapping of library API definitions 构建库API定义的重导出映射
Definition: getDef.py:354
def task(codeText, libApi, prefix, fileDict, pyiFlag=0, importCache=None, exportMap=None)
Extract all lib API definitions from a source file 抽取库源码API定义任务
Definition: getDef.py:528
def getClass(lst, root, prefix, fileDict, pyiFlag=0, importCache=None, exportMap=None)
Extract class method definitions from a give class.
Definition: getDef.py:444
def shortenPath(lst, fileDict, importCache=None)
Shorten the API path based on init.py and import alias 通过解析__init__.py和import别名,把源码中的部分API路径缩短
Definition: getDef.py:289
def getAssign(root_node, filePath=None)
Extract all assign node from a .py file's AST 通过AST获取.py文件的Assign语句
Definition: getDef.py:242
def getApiPaths(apiPath, fileDict, importCache=None, exportMap=None)
Get all available paths of a library API definition 获取库API定义的所有可用路径
Definition: getDef.py:414
def getDefFunction(args)
Extract all lib API definitions from a specified version 抽取给定版本的库API定义
Definition: getDef.py:597
def getPublicAliasLine(line, sourceRoot, publicRoot)
Return a public alias line for source-root API lines 为源码根路径API行生成公开路径别名
Definition: getDef.py:563