CMS 3D CMS Logo

runTheMatrix.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 from __future__ import print_function
3 import sys, os
4 
5 from Configuration.PyReleaseValidation.MatrixReader import MatrixReader
6 from Configuration.PyReleaseValidation.MatrixRunner import MatrixRunner
7 from Configuration.PyReleaseValidation.MatrixInjector import MatrixInjector,performInjectionOptionTest
8 
9 # ================================================================================
10 
11 def showRaw(opt):
12 
13  mrd = MatrixReader(opt)
14  mrd.showRaw(opt.useInput, opt.refRel, opt.fromScratch, opt.raw, opt.step1Only, selected=opt.testList)
15 
16  return 0
17 
18 # ================================================================================
19 
20 def runSelected(opt):
21 
22  mrd = MatrixReader(opt)
23  mrd.prepare(opt.useInput, opt.refRel, opt.fromScratch)
24 
25  # test for wrong input workflows
26  if opt.testList:
27  definedWf = [dwf.numId for dwf in mrd.workFlows]
28  definedSet = set(definedWf)
29  testSet = set(opt.testList)
30  undefSet = testSet - definedSet
31  if len(undefSet)>0: raise ValueError('Undefined workflows: '+', '.join(map(str,list(undefSet))))
32  duplicates = [wf for wf in testSet if definedWf.count(wf)>1 ]
33  if len(duplicates)>0: raise ValueError('Duplicated workflows: '+', '.join(map(str,list(duplicates))))
34 
35  ret = 0
36  if opt.show:
37  mrd.show(opt.testList, opt.extended, opt.cafVeto)
38  if opt.testList : print('testListected items:', opt.testList)
39  else:
40  mRunnerHi = MatrixRunner(mrd.workFlows, opt.nProcs, opt.nThreads)
41  ret = mRunnerHi.runTests(opt)
42 
43  if opt.wmcontrol:
44  if ret!=0:
45  print('Cannot go on with wmagent injection with failing workflows')
46  else:
47  wfInjector = MatrixInjector(opt,mode=opt.wmcontrol,options=opt.wmoptions)
48  ret= wfInjector.prepare(mrd,
49  mRunnerHi.runDirs)
50  if ret==0:
51  wfInjector.upload()
52  wfInjector.submit()
53  return ret
54 
55 # ================================================================================
56 
57 if __name__ == '__main__':
58 
59  #this can get out of here
60  predefinedSet={
61  'limited' : [5.1, #FastSim ttbar
62  7.3, #CosmicsSPLoose_UP17
63  8, #BH/Cosmic MC
64  25, #MC ttbar
65  4.22, #cosmic data
66  4.53, #run1 data + miniAOD
67  9.0, #Higgs200 charged taus
68  1000, #data+prompt
69  1001, #data+express
70  101.0, #SingleElectron120E120EHCAL
71  136.731, #2016B Photon data
72  136.7611, #2016E JetHT reMINIAOD from 80X legacy
73  136.8311, #2017F JetHT reMINIAOD from 94X reprocessing
74  136.88811,#2018D JetHT reMINIAOD from UL processing
75  136.793, #2017C DoubleEG
76  136.874, #2018C EGamma
77  138.4, #2021 MinimumBias prompt reco
78  138.5, #2021 MinimumBias express
79  139.001, #2021 MinimumBias offline with HLT step
80  140.53, #2011 HI data
81  140.56, #2018 HI data
82  158.01, #reMiniAOD of 2018 HI MC with pp-like reco
83  312.0, #2021/Run3 HI MC Pyquen_ZeemumuJets_pt10 with pp-like reco
84  1306.0, #SingleMu Pt1 UP15
85  2500.601, #test NanoAOD from existing MINI
86  1330, #Run2 2015/2016 MC Zmm
87  135.4, #Run 2 2015/2016 Zee ttbar fastsim
88  10042.0, #2017 ZMM
89  10024.0, #2017 ttbar
90  10824.0, #2018 ttbar
91  2018.1, #2018 ttbar fastsim
92  11634.911, #2021 DD4hep ttbar reading geometry from XML
93  11634.914, #2021 DDD ttbar reading geometry from the DB
94  11634.0, #2021 ttbar (switching to DD4hep by default)
95  11634.7, #2021 ttbar mkFit
96  11834.0, #2021 ttbar PU
97  13234.0, #2021 ttbar fastsim
98  13434.0, #2021 ttbar PU fastsim
99  12434.0, #2023 ttbar
100  23634.0, #2026D95 ttbar (Phase-2 baseline)
101  23634.911, #2026D95 ttbar DD4hep XML
102  23834.999, #2026D95 ttbar premixing stage1+stage2, PU50
103  23696.0, #CE_E_Front_120um D95
104  23700.0, #CE_H_Coarse_Scint D95
105  23234.0, #2026D94 ttbar (exercise with HFNose)
106  25202.0, #2016 ttbar UP15 PU
107  250202.181, #2018 ttbar stage1 + stage2 premix
108  ],
109  'jetmc': [5.1, 13, 15, 25, 38, 39], #MC
110  'metmc' : [5.1, 15, 25, 37, 38, 39], #MC
111  'muonmc' : [5.1, 124.4, 124.5, 20, 21, 22, 23, 25, 30], #MC
112  }
113 
114 
115  import argparse
116  usage = 'usage: runTheMatrix.py --show -s '
117 
118  parser = argparse.ArgumentParser(usage,formatter_class=argparse.ArgumentDefaultsHelpFormatter)
119 
120  parser.add_argument('-b','--batchName',
121  help='relval batch: suffix to be appended to Campaign name',
122  dest='batchName',
123  default='')
124 
125  parser.add_argument('-m','--memoryOffset',
126  help='memory of the wf for single core',
127  dest='memoryOffset',
128  type=int,
129  default=3000)
130 
131  parser.add_argument('--addMemPerCore',
132  help='increase of memory per each n > 1 core: memory(n_core) = memoryOffset + (n_core-1) * memPerCore',
133  dest='memPerCore',
134  type=int,
135  default=1500)
136 
137  parser.add_argument('-j','--nproc',
138  help='number of processes. 0 Will use 4 processes, not execute anything but create the wfs',
139  dest='nProcs',
140  type=int,
141  default=4)
142 
143  parser.add_argument('-t','--nThreads',
144  help='number of threads per process to use in cmsRun.',
145  dest='nThreads',
146  type=int,
147  default=1)
148 
149  parser.add_argument('--nStreams',
150  help='number of streams to use in cmsRun.',
151  dest='nStreams',
152  type=int,
153  default=0)
154 
155  parser.add_argument('--numberEventsInLuminosityBlock',
156  help='number of events in a luminosity block',
157  dest='numberEventsInLuminosityBlock',
158  type=int,
159  default=-1)
160 
161  parser.add_argument('-n','--showMatrix',
162  help='Only show the worflows. Use --ext to show more',
163  dest='show',
164  default=False,
165  action='store_true')
166 
167  parser.add_argument('-e','--extended',
168  help='Show details of workflows, used with --show',
169  dest='extended',
170  default=False,
171  action='store_true')
172 
173  parser.add_argument('-s','--selected',
174  help='Run a pre-defined selected matrix of wf. Deprecated, please use -l limited',
175  dest='restricted',
176  default=False,
177  action='store_true')
178 
179  parser.add_argument('-l','--list',
180  help='Comma separated list of workflow to be shown or ran. Possible keys are also '+str(predefinedSet.keys())+'. and wild card like muon, or mc',
181  dest='testList',
182  default=None)
183 
184  parser.add_argument('-f','--failed-from',
185  help='Provide a matrix report to specify the workflows to be run again. Augments the -l option if specified already',
186  dest='failed_from',
187  default=None)
188 
189  parser.add_argument('-r','--raw',
190  help='Temporary dump the .txt needed for prodAgent interface. To be discontinued soon. Argument must be the name of the set (standard, pileup,...)',
191  dest='raw')
192 
193  parser.add_argument('-i','--useInput',
194  help='Use recyling where available. Either all, or a comma separated list of wf number.',
195  dest='useInput',
196  type=lambda x: x.split(','),
197  default=None)
198 
199  parser.add_argument('-w','--what',
200  help='Specify the set to be used. Argument must be the name of a set (standard, pileup,...) or multiple sets separated by commas (--what standard,pileup )',
201  dest='what',
202  default='all')
203 
204  parser.add_argument('--step1',
205  help='Used with --raw. Limit the production to step1',
206  dest='step1Only',
207  default=False)
208 
209  parser.add_argument('--maxSteps',
210  help='Only run maximum on maxSteps. Used when we are only interested in first n steps.',
211  dest='maxSteps',
212  default=9999,
213  type=int)
214 
215  parser.add_argument('--fromScratch',
216  help='Comma separated list of wf to be run without recycling. all is not supported as default.',
217  dest='fromScratch',
218  type=lambda x: x.split(','),
219  default=None)
220 
221  parser.add_argument('--refRelease',
222  help='Allow to modify the recycling dataset version',
223  dest='refRel',
224  default=None)
225 
226  parser.add_argument('--wmcontrol',
227  help='Create the workflows for injection to WMAgent. In the WORKING. -wmcontrol init will create the the workflows, -wmcontrol test will dryRun a test, -wmcontrol submit will submit to wmagent',
228  choices=['init','test','submit','force'],
229  dest='wmcontrol',
230  default=None)
231 
232  parser.add_argument('--revertDqmio',
233  help='When submitting workflows to wmcontrol, force DQM outout to use pool and not DQMIO',
234  choices=['yes','no'],
235  dest='revertDqmio',
236  default='no')
237 
238  parser.add_argument('--optionswm',
239  help='Specify a few things for wm injection',
240  default='',
241  dest='wmoptions')
242 
243  parser.add_argument('--keep',
244  help='allow to specify for which comma separated steps the output is needed',
245  default=None)
246 
247  parser.add_argument('--label',
248  help='allow to give a special label to the output dataset name',
249  default='')
250 
251  parser.add_argument('--command',
252  help='provide a way to add additional command to all of the cmsDriver commands in the matrix',
253  dest='command',
254  action='append',
255  default=None)
256 
257  parser.add_argument('--apply',
258  help='allow to use the --command only for 1 comma separeated',
259  dest='apply',
260  default=None)
261 
262  parser.add_argument('--workflow',
263  help='define a workflow to be created or altered from the matrix',
264  action='append',
265  dest='workflow',
266  default=None)
267 
268  parser.add_argument('--dryRun',
269  help='do not run the wf at all',
270  action='store_true',
271  dest='dryRun',
272  default=False)
273 
274  parser.add_argument('--testbed',
275  help='workflow injection to cmswebtest (you need dedicated rqmgr account)',
276  dest='testbed',
277  default=False,
278  action='store_true')
279 
280  parser.add_argument('--noCafVeto',
281  help='Run from any source, ignoring the CAF label',
282  dest='cafVeto',
283  default=True,
284  action='store_false')
285 
286  parser.add_argument('--overWrite',
287  help='Change the content of a step for another. List of pairs.',
288  dest='overWrite',
289  default=None)
290 
291  parser.add_argument('--noRun',
292  help='Remove all run list selection from wfs',
293  dest='noRun',
294  default=False,
295  action='store_true')
296 
297  parser.add_argument('--das-options',
298  help='Options to be passed to dasgoclient.',
299  dest='dasOptions',
300  default="--limit 0",
301  action='store')
302 
303  parser.add_argument('--job-reports',
304  help='Dump framework job reports',
305  dest='jobReports',
306  default=False,
307  action='store_true')
308 
309  parser.add_argument('--ibeos',
310  help='Use IB EOS site configuration',
311  dest='IBEos',
312  default=False,
313  action='store_true')
314 
315  parser.add_argument('--sites',
316  help='Run DAS query to get data from a specific site. Set it to empty string to search all sites.',
317  dest='dasSites',
318  default='T2_CH_CERN',
319  action='store')
320 
321  parser.add_argument('--interactive',
322  help="Open the Matrix interactive shell",
323  action='store_true',
324  default=False)
325 
326  parser.add_argument('--dbs-url',
327  help='Overwrite DbsUrl value in JSON submitted to ReqMgr2',
328  dest='dbsUrl',
329  default=None,
330  action='store')
331 
332  gpugroup = parser.add_argument_group('GPU-related options','These options are only meaningful when --gpu is used, and is not set to forbidden.')
333 
334  gpugroup.add_argument('--gpu','--requires-gpu',
335  help='Enable GPU workflows. Possible options are "forbidden" (default), "required" (implied if no argument is given), or "optional".',
336  dest='gpu',
337  choices=['forbidden', 'optional', 'required'],
338  nargs='?',
339  const='required',
340  default='forbidden',
341  action='store')
342 
343  gpugroup.add_argument('--gpu-memory',
344  help='Specify the minimum amount of GPU memory required by the job, in MB.',
345  dest='GPUMemoryMB',
346  type=int,
347  default=8000)
348 
349  gpugroup.add_argument('--cuda-capabilities',
350  help='Specify a comma-separated list of CUDA "compute capabilities", or GPU hardware architectures, that the job can use.',
351  dest='CUDACapabilities',
352  type=lambda x: x.split(','),
353  default='6.0,6.1,6.2,7.0,7.2,7.5,8.0,8.6')
354 
355  # read the CUDA runtime version included in CMSSW
356  cudart_version = None
357  libcudart = os.path.realpath(os.path.expandvars('$CMSSW_RELEASE_BASE/external/$SCRAM_ARCH/lib/libcudart.so'))
358  if os.path.isfile(libcudart):
359  cudart_basename = os.path.basename(libcudart)
360  cudart_version = '.'.join(cudart_basename.split('.')[2:4])
361  gpugroup.add_argument('--cuda-runtime',
362  help='Specify major and minor version of the CUDA runtime used to build the application.',
363  dest='CUDARuntime',
364  default=cudart_version)
365 
366  gpugroup.add_argument('--force-gpu-name',
367  help='Request a specific GPU model, e.g. "Tesla T4" or "NVIDIA GeForce RTX 2080". The default behaviour is to accept any supported GPU.',
368  dest='GPUName',
369  default='')
370 
371  gpugroup.add_argument('--force-cuda-driver-version',
372  help='Request a specific CUDA driver version, e.g. 470.57.02. The default behaviour is to accept any supported CUDA driver version.',
373  dest='CUDADriverVersion',
374  default='')
375 
376  gpugroup.add_argument('--force-cuda-runtime-version',
377  help='Request a specific CUDA runtime version, e.g. 11.4. The default behaviour is to accept any supported CUDA runtime version.',
378  dest='CUDARuntimeVersion',
379  default='')
380 
381  opt = parser.parse_args()
382  if opt.command: opt.command = ' '.join(opt.command)
383  os.environ["CMSSW_DAS_QUERY_SITES"]=opt.dasSites
384  if opt.failed_from:
385  rerunthese=[]
386  with open(opt.failed_from,'r') as report:
387  for report_line in report:
388  if 'FAILED' in report_line:
389  to_run,_=report_line.split('_',1)
390  rerunthese.append(to_run)
391  if opt.testList:
392  opt.testList+=','.join(['']+rerunthese)
393  else:
394  opt.testList = ','.join(rerunthese)
395 
396  if opt.IBEos:
397  from subprocess import getstatusoutput as run_cmd
398 
399  ibeos_cache = os.path.join(os.getenv("LOCALRT"), "ibeos_cache.txt")
400  if not os.path.exists(ibeos_cache):
401  err, out = run_cmd("curl -L -s -o %s https://raw.githubusercontent.com/cms-sw/cms-sw.github.io/master/das_queries/ibeos.txt" % ibeos_cache)
402  if err:
403  run_cmd("rm -f %s" % ibeos_cache)
404  print("Error: Unable to download ibeos cache information")
405  print(out)
406  sys.exit(err)
407 
408  for cmssw_env in [ "CMSSW_BASE", "CMSSW_RELEASE_BASE" ]:
409  cmssw_base = os.getenv(cmssw_env,None)
410  if not cmssw_base: continue
411  cmssw_base = os.path.join(cmssw_base,"src/Utilities/General/ibeos")
412  if os.path.exists(cmssw_base):
413  os.environ["PATH"]=cmssw_base+":"+os.getenv("PATH")
414  os.environ["CMS_PATH"]="/cvmfs/cms-ib.cern.ch"
415  os.environ["SITECONFIG_PATH"]="/cvmfs/cms-ib.cern.ch/SITECONF/local"
416  os.environ["CMSSW_USE_IBEOS"]="true"
417  print(">> WARNING: You are using SITECONF from /cvmfs/cms-ib.cern.ch")
418  break
419  if opt.restricted:
420  print('Deprecated, please use -l limited')
421  if opt.testList: opt.testList+=',limited'
422  else: opt.testList='limited'
423 
424  def stepOrIndex(s):
425  if s.isdigit():
426  return int(s)
427  else:
428  return s
429  if opt.apply:
430  opt.apply=map(stepOrIndex,opt.apply.split(','))
431  if opt.keep:
432  opt.keep=map(stepOrIndex,opt.keep.split(','))
433 
434  if opt.testList:
435  testList=[]
436  for entry in opt.testList.split(','):
437  if not entry: continue
438  mapped=False
439  for k in predefinedSet:
440  if k.lower().startswith(entry.lower()) or k.lower().endswith(entry.lower()):
441  testList.extend(predefinedSet[k])
442  mapped=True
443  break
444  if not mapped:
445  try:
446  testList.append(float(entry))
447  except:
448  print(entry,'is not a possible selected entry')
449 
450  opt.testList = list(set(testList))
451 
452  if opt.wmcontrol:
454  if opt.overWrite:
455  opt.overWrite=eval(opt.overWrite)
456  if opt.interactive:
457  import cmd
458  from colorama import Fore, Style
459  from os import isatty
460 
461  class TheMatrix(cmd.Cmd):
462  intro = "Welcome to the Matrix (? for help)"
463  prompt = "matrix> "
464 
465  def __init__(self, opt):
466  cmd.Cmd.__init__(self)
467  self.opt_ = opt
468  self.matrices_ = {}
469  tmp = MatrixReader(self.opt_)
470  for what in tmp.files:
471  what = what.replace('relval_','')
472  self.opt_.what = what
473  self.matrices_[what] = MatrixReader(self.opt_)
474  self.matrices_[what].prepare(self.opt_.useInput, self.opt_.refRel,
475  self.opt_.fromScratch)
476  os.system("clear")
477 
478  def do_clear(self, arg):
479  """Clear the screen, put prompt at the top"""
480  os.system("clear")
481 
482  def do_exit(self, arg):
483  print("Leaving the Matrix")
484  return True
485 
486  def default(self, inp):
487  if inp == 'x' or inp == 'q':
488  return self.do_exit(inp)
489  else:
490  is_pipe = not isatty(sys.stdin.fileno())
491  print(Fore.RED + "Error: " + Fore.RESET + "unrecognized command.")
492  # Quit only if given a piped command.
493  if is_pipe:
494  sys.exit(1)
495 
496  def help_predefined(self):
497  print("\n".join(["predefined [predef1 [...]]\n",
498  "Run w/o argument, it will print the list of known predefined workflows.",
499  "Run with space-separated predefined workflows, it will print the workflow-ids registered to them"]))
500 
501  def complete_predefined(self, text, line, start_idx, end_idx):
502  if text and len(text) > 0:
503  return [t for t in predefinedSet.keys() if t.startswith(text)]
504  else:
505  return predefinedSet.keys()
506 
507  def do_predefined(self, arg):
508  """Print the list of predefined workflows"""
509  print("List of predefined workflows")
510  if arg:
511  for w in arg.split():
512  if w in predefinedSet.keys():
513  print("Predefined Set: %s" % w)
514  print(predefinedSet[w])
515  else:
516  print("Unknown Set: %s" % w)
517  else:
518  print("[ " + Fore.RED + ", ".join([str(k) for k in predefinedSet.keys()]) + Fore.RESET + " ]")
519 
520  def help_showWorkflow(self):
521  print("\n".join(["showWorkflow [workflow1 [...]]\n",
522  "Run w/o arguments, it will print the list of registered macro-workflows.",
523  "Run with space-separated workflows, it will print the full list of workflow-ids registered to them"]))
524 
525  def complete_showWorkflow(self, text, line, start_idx, end_idx):
526  if text and len(text) > 0:
527  return [t for t in self.matrices_.keys() if t.startswith(text)]
528  else:
529  return self.matrices_.keys()
530 
531  def do_showWorkflow(self, arg):
532  if arg == '':
533  print("Available workflows:")
534  for k in self.matrices_.keys():
535  print(Fore.RED + Style.BRIGHT + k)
536  print(Style.RESET_ALL)
537  else:
538  selected = arg.split()
539  for k in selected:
540  if k not in self.matrices_.keys():
541  print("Unknown workflow %s: skipping" % k)
542  else:
543  for wfl in self.matrices_[k].workFlows:
544  print("%s %s" % (Fore.BLUE + str(wfl.numId) + Fore.RESET,
545  Fore.GREEN + wfl.nameId + Fore.RESET))
546  print("%s contains %d workflows" % (Fore.RED + k + Fore.RESET, len(self.matrices_[k].workFlows)))
547 
549  print("\n".join(["searchInWorkflow wfl_name search_regexp\n",
550  "This command will search for a match within all workflows registered to wfl_name.",
551  "The search is done on both the workflow name and the names of steps registered to it."]))
552 
553  def complete_searchInWorkflow(self, text, line, start_idx, end_idx):
554  if text and len(text) > 0:
555  return [t for t in self.matrices_.keys() if t.startswith(text)]
556  else:
557  return self.matrices_.keys()
558 
559  def do_searchInWorkflow(self, arg):
560  args = arg.split()
561  if len(args) < 2:
562  print("searchInWorkflow name regexp")
563  return
564  if args[0] not in self.matrices_.keys():
565  print("Unknown workflow")
566  return
567  import re
568  pattern = None
569  try:
570  pattern = re.compile(args[1])
571  except:
572  print("Failed to compile regexp %s" % args[1])
573  return
574  counter = 0
575  for wfl in self.matrices_[args[0]].workFlows:
576  if re.match(pattern, wfl.nameId):
577  print("%s %s" % (Fore.BLUE + str(wfl.numId) + Fore.RESET,
578  Fore.GREEN + wfl.nameId + Fore.RESET))
579  counter +=1
580  print("Found %s compatible workflows inside %s" % (Fore.RED + str(counter) + Fore.RESET,
581  Fore.YELLOW + str(args[0])) + Fore.RESET)
582 
583  def help_search(self):
584  print("\n".join(["search search_regexp\n",
585  "This command will search for a match within all workflows registered.",
586  "The search is done on both the workflow name and the names of steps registered to it."]))
587 
588  def do_search(self, arg):
589  args = arg.split()
590  if len(args) < 1:
591  print("search regexp")
592  return
593  for wfl in self.matrices_.keys():
594  self.do_searchInWorkflow(' '.join([wfl, args[0]]))
595 
597  print("\n".join(["dumpWorkflowId [wfl-id1 [...]]\n",
598  "Dumps the details (cmsDriver commands for all steps) of the space-separated workflow-ids in input."]))
599 
600  def do_dumpWorkflowId(self, arg):
601  wflids = arg.split()
602  if len(wflids) == 0:
603  print("dumpWorkflowId [wfl-id1 [...]]")
604  return
605 
606  fmt = "[%s]: %s\n"
607  maxLen = 100
608  for wflid in wflids:
609  dump = True
610  for key, mrd in self.matrices_.items():
611  for wfl in mrd.workFlows:
612  if wfl.numId == float(wflid):
613  if dump:
614  dump = False
615  print(Fore.GREEN + str(wfl.numId) + Fore.RESET + " " + Fore.YELLOW + wfl.nameId + Fore.RESET)
616  for i,s in enumerate(wfl.cmds):
617  print(fmt % (Fore.RED + str(i+1) + Fore.RESET,
618  (str(s)+' ')))
619  print("\nWorkflow found in %s." % key)
620  else:
621  print("Workflow also found in %s." % key)
622 
623  do_EOF = do_exit
624 
625  TheMatrix(opt).cmdloop()
626  sys.exit(0)
627 
628  if opt.raw and opt.show:
629  ret = showRaw(opt)
630  else:
631  ret = runSelected(opt)
632 
633 
634  sys.exit(ret)
def performInjectionOptionTest(opt)
def runSelected(opt)
Definition: runTheMatrix.py:20
def do_predefined(self, arg)
def do_showWorkflow(self, arg)
def complete_showWorkflow(self, text, line, start_idx, end_idx)
def do_search(self, arg)
def showRaw(opt)
Definition: runTheMatrix.py:11
def do_clear(self, arg)
def help_dumpWorkflowId(self)
def complete_predefined(self, text, line, start_idx, end_idx)
def help_searchInWorkflow(self)
def do_dumpWorkflowId(self, arg)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
Definition: Utilities.cc:47
def __init__(self, opt)
static std::string join(char **cmd)
Definition: RemoteFile.cc:19
def do_exit(self, arg)
def default(self, inp)
def complete_searchInWorkflow(self, text, line, start_idx, end_idx)
def stepOrIndex(s)
#define str(s)
def do_searchInWorkflow(self, arg)