PCART  v1.4
Automated Repair of Python API Parameter Compatibility Issues
tool.py
Go to the documentation of this file.
1 
12 
13 
14 
15 import re
16 import os
17 import ast
18 import json
19 import hashlib
20 import platform
21 import shlex
22 import tokenize
23 from Path.getPath import Path
24 
25 
26 
31 def getArtifactHash(artifactId):
32  hashPattern=re.compile(r'(?:^|__)([0-9a-f]{64})$')
33  match=hashPattern.search(artifactId)
34  if match:
35  return match.group(1)
36  return ''
37 
38 
39 
44 def getArtifactDisplayName(artifactId):
45  artifactHash=getArtifactHash(artifactId)
46  if artifactHash and artifactId.endswith(artifactHash):
47  displayName=artifactId[:-len(artifactHash)].rstrip('_')
48  return displayName or artifactHash
49  return artifactId
50 
51 
52 
58 def shortenArtifactFileName(fileName,extension):
59  fileName=re.sub(r'[^0-9A-Za-z_.-]+','_',fileName).strip('._')
60  length=255-len(extension) if extension else 255
61  if len(fileName)<=length:
62  return fileName+extension
63  artifactHash=getArtifactHash(fileName)
64  if not artifactHash:
65  return fileName[:length]+extension
66  suffix='__'+artifactHash
67  prefixLength=max(0,length-len(suffix))
68  prefix=fileName[:prefixLength].rstrip('_')
69  return prefix+suffix+extension
70 
71 
72 
73 
84 def getParameter(p_string,separator=',',space=1):
85  #库定义的参数去空格,项目中的参数不去空格,防止出问题
86  if space: #默认是去空格的
87  p_string=p_string.replace(' ','') #去掉参数中的空格
88 
89  if p_string=='':
90  return []
91 
92  parameters=[]
93  stack=[]
94  count_left_min=0 #统计'('的个数
95  count_right_min=0 #统计')'的个数
96 
97  count_left_middle=0 #统计'['的个数
98  count_right_middle=0 #统计']'的个数
99 
100  count_left_hua=0 #统计'{'的个数
101  count_right_hua=0 #统计'}'的个数
102 
103  count_single_yinhao=0 #统计单引号的引号的个数
104  count_double_yinhao=0 #统计双引号的引号的个数
105 
106  for index,value in enumerate(p_string):
107  stack.append(value)
108  if (value=="'" or count_single_yinhao) and not count_double_yinhao: #若上一步出现了双引号,则说明此处的单引号是在双引号内的,所以不计算单引号的个数
109  if value=="'":
110  count_single_yinhao+=1
111  if count_single_yinhao&1:
112  continue
113 
114  elif (value=='"' or count_double_yinhao) and not count_single_yinhao: #若上一步出现了单引号,则说明此处的双引号是在单引号内的,所以不计算双引号的个数
115  if value=='"':
116  count_double_yinhao+=1
117  if count_double_yinhao&1:
118  continue
119 
120  count_single_yinhao=0 #重置为0
121  count_double_yinhao=0
122 
123  #只计算引号之外的括号是否成对出现
124  if value=='(':
125  count_left_min+=1
126  elif value==')':
127  count_right_min+=1
128 
129  elif value=='[':
130  count_left_middle+=1
131  elif value==']':
132  count_right_middle+=1
133 
134  elif value=='{':
135  count_left_hua+=1
136  elif value=='}':
137  count_right_hua+=1
138 
139 
140  #弹栈,遇到分隔符或达到字符串末尾
141  if value==separator:
142  flagMin=1 #假设左右括号的个数都是相等的
143  flagMid=1
144  flagHua=1
145  if '(' in stack:
146  if count_left_min!=count_right_min:
147  flagMin=0
148  if '[' in stack:
149  if count_left_middle!=count_right_middle:
150  flagMid=0
151  if '{' in stack:
152  if count_left_hua!=count_right_hua:
153  flagHua=0
154 
155  if flagMin and flagMid and flagHua:
156  parameters.append(''.join(stack[0:-1]))
157  stack.clear()
158 
159  elif index==len(p_string)-1:
160  parameters.append(''.join(stack))
161 
162 
163  return parameters
164 
165 
166 
172 def getAst(filePath,strFlag=0): #若strFlag=1,则表明传进来的是一个api,而不是一个路径
173  if strFlag==0:
174  with tokenize.open(filePath) as f:
175  s=f.read()
176  root=ast.parse(s,filename='<unknown>',mode='exec')
177  return root
178  root=ast.parse(filePath,filename='<unknown>',mode='exec')
179  return root
180 
181 
182 
183 
188 def getImportLst(filePath):
189  importLst=[]
190  with open(filePath,'r',encoding='UTF-8') as f:
191  lstR=f.readlines()
192  for it in lstR:
193  #这个判断条件在后续应该将其设置为通用的,不同的库判断条件不同
194  if (it[0:6]=='import' or it[0:4]=='from') and 'torch' in it and 'torchvision' not in it and 'torch_' not in it and '.torch' not in it:
195  importLst.append(it)
196  return importLst
197 
198 
199 
200 
209 def removeParameter(s,flag=0):
210  if '->' in s: #若有返回值,则把返回值也去掉
211  s=s.split('->')[0]
212  if flag==0: #去掉API中所有参数
213  stack=[]
214  left=0
215  right=0
216  ans=''
217  for index,value in enumerate(s):
218  #进栈
219  stack.append(value)
220  if value=='(':
221  left+=1
222  if value==')':
223  right+=1
224  #出栈
225  if left==right and left>0 and right>0:
226  pos=stack.index('(')
227  ans+=''.join(stack[0:pos])
228  stack.clear()
229  left=0
230  right=0
231  elif index==len(s)-1:
232  ans+=''.join(stack)
233  else: #只去除最后一个API的参数
234  i=len(s)-1
235  left=0 #记录左括号的个数
236  right=0
237  pos=len(s)
238  while i>=0:
239  if s[i]==')':
240  right+=1
241  if s[i]=='(':
242  left+=1
243  if left==right and left>0 and right>0:
244  pos=i #更新pos
245  break
246  i-=1
247  ans=s[0:pos]
248 
249  return ans
250 
251 
252 
253 
262  ans=''
263  left=0 #记录左括号的个数
264  right=0 #记录右括号的个数
265  pos=len(apiStr)
266  i=len(apiStr)-1
267  while i>=0:
268  if apiStr[i]==')':
269  right+=1
270  if apiStr[i]=='(':
271  left+=1
272  if left==right and left>0 and right>0:
273  pos=i
274  break
275  i-=1
276  if pos!=len(apiStr):
277  ans=apiStr[pos+1:-1]
278  return ans
279 
280 
281 
282 
291 def departAPI(s):
292  ansLst=[]
293  stack=''
294  leftMin=0 #记录左'('的个数
295  rightMin=0
296  leftMid=0
297  rightMid=0
298  for i in range(len(s)):
299  stack+=s[i]
300  if s[i]=='(':
301  leftMin+=1
302  if s[i]==')':
303  rightMin+=1
304  if s[i]=='[':
305  leftMid+=1
306  if s[i]==']':
307  rightMid+=1
308 
309  flagMid=1
310  if '[' in stack:
311  if leftMid!=rightMid:
312  flagMid=0
313 
314  #拆分函数字符串里面必须要出现()
315  if leftMin and rightMin and leftMin==rightMin and flagMid:
316  ansLst.append(stack[0:i+1])
317  leftMin=0
318  rightMin=0
319  if leftMid and rightMid:
320  leftMin=0
321  rightMid=0
322 
323  elif i==len(s)-1:
324  ansLst.append(stack[0:i+1])
325 
326  return ansLst
327 
328 
329 
330 
339 def departAPI2(s,separator='.'):
340  ansLst=[]
341  lst=[]
342  count_left_min=0 #统计左'('的个数
343  count_right_min=0 #统计右')'的个数
344 
345  count_left_middle=0 #统计左'['的个数
346  count_right_middle=0 #统计左']'的个数
347  for index,value in enumerate(s):
348  #入栈,分两种情况
349  if value!=separator:
350  lst.append(value)
351  if value=='(':
352  count_left_min+=1
353  if value==')':
354  count_right_min+=1
355  if value=='[':
356  count_left_middle+=1
357  if value==']':
358  count_right_middle+=1
359 
360  elif value==separator and ((count_left_min>count_right_min) or (count_left_middle>count_right_middle)):
361  lst.append(value)
362 
363  #弹栈,分三种情况
364  if value==separator:
365  flagMin=1 #假设左右括号的个数都是相等的
366  flagMid=1
367  if '(' in lst:
368  if count_left_min!=count_right_min:
369  flagMin=0
370  if '[' in lst:
371  if count_left_middle!=count_right_middle:
372  flagMid=0
373  if flagMin and flagMid:
374  ansLst.append(''.join(lst))
375  lst.clear()
376  elif index==len(s)-1:
377  ansLst.append(''.join(lst))
378 
379  return ansLst
380 
381 
382 
383 
388 def isDynamic(dic):
389  lst=list(dic.values())
390  for it in lst:
391  if isinstance(it,str):
392  return 1
393  return 0 #空字典也看作是模糊匹配的字典
394 
395 
396 
397 
402 def getVersionLst(libPath):
403  obj=Path('D')
404  versionLst=[]
405  obj.getPath(libPath)
406  path=obj.path
407  for p in path:
408  p = os.path.basename(p)
409  index=-1
410  for i in range(len(p)):
411  if p[i].isdigit():
412  index=i
413  break
414  versionLst.append(p[index:])
415  versionLst.sort(key=lambda it:cmp(it))
416  return versionLst
417 
418 
419 
420 
430 def cmp(version):
431  index=len(version)
432  innerVersion=""
433  for i in range(len(version)):
434  if version[i].isalpha():
435  index=i
436  break
437  if index!=len(version):
438  innerVersion=re.findall(r'\d+',version[index:])[0]
439  Type=re.findall(r'[a-zA-Z]+',version[index:])[0] #type='rc' or type='b' or type='a'
440  if Type=='a': #alpha版
441  if len(innerVersion)==1:
442  innerVersion='0.00000'+innerVersion
443  else:
444  innerVersion='0.0000'+innerVersion #为了确保a10<b1
445  elif Type=='b':#beta版
446  if len(innerVersion)==1:
447  innerVersion='0.000'+innerVersion
448  else:
449  innerVersion='0.00'+innerVersion #为了确保b10<rc1
450  elif Type=='rc': #release版
451  if len(innerVersion)==1:
452  innerVersion='0.0'+innerVersion
453  else:
454  innerVersion='0.'+innerVersion
455 
456 
457  lst=version[0:index].split('.')
458  if len(lst)==2:
459  lst.append('0')
460  if len(lst[1])==1:
461  lst[1]='0'+lst[1]
462  if len(lst[2])==1:
463  lst[2]='0'+lst[2]
464  v=''.join(lst)
465  if innerVersion=="":
466  return float(v)
467  else:
468  return float(v)-(1-float(innerVersion)) #转化为浮点数形式
469 
470 
471 
472 
478 def getFileName(fileName,extension):
479  if getArtifactHash(fileName):
480  return shortenArtifactFileName(fileName,extension)
481  #step1:先把fileName中的非法字符去除
482  fileName=fileName.replace(' ','')
483  fileName=fileName.replace('/','')
484  fileName=fileName.replace('\\','')
485  if len(extension) != 0:
486  length=255-len(extension)
487  #if len(fileName)>length:
488  #fileName=fileName[0:length] #如果超出了长度,就进行截断
489  fileName=fileName.split('(')[0] + '_' + hashlib.md5(fileName.encode()).hexdigest()[:16] # 2025.7.18 More robust file name processing
490  fileName=fileName[0:length]
491 
492  fileName+=extension
493  return fileName
494 
495 
496 
497 
506 def writeLine(width,s,fw):
507  contentWidth=width-4
508  for start in range(0,max(len(s),1),contentWidth):
509  content=s[start:start+contentWidth]
510  tailSpaceNum=width-2-len(content)-1
511  fw.write('| '+content+' '*tailSpaceNum+'|'+'\n')
512 
513 
514 
515 
522 def save2txt(lst,libName,runCommand,savePath):
523  fw=open(savePath,'w',encoding='UTF-8')
524  totalFileNum=0
525  totalAPINum=0
526  compatibleNum=0
527  incompatibleNum=0
528  unknownCompatibleNum=0
529  notCoverNum=0
530  successRepairNum=0
531  failedRepairNum=0
532  unknownRepairNum=0
533  for Tuple in lst: #计数
534  totalFileNum+=1
535  totalAPINum+=Tuple[2]
536  dic=Tuple[0]
537  for callAPI,subDict in dic.items():
538  if subDict['Coverage']=='No':
539  notCoverNum+=1
540  continue
541 
542  if subDict['Compatible']=='Yes':
543  compatibleNum+=1
544  elif subDict['Compatible']=='No':
545  incompatibleNum+=1
546  else:
547  unknownCompatibleNum+=1
548 
549  if 'Repair <Successful>' in subDict:
550  successRepairNum+=1
551  elif 'Repair <Failed>' in subDict:
552  failedRepairNum+=1
553  elif 'Repair <Unknown>' in subDict:
554  unknownRepairNum+=1
555 
556  #写结果
557  libName=libName.capitalize()
558  fw.write(f"Run Command: {runCommand}\n")
559  fw.write(f"Total File Number: {totalFileNum}\n")
560  fw.write(f"Total {libName} Invoked API Number: {totalAPINum}\n")
561  fw.write(f"Not Covered {libName} Invoked API Number: {notCoverNum}/{totalAPINum}\n")
562  fw.write(f"Covered {libName} Invoked API Number: {totalAPINum-notCoverNum}/{totalAPINum}\n\n")
563 
564  fw.write(f"Compatible {libName} Invoked API Number: {compatibleNum}/{totalAPINum-notCoverNum}\n")
565  fw.write(f"Unknown Compatible {libName} Invoked API Number: {unknownCompatibleNum}/{totalAPINum-notCoverNum}\n\n")
566 
567  fw.write(f"Incompatible {libName} Invoked API Number: {incompatibleNum}/{totalAPINum-notCoverNum}\n")
568  fw.write(f"-> Successfully Repaired {libName} Invoked API number: {successRepairNum}/{incompatibleNum}\n")
569  fw.write(f"-> Failed to Repair {libName} Invoked API Number: {failedRepairNum}/{incompatibleNum}\n")
570  fw.write(f"-> Unknown Repair Status {libName} Invoked API Number: {unknownRepairNum}/{incompatibleNum}\n\n")
571  fileCount=0
572  for Tuple in lst:
573  dic=Tuple[0]
574  filePath=Tuple[1]
575  invokedAPINum=Tuple[2]
576  fileCount+=1
577  width=102 #设置列表总宽度175个字符
578  title=f"File #{fileCount}: {filePath} has {invokedAPINum} {libName}-Invoked API(s)"
579  line='='*width+'\n'
580  fontSpaceNum=(width-len(title)-2)//2
581  tailSpaceNum=width-2-fontSpaceNum-len(title)
582  title='|'+' '*fontSpaceNum+title+' '*tailSpaceNum+'|'+'\n'
583  fw.write(line)
584  fw.write(title)
585  fw.write(line)
586  index=1
587  for callAPI,subDict in dic.items():
588  displayCall=subDict.pop('Invoked API',callAPI)
589  tempStr1=f"Invoked API #{index}: {displayCall}"
590  writeLine(width,tempStr1,fw)
591  fw.write('|'+' '*(width-2)+'|'+'\n')
592  cnt=1
593  for k,v in subDict.items():
594  tempStr2=f"{k}: {v}"
595  writeLine(width,tempStr2,fw)
596  if cnt!=len(subDict):
597  fw.write('|'+' '*(width-2)+'|'+'\n')
598  cnt+=1
599 
600  fw.write('|'+' '*(width-2)+'|'+'\n')
601  fw.write('|'+'-'*(width-2)+'|'+'\n')
602  if index!=len(dic):
603  fw.write('|'+' '*(width-2)+'|'+'\n')
604  index+=1
605  fw.write('\n\n')
606 
607  fw.close()
608 
609 
610 
611 
616 def loadConfig(configPath):
617  with open(configPath,'r',encoding='UTF-8') as fr:
618  dic=json.load(fr)
619  for key in dic:
620  if isinstance(dic[key], str):
621  dic[key] = dic[key].strip()
622  runCommand=dic['runCommand']
623  return dic['projPath'],runCommand,dic['runFilePath'],dic['libName'],dic['currentVersion'],dic['targetVersion'],dic['currentEnv'],dic['targetEnv']
624 
625 
626 
632 def resolveConfigFilePath(config,repoRoot):
633  if os.path.isabs(config):
634  return config
635  if os.path.exists(config):
636  return os.path.abspath(config)
637  return os.path.join(repoRoot,'Configure',config)
638 
639 
640 
646 def resolveConfigValuePath(repoRoot,path):
647  if not path:
648  return path
649  expanded=os.path.expanduser(path)
650  if os.name=='nt' and expanded.startswith('/') and not expanded.startswith('//'):
651  return expanded
652  if os.path.isabs(expanded):
653  return os.path.abspath(expanded)
654  return os.path.abspath(os.path.join(repoRoot,expanded))
655 
656 
657 
661 class UnsupportedRunCommand(Exception):
662  """Raised when PCART cannot safely execute a configured run command."""
663 
664 
665 
670 def isPythonExecutable(command):
671  baseName=os.path.basename(command.replace('\\','/')).lower()
672  if baseName in ('python','python.exe'):
673  return True
674  if re.match(r'^python3(\.\d+)?(\.exe)?$',baseName):
675  return True
676  return False
677 
678 
679 
684 def isPyLauncher(command):
685  baseName=os.path.basename(command.replace('\\','/')).lower()
686  return baseName in ('py','py.exe')
687 
688 
689 
695  if tokens and re.match(r'^-\d(\.\d+)?(-\d+)?$',tokens[0]):
696  return tokens[1:]
697  return tokens
698 
699 
700 
707 def normalizeRunCommand(runCommand):
708  tokens=shlex.split(runCommand)
709  if not tokens:
710  return runCommand,'python'
711 
712  # 兼容未加引号的Windows Python路径,避免shlex处理反斜杠
713  simpleTokens=runCommand.split(maxsplit=1)
714  if simpleTokens and isPythonExecutable(simpleTokens[0]):
715  rest=simpleTokens[1].lstrip() if len(simpleTokens)>1 else ''
716  return rest,'python'
717  if simpleTokens and isPyLauncher(simpleTokens[0]):
718  rest=simpleTokens[1].lstrip() if len(simpleTokens)>1 else ''
719  restTokens=shlex.split(rest)
720  return shlex.join(stripPyLauncherOption(restTokens)),'python'
721 
722  if isPythonExecutable(tokens[0]):
723  return shlex.join(tokens[1:]),'python'
724  if isPyLauncher(tokens[0]):
725  return shlex.join(stripPyLauncherOption(tokens[1:])),'python'
726  if tokens[0]=='-m' or tokens[0].endswith('.py'):
727  return runCommand,'python'
728  return runCommand,'console'
729 
730 
731 
737 def resolveConsoleExecutable(envPath,commandName):
738  normalizedEnvPath=os.path.abspath(envPath)
739  candidates=[]
740  if platform.system()=='Windows':
741  scriptsPath=os.path.join(normalizedEnvPath,'Scripts')
742  candidates.extend([
743  os.path.join(scriptsPath,commandName),
744  os.path.join(scriptsPath,commandName+'.exe'),
745  os.path.join(scriptsPath,commandName+'.cmd'),
746  os.path.join(scriptsPath,commandName+'.bat'),
747  ])
748  else:
749  candidates.append(os.path.join(normalizedEnvPath,'bin',commandName))
750 
751  for candidate in candidates:
752  if os.path.exists(candidate):
753  return candidate
754 
755  candidateStr=', '.join(candidates)
756  raise FileNotFoundError(f"Cannot find console command '{commandName}' under virtual " f"environment root: {normalizedEnvPath}. Tried: {candidateStr}")
757 
758 
759 
765 def buildRunCommand(runCommand,envPath):
766  normalizedCommand,commandType=normalizeRunCommand(runCommand)
767  if commandType=='python':
768  return [resolvePythonExecutable(envPath)] + shlex.split(normalizedCommand)
769 
770  args=shlex.split(normalizedCommand)
771  if not args:
772  raise UnsupportedRunCommand('Empty run command')
773  try:
774  commandPath=resolveConsoleExecutable(envPath,args[0])
775  except FileNotFoundError as e:
776  raise UnsupportedRunCommand(str(e)) from e
777  return [commandPath] + args[1:]
778 
779 
780 
785 def getRunFile(runCommand):
786  tokens=shlex.split(runCommand)
787  for token in tokens:
788  candidate=token.split('::',1)[0]
789  if candidate.endswith('.py'):
790  return candidate
791  return ''
792 
793 
794 
795 
800 def findPythonDir(basePath):
801  if not os.path.exists(basePath):
802  raise FileNotFoundError(f"Cannot find Python directory: {basePath} does not exist")
803 
804  for entry in os.listdir(basePath):
805  full_path = os.path.join(basePath, entry)
806  if os.path.isdir(full_path):
807  if entry.startswith("python"):
808  return full_path
809 
810  raise FileNotFoundError(f"Cannot find Python directory under {basePath}/pythonxx.xx")
811 
812 
813 
819 def resolveLibSourceCodePath(envPath,libName):
820  if platform.system() == 'Windows':
821  sitePackages=os.path.join(envPath, "Lib", "site-packages")
822  else:
823  sitePackages=os.path.join(findPythonDir(os.path.join(envPath, "lib")), "site-packages")
824 
825  if libName=="tensorflow":
826  tensorflowCore=os.path.join(sitePackages, "tensorflow_core")
827  if os.path.isdir(tensorflowCore):
828  return tensorflowCore
829 
830  return os.path.join(sitePackages, libName)
831 
832 
833 
839  normalizedEnvPath = os.path.abspath(envPath)
840  if platform.system() == 'Windows':
841  candidates = [
842  os.path.join(normalizedEnvPath, 'python.exe'),
843  os.path.join(normalizedEnvPath, 'Scripts', 'python.exe'),
844  ]
845  else:
846  candidates = [
847  os.path.join(normalizedEnvPath, 'bin', 'python'),
848  ]
849 
850  for candidate in candidates:
851  if os.path.exists(candidate):
852  return candidate
853 
854  candidate_str = ', '.join(candidates)
855  raise FileNotFoundError(f"Cannot find Python executable under virtual environment root: " f"{normalizedEnvPath}. Tried: {candidate_str}")
856 
857 
858 
866 def getSourceCodePath(configPath,repoRoot=None):
867  if repoRoot is None:
868  repoRoot=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
869  resolvedConfigPath=resolveConfigFilePath(configPath,repoRoot)
870  with open(resolvedConfigPath,'r',encoding='UTF-8') as fr:
871  dic=json.load(fr)
872  libName=dic['libName']
873  currentVersion=dic['currentVersion']
874  targetVersion=dic['targetVersion']
875  currentEnvPath=resolveConfigValuePath(repoRoot,dic['currentEnv'])
876  targetEnvPath=resolveConfigValuePath(repoRoot,dic['targetEnv'])
877 
878  currentSourceCodePath=resolveLibSourceCodePath(currentEnvPath, libName)
879  targetSourceCodePath=resolveLibSourceCodePath(targetEnvPath, libName)
880 
881  return currentVersion, targetVersion, currentSourceCodePath, targetSourceCodePath
882 
883 
884 
885 
887 class ConditionalReturnTransformer(ast.NodeTransformer):
888 
892  def visit_Return(self, node):
893  #检查return语句是否为单行条件语句(IfExp)
894  if isinstance(node, ast.Return) and isinstance(node.value, ast.IfExp):
895  ifExp = node.value
896  newIf = ast.If(test=ifExp.test,body=[ast.Return(value=ifExp.body)],orelse=[ast.Return(value=ifExp.orelse)])
897  return newIf
898  return node
Convert conditional return statement to multi-line if-else structure 展开单行条件返回语句为多行if-else结构
Definition: tool.py:887
def visit_Return(self, node)
Transform single-line conditional return into multi-line if-else structure 将单行条件返回语句转换为多行if-else结构
Definition: tool.py:892
Unsupported run command exception 不支持的运行命令异常
Definition: tool.py:661
def buildRunCommand(runCommand, envPath)
Build subprocess argv for project run command 为被测项目运行命令构造subprocess argv.
Definition: tool.py:765
def removeParameter(s, flag=0)
Remove parameter(s) from API call string 去掉API中的参数部分
Definition: tool.py:209
def save2txt(lst, libName, runCommand, savePath)
Save PCART report 保存PCART报告
Definition: tool.py:522
def getAst(filePath, strFlag=0)
Get AST for code 将代码转化为Ast树
Definition: tool.py:172
def getArtifactDisplayName(artifactId)
Return readable artifact display name 返回可读运行产物展示名
Definition: tool.py:44
def getImportLst(filePath)
Extract import statements from a source file (currently torch-specific) 从源码文件中提取import语句(目前仅筛选torch相关...
Definition: tool.py:188
def resolveLibSourceCodePath(envPath, libName)
Resolve the source package directory for a library in a virtual environment 从虚拟环境中解析库源码目录
Definition: tool.py:819
def shortenArtifactFileName(fileName, extension)
Shorten artifact file name while preserving full hash 缩短运行产物文件名并保留完整hash.
Definition: tool.py:58
def cmp(version)
Normalize and compare version strings 归一化并比较版本号字符串
Definition: tool.py:430
def getSourceCodePath(configPath, repoRoot=None)
Get source code path of the lib 获取库源码路径
Definition: tool.py:866
def getVersionLst(libPath)
Get all version numbers from a library package directory 给定库的路径,获得该库的所有版本号
Definition: tool.py:402
def getFileName(fileName, extension)
Normalize file name 给文件取名字
Definition: tool.py:478
def resolveConfigValuePath(repoRoot, path)
Resolve path value loaded from PCART config 解析PCART配置字段中的路径值
Definition: tool.py:646
def getParameter(p_string, separator=',', space=1)
Split parameter string into list of separated parameters 将参数字符串拆分成单个的参数
Definition: tool.py:84
def writeLine(width, s, fw)
Format output 格式化输出
Definition: tool.py:506
def isDynamic(dic)
Check if a match dictionary contains dynamic (string) match results 检查匹配字典是否包含动态(字符串)匹配结果
Definition: tool.py:388
def resolveConfigFilePath(config, repoRoot)
Resolve PCART config file path 解析PCART配置文件路径
Definition: tool.py:632
def resolveConsoleExecutable(envPath, commandName)
Resolve console script executable from a virtual environment 从虚拟环境中解析console script可执行文件
Definition: tool.py:737
def isPyLauncher(command)
Check whether command is Windows py launcher 判断命令是否为Windows py启动器
Definition: tool.py:684
def departAPI2(s, separator='.')
Split API call string based on separator "." 根据"."拆分API调用字符串
Definition: tool.py:339
def loadConfig(configPath)
Load PCART's configuration file 加载PCART配置文件
Definition: tool.py:616
def departAPI(s)
Split API call string based on parameter passing 根据参数传递拆分API调用字符串
Definition: tool.py:291
def resolvePythonExecutable(envPath)
Resolve Python executable from a virtual environment root 从虚拟环境根目录解析 Python 解释器路径
Definition: tool.py:838
def normalizeRunCommand(runCommand)
Normalize project run command 归一化被测项目运行命令
Definition: tool.py:707
def getArtifactHash(artifactId)
Return artifact hash from an artifact id 从运行产物id中提取hash.
Definition: tool.py:31
def getRunFile(runCommand)
Extract the .py script filename from a run command 从运行命令中提取.py脚本文件名
Definition: tool.py:785
def isPythonExecutable(command)
Check whether command is a Python executable name/path 判断命令是否为Python解释器名称或路径
Definition: tool.py:670
def getLastAPIParameter(apiStr)
Get parameter(s) of the last API from the API call string 获取最后一个API参数
Definition: tool.py:261
def stripPyLauncherOption(tokens)
Remove version option after py launcher 去除py启动器后的版本选项
Definition: tool.py:694
def findPythonDir(basePath)
Get Python interpreter path 获取Python解释器路径
Definition: tool.py:800