Package madgraph :: Package interface :: Module extended_cmd
[hide private]
[frames] | no frames]

Source Code for Module madgraph.interface.extended_cmd

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2011 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15  """  A file containing different extension of the cmd basic python library""" 
  16   
  17   
  18  import logging 
  19  import os 
  20  import pydoc 
  21  import re 
  22  import signal 
  23  import subprocess 
  24  import sys 
  25  import traceback 
  26  try: 
  27      import readline 
  28      GNU_SPLITTING = ('GNU' in readline.__doc__) 
  29  except: 
  30      readline = None 
  31      GNU_SPLITTING = True 
  32   
  33   
  34  logger = logging.getLogger('cmdprint') # for stdout 
  35  logger_stderr = logging.getLogger('fatalerror') # for stderr 
  36   
  37  try: 
  38      import madgraph.various.misc as misc 
  39      from madgraph import MG5DIR, MadGraph5Error 
  40      MADEVENT = False 
  41  except ImportError, error: 
  42      try: 
  43          import internal.misc as misc 
  44      except: 
  45          raise error 
  46       
  47      MADEVENT = True 
  48   
  49   
  50  pjoin = os.path.join 
51 52 -class TimeOutError(Exception):
53 """Class for run-time error"""
54
55 -def debug(debug_only=True):
56 57 def deco_debug(f): 58 59 if debug_only and not __debug__: 60 return f 61 62 def deco_f(*args, **opt): 63 try: 64 return f(*args, **opt) 65 except Exception, error: 66 logger.error(error) 67 logger.error(traceback.print_exc(file=sys.stdout)) 68 return
69 return deco_f 70 return deco_debug 71 72 import string 73 74 # The following is copy from the standard cmd routine but pass in new class type 75 __all__ = ["Cmd"] 76 PROMPT = '(Cmd) ' 77 IDENTCHARS = string.ascii_letters + string.digits + '_'
78 -class OriginalCmd(object):
79 """A simple framework for writing line-oriented command interpreters. 80 81 These are often useful for test harnesses, administrative tools, and 82 prototypes that will later be wrapped in a more sophisticated interface. 83 84 A Cmd instance or subclass instance is a line-oriented interpreter 85 framework. There is no good reason to instantiate Cmd itself; rather, 86 it's useful as a superclass of an interpreter class you define yourself 87 in order to inherit Cmd's methods and encapsulate action methods. 88 89 """ 90 prompt = PROMPT 91 identchars = IDENTCHARS 92 ruler = '=' 93 lastcmd = '' 94 intro = None 95 doc_leader = "" 96 doc_header = "Documented commands (type help <topic>):" 97 misc_header = "Miscellaneous help topics:" 98 undoc_header = "Undocumented commands:" 99 nohelp = "*** No help on %s" 100 use_rawinput = 1 101
102 - def __init__(self, completekey='tab', stdin=None, stdout=None,**opt):
103 """Instantiate a line-oriented interpreter framework. 104 105 The optional argument 'completekey' is the readline name of a 106 completion key; it defaults to the Tab key. If completekey is 107 not None and the readline module is available, command completion 108 is done automatically. The optional arguments stdin and stdout 109 specify alternate input and output file objects; if not specified, 110 sys.stdin and sys.stdout are used. 111 112 """ 113 import sys 114 if stdin is not None: 115 self.stdin = stdin 116 else: 117 self.stdin = sys.stdin 118 if stdout is not None: 119 self.stdout = stdout 120 else: 121 self.stdout = sys.stdout 122 self.cmdqueue = [] 123 self.completekey = completekey 124 self.cmd_options = opt
125
126 - def cmdloop(self, intro=None):
127 """Repeatedly issue a prompt, accept input, parse an initial prefix 128 off the received input, and dispatch to action methods, passing them 129 the remainder of the line as argument. 130 131 """ 132 133 self.preloop() 134 if self.use_rawinput and self.completekey: 135 try: 136 import readline 137 self.old_completer = readline.get_completer() 138 readline.set_completer(self.complete) 139 readline.parse_and_bind(self.completekey+": complete") 140 except ImportError: 141 pass 142 try: 143 if intro is not None: 144 self.intro = intro 145 if self.intro: 146 self.stdout.write(str(self.intro)+"\n") 147 stop = None 148 while not stop: 149 if self.cmdqueue: 150 line = self.cmdqueue.pop(0) 151 else: 152 if self.use_rawinput: 153 try: 154 line = raw_input(self.prompt) 155 except EOFError: 156 line = 'EOF' 157 else: 158 self.stdout.write(self.prompt) 159 self.stdout.flush() 160 line = self.stdin.readline() 161 if not len(line): 162 line = 'EOF' 163 else: 164 line = line.rstrip('\r\n') 165 line = self.precmd(line) 166 stop = self.onecmd(line) 167 stop = self.postcmd(stop, line) 168 self.postloop() 169 finally: 170 if self.use_rawinput and self.completekey: 171 try: 172 import readline 173 readline.set_completer(self.old_completer) 174 except ImportError: 175 pass
176 177
178 - def precmd(self, line):
179 """Hook method executed just before the command line is 180 interpreted, but after the input prompt is generated and issued. 181 182 """ 183 return line
184
185 - def postcmd(self, stop, line):
186 """Hook method executed just after a command dispatch is finished.""" 187 return stop
188
189 - def preloop(self):
190 """Hook method executed once when the cmdloop() method is called.""" 191 pass
192
193 - def postloop(self):
194 """Hook method executed once when the cmdloop() method is about to 195 return. 196 197 """ 198 pass
199
200 - def parseline(self, line):
201 """Parse the line into a command name and a string containing 202 the arguments. Returns a tuple containing (command, args, line). 203 'command' and 'args' may be None if the line couldn't be parsed. 204 """ 205 line = line.strip() 206 if not line: 207 return None, None, line 208 elif line[0] == '?': 209 line = 'help ' + line[1:] 210 elif line[0] == '!': 211 if hasattr(self, 'do_shell'): 212 line = 'shell ' + line[1:] 213 else: 214 return None, None, line 215 i, n = 0, len(line) 216 while i < n and line[i] in self.identchars: i = i+1 217 cmd, arg = line[:i], line[i:].strip() 218 return cmd, arg, line
219
220 - def onecmd(self, line):
221 """Interpret the argument as though it had been typed in response 222 to the prompt. 223 224 This may be overridden, but should not normally need to be; 225 see the precmd() and postcmd() methods for useful execution hooks. 226 The return value is a flag indicating whether interpretation of 227 commands by the interpreter should stop. 228 229 """ 230 cmd, arg, line = self.parseline(line) 231 if not line: 232 return self.emptyline() 233 if cmd is None: 234 return self.default(line) 235 self.lastcmd = line 236 if cmd == '': 237 return self.default(line) 238 else: 239 try: 240 func = getattr(self, 'do_' + cmd) 241 except AttributeError: 242 return self.default(line) 243 return func(arg)
244
245 - def emptyline(self):
246 """Called when an empty line is entered in response to the prompt. 247 248 If this method is not overridden, it repeats the last nonempty 249 command entered. 250 251 """ 252 if self.lastcmd: 253 return self.onecmd(self.lastcmd)
254
255 - def default(self, line):
256 """Called on an input line when the command prefix is not recognized. 257 258 If this method is not overridden, it prints an error message and 259 returns. 260 261 """ 262 self.stdout.write('*** Unknown syntax: %s\n'%line)
263
264 - def completedefault(self, *ignored):
265 """Method called to complete an input line when no command-specific 266 complete_*() method is available. 267 268 By default, it returns an empty list. 269 270 """ 271 return []
272
273 - def completenames(self, text, *ignored):
274 dotext = 'do_'+text 275 276 done = set() # store the command already handle 277 278 return [a[3:] for a in self.get_names() 279 if a.startswith(dotext) and a not in done and not done.add(a)]
280
281 - def complete(self, text, state):
282 """Return the next possible completion for 'text'. 283 284 If a command has not been entered, then complete against command list. 285 Otherwise try to call complete_<command> to get list of completions. 286 """ 287 if state == 0: 288 import readline 289 origline = readline.get_line_buffer() 290 line = origline.lstrip() 291 stripped = len(origline) - len(line) 292 begidx = readline.get_begidx() - stripped 293 endidx = readline.get_endidx() - stripped 294 if begidx>0: 295 cmd, args, foo = self.parseline(line) 296 if cmd == '': 297 compfunc = self.completedefault 298 else: 299 try: 300 compfunc = getattr(self, 'complete_' + cmd) 301 except AttributeError: 302 compfunc = self.completedefault 303 else: 304 compfunc = self.completenames 305 self.completion_matches = compfunc(text, line, begidx, endidx) 306 try: 307 return self.completion_matches[state] 308 except IndexError: 309 return None
310
311 - def get_names(self):
312 # Inheritance says we have to look in class and 313 # base classes; order is not important. 314 names = [] 315 classes = [self.__class__] 316 while classes: 317 aclass = classes.pop(0) 318 if aclass.__bases__: 319 classes = classes + list(aclass.__bases__) 320 names = names + dir(aclass) 321 return names
322
323 - def complete_help(self, *args):
324 return self.completenames(*args)
325
326 - def do_help(self, arg):
327 if arg: 328 # XXX check arg syntax 329 try: 330 func = getattr(self, 'help_' + arg) 331 except AttributeError: 332 try: 333 doc=getattr(self, 'do_' + arg).__doc__ 334 if doc: 335 self.stdout.write("%s\n"%str(doc)) 336 return 337 except AttributeError: 338 pass 339 self.stdout.write("%s\n"%str(self.nohelp % (arg,))) 340 return 341 func() 342 else: 343 names = self.get_names() 344 cmds_doc = [] 345 cmds_undoc = [] 346 help = {} 347 for name in names: 348 if name[:5] == 'help_': 349 help[name[5:]]=1 350 names.sort() 351 # There can be duplicates if routines overridden 352 prevname = '' 353 for name in names: 354 if name[:3] == 'do_': 355 if name == prevname: 356 continue 357 prevname = name 358 cmd=name[3:] 359 if cmd in help: 360 cmds_doc.append(cmd) 361 del help[cmd] 362 elif getattr(self, name).__doc__: 363 cmds_doc.append(cmd) 364 else: 365 cmds_undoc.append(cmd) 366 self.stdout.write("%s\n"%str(self.doc_leader)) 367 self.print_topics(self.doc_header, cmds_doc, 15,80) 368 self.print_topics(self.misc_header, help.keys(),15,80) 369 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
370
371 - def print_topics(self, header, cmds, cmdlen, maxcol):
372 if cmds: 373 self.stdout.write("%s\n"%str(header)) 374 if self.ruler: 375 self.stdout.write("%s\n"%str(self.ruler * len(header))) 376 self.columnize(cmds, maxcol-1) 377 self.stdout.write("\n")
378
379 - def columnize(self, list, displaywidth=80):
380 """Display a list of strings as a compact set of columns. 381 382 Each column is only as wide as necessary. 383 Columns are separated by two spaces (one was not legible enough). 384 """ 385 if not list: 386 self.stdout.write("<empty>\n") 387 return 388 nonstrings = [i for i in range(len(list)) 389 if not isinstance(list[i], str)] 390 if nonstrings: 391 raise TypeError, ("list[i] not a string for i in %s" % 392 ", ".join(map(str, nonstrings))) 393 size = len(list) 394 if size == 1: 395 self.stdout.write('%s\n'%str(list[0])) 396 return 397 # Try every row count from 1 upwards 398 for nrows in range(1, len(list)): 399 ncols = (size+nrows-1) // nrows 400 colwidths = [] 401 totwidth = -2 402 for col in range(ncols): 403 colwidth = 0 404 for row in range(nrows): 405 i = row + nrows*col 406 if i >= size: 407 break 408 x = list[i] 409 colwidth = max(colwidth, len(x)) 410 colwidths.append(colwidth) 411 totwidth += colwidth + 2 412 if totwidth > displaywidth: 413 break 414 if totwidth <= displaywidth: 415 break 416 else: 417 nrows = len(list) 418 ncols = 1 419 colwidths = [0] 420 for row in range(nrows): 421 texts = [] 422 for col in range(ncols): 423 i = row + nrows*col 424 if i >= size: 425 x = "" 426 else: 427 x = list[i] 428 texts.append(x) 429 while texts and not texts[-1]: 430 del texts[-1] 431 for col in range(len(texts)): 432 texts[col] = texts[col].ljust(colwidths[col]) 433 self.stdout.write("%s\n"%str(" ".join(texts)))
434
435 436 437 438 #=============================================================================== 439 # CmdExtended 440 #=============================================================================== 441 -class BasicCmd(OriginalCmd):
442 """Simple extension for the readline""" 443
445 """ This has been refactorized here so that it can be called when another 446 program called by MG5 (such as MadAnalysis5) changes this attribute of readline""" 447 if readline: 448 if not 'libedit' in readline.__doc__: 449 readline.set_completion_display_matches_hook(self.print_suggestions) 450 else: 451 readline.set_completion_display_matches_hook()
452
453 - def preloop(self):
456
457 - def deal_multiple_categories(self, dico, formatting=True, forceCategory=False):
458 """convert the multiple category in a formatted list understand by our 459 specific readline parser""" 460 461 if not formatting: 462 return dico 463 464 if 'libedit' in readline.__doc__: 465 # No parser in this case, just send all the valid options 466 out = [] 467 for name, opt in dico.items(): 468 out += opt 469 return out 470 471 # check if more than one categories but only one value: 472 if not forceCategory and all(len(s) <= 1 for s in dico.values() ): 473 values = set((s[0] for s in dico.values() if len(s)==1)) 474 if len(values) == 1: 475 return values 476 477 # That's the real work 478 out = [] 479 valid=0 480 # if the key starts with number order the key with that number. 481 for name, opt in dico.items(): 482 if not opt: 483 continue 484 name = name.replace(' ', '_') 485 valid += 1 486 out.append(opt[0].rstrip()+'@@'+name+'@@') 487 # Remove duplicate 488 d = {} 489 for x in opt: 490 d[x] = 1 491 opt = list(d.keys()) 492 opt.sort() 493 out += opt 494 495 if not forceCategory and valid == 1: 496 out = out[1:] 497 498 return out
499 500 @debug()
501 - def print_suggestions(self, substitution, matches, longest_match_length) :
502 """print auto-completions by category""" 503 if not hasattr(self, 'completion_prefix'): 504 self.completion_prefix = '' 505 longest_match_length += len(self.completion_prefix) 506 try: 507 if len(matches) == 1: 508 self.stdout.write(matches[0]+' ') 509 return 510 self.stdout.write('\n') 511 l2 = [a[-2:] for a in matches] 512 if '@@' in l2: 513 nb_column = self.getTerminalSize()//(longest_match_length+1) 514 pos=0 515 for val in self.completion_matches: 516 if val.endswith('@@'): 517 category = val.rsplit('@@',2)[1] 518 category = category.replace('_',' ') 519 self.stdout.write('\n %s:\n%s\n' % (category, '=' * (len(category)+2))) 520 start = 0 521 pos = 0 522 continue 523 elif pos and pos % nb_column ==0: 524 self.stdout.write('\n') 525 self.stdout.write(self.completion_prefix + val + \ 526 ' ' * (longest_match_length +1 -len(val))) 527 pos +=1 528 self.stdout.write('\n') 529 else: 530 # nb column 531 nb_column = self.getTerminalSize()//(longest_match_length+1) 532 for i,val in enumerate(matches): 533 if i and i%nb_column ==0: 534 self.stdout.write('\n') 535 self.stdout.write(self.completion_prefix + val + \ 536 ' ' * (longest_match_length +1 -len(val))) 537 self.stdout.write('\n') 538 539 self.stdout.write(self.prompt+readline.get_line_buffer()) 540 self.stdout.flush() 541 except Exception, error: 542 if __debug__: 543 logger.error(error)
544
545 - def getTerminalSize(self):
546 def ioctl_GWINSZ(fd): 547 try: 548 import fcntl, termios, struct 549 cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 550 '1234')) 551 except Exception: 552 return None 553 return cr
554 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 555 if not cr: 556 try: 557 fd = os.open(os.ctermid(), os.O_RDONLY) 558 cr = ioctl_GWINSZ(fd) 559 os.close(fd) 560 except Exception: 561 pass 562 if not cr: 563 try: 564 cr = (os.environ['LINES'], os.environ['COLUMNS']) 565 except Exception: 566 cr = (25, 80) 567 return int(cr[1])
568
569 - def complete(self, text, state):
570 """Return the next possible completion for 'text'. 571 If a command has not been entered, then complete against command list. 572 Otherwise try to call complete_<command> to get list of completions. 573 """ 574 if state == 0: 575 import readline 576 origline = readline.get_line_buffer() 577 line = origline.lstrip() 578 stripped = len(origline) - len(line) 579 begidx = readline.get_begidx() - stripped 580 endidx = readline.get_endidx() - stripped 581 582 if ';' in line: 583 begin, line = line.rsplit(';',1) 584 begidx = begidx - len(begin) - 1 585 endidx = endidx - len(begin) - 1 586 if line[:begidx] == ' ' * begidx: 587 begidx=0 588 589 if begidx>0: 590 cmd, args, foo = self.parseline(line) 591 if cmd == '': 592 compfunc = self.completedefault 593 else: 594 try: 595 compfunc = getattr(self, 'complete_' + cmd) 596 except AttributeError, error: 597 compfunc = self.completedefault 598 except Exception, error: 599 misc.sprint(error) 600 else: 601 compfunc = self.completenames 602 603 # correct wrong splittion with '\ ' 604 if line and begidx > 2 and line[begidx-2:begidx] == '\ ': 605 Ntext = line.split(os.path.sep)[-1] 606 self.completion_prefix = Ntext.rsplit('\ ', 1)[0] + '\ ' 607 to_rm = len(self.completion_prefix) - 1 608 Nbegidx = len(line.rsplit(os.path.sep, 1)[0]) + 1 609 data = compfunc(Ntext.replace('\ ', ' '), line, Nbegidx, endidx) 610 self.completion_matches = [p[to_rm:] for p in data 611 if len(p)>to_rm] 612 # correct wrong splitting with '-'/"=" 613 elif line and line[begidx-1] in ['-',"=",':']: 614 try: 615 sep = line[begidx-1] 616 Ntext = line.split()[-1] 617 self.completion_prefix = Ntext.rsplit(sep,1)[0] + sep 618 to_rm = len(self.completion_prefix) 619 Nbegidx = len(line.rsplit(None, 1)[0]) 620 data = compfunc(Ntext, line, Nbegidx, endidx) 621 self.completion_matches = [p[to_rm:] for p in data 622 if len(p)>to_rm] 623 except Exception, error: 624 print error 625 else: 626 self.completion_prefix = '' 627 self.completion_matches = compfunc(text, line, begidx, endidx) 628 629 self.completion_matches = [ l if l[-1] in [' ','@','=',os.path.sep] 630 else ((l + ' ') if not l.endswith('\\$') else l[:-2]) 631 for l in self.completion_matches if l] 632 633 try: 634 return self.completion_matches[state] 635 except IndexError, error: 636 # if __debug__: 637 # logger.error('\n Completion ERROR:') 638 # logger.error( error) 639 return None
640 641 @staticmethod
642 - def split_arg(line):
643 """Split a line of arguments""" 644 645 split = line.split() 646 out=[] 647 tmp='' 648 for data in split: 649 if data[-1] == '\\': 650 tmp += data[:-1]+' ' 651 elif tmp: 652 tmp += data 653 tmp = os.path.expanduser(os.path.expandvars(tmp)) 654 out.append(tmp) 655 # Reinitialize tmp in case there is another differen argument 656 # containing escape characters 657 tmp = '' 658 else: 659 out.append(data) 660 return out
661 662 @staticmethod
663 - def list_completion(text, list, line=''):
664 """Propose completions of text in list""" 665 666 if not text: 667 completions = list 668 else: 669 completions = [ f 670 for f in list 671 if f.startswith(text) 672 ] 673 674 return completions
675 676 677 @staticmethod
678 - def path_completion(text, base_dir = None, only_dirs = False, 679 relative=True):
680 """Propose completions of text to compose a valid path""" 681 682 if base_dir is None: 683 base_dir = os.getcwd() 684 base_dir = os.path.expanduser(os.path.expandvars(base_dir)) 685 686 if text == '~': 687 text = '~/' 688 prefix, text = os.path.split(text) 689 prefix = os.path.expanduser(os.path.expandvars(prefix)) 690 base_dir = os.path.join(base_dir, prefix) 691 if prefix: 692 prefix += os.path.sep 693 694 if only_dirs: 695 completion = [prefix + f + os.path.sep 696 for f in os.listdir(base_dir) 697 if f.startswith(text) and \ 698 os.path.isdir(os.path.join(base_dir, f)) and \ 699 (not f.startswith('.') or text.startswith('.')) 700 ] 701 else: 702 completion = [ prefix + f 703 for f in os.listdir(base_dir) 704 if f.startswith(text) and \ 705 os.path.isfile(os.path.join(base_dir, f)) and \ 706 (not f.startswith('.') or text.startswith('.')) 707 ] 708 709 completion = completion + \ 710 [prefix + f + os.path.sep 711 for f in os.listdir(base_dir) 712 if f.startswith(text) and \ 713 os.path.isdir(os.path.join(base_dir, f)) and \ 714 (not f.startswith('.') or text.startswith('.')) 715 ] 716 717 if relative: 718 completion += [prefix + f for f in ['.'+os.path.sep, '..'+os.path.sep] if \ 719 f.startswith(text) and not prefix.startswith('.')] 720 721 completion = [a.replace(' ','\ ') for a in completion] 722 return completion
723
724 725 726 727 -class CheckCmd(object):
728 """Extension of the cmd object for only the check command""" 729
730 - def check_history(self, args):
731 """check the validity of line""" 732 733 if len(args) > 1: 734 self.help_history() 735 raise self.InvalidCmd('\"history\" command takes at most one argument') 736 737 if not len(args): 738 return 739 740 if args[0] =='.': 741 if not self._export_dir: 742 raise self.InvalidCmd("No default directory is defined for \'.\' option") 743 elif args[0] != 'clean': 744 dirpath = os.path.dirname(args[0]) 745 if dirpath and not os.path.exists(dirpath) or \ 746 os.path.isdir(args[0]): 747 raise self.InvalidCmd("invalid path %s " % dirpath)
748
749 - def check_save(self, args):
750 """check that the line is compatible with save options""" 751 752 if len(args) > 2: 753 self.help_save() 754 raise self.InvalidCmd, 'too many arguments for save command.' 755 756 if len(args) == 2: 757 if args[0] != 'options': 758 self.help_save() 759 raise self.InvalidCmd, '\'%s\' is not recognized as first argument.' % \ 760 args[0] 761 else: 762 args.pop(0)
763
764 -class HelpCmd(object):
765 """Extension of the cmd object for only the help command""" 766
767 - def help_quit(self):
768 logger.info("-- terminates the application",'$MG:color:BLUE') 769 logger.info("syntax: quit",'$MG:color:BLACK')
770 771 help_EOF = help_quit 772
773 - def help_history(self):
774 logger.info("-- interact with the command history.",'$MG:color:BLUE') 775 logger.info("syntax: history [FILEPATH|clean|.] ",'$MG:color:BLACK') 776 logger.info(" > If FILEPATH is \'.\' and \'output\' is done,") 777 logger.info(" Cards/proc_card_mg5.dat will be used.") 778 logger.info(" > If FILEPATH is omitted, the history will be output to stdout.") 779 logger.info(" \"clean\" will remove all entries from the history.")
780
781 - def help_help(self):
782 logger.info("-- access to the in-line help",'$MG:color:BLUE') 783 logger.info("syntax: help",'$MG:color:BLACK')
784
785 - def help_save(self):
786 """help text for save""" 787 logger.info("-- save options configuration to filepath.",'$MG:color:BLUE') 788 logger.info("syntax: save [options] [FILEPATH]",'$MG:color:BLACK')
789
790 - def help_display(self):
791 """help for display command""" 792 logger.info("-- display a the status of various internal state variables",'$MG:color:BLUE') 793 logger.info("syntax: display " + "|".join(self._display_opts),'$MG:color:BLACK')
794
795 -class CompleteCmd(object):
796 """Extension of the cmd object for only the complete command""" 797
798 - def complete_display(self,text, line, begidx, endidx):
799 args = self.split_arg(line[0:begidx]) 800 # Format 801 if len(args) == 1: 802 return self.list_completion(text, self._display_opts)
803
804 - def complete_history(self, text, line, begidx, endidx):
805 "Complete the history command" 806 807 args = self.split_arg(line[0:begidx]) 808 809 # Directory continuation 810 if args[-1].endswith(os.path.sep): 811 return self.path_completion(text, 812 os.path.join('.',*[a for a in args \ 813 if a.endswith(os.path.sep)])) 814 815 if len(args) == 1: 816 return self.path_completion(text)
817
818 - def complete_save(self, text, line, begidx, endidx):
819 "Complete the save command" 820 821 args = self.split_arg(line[0:begidx]) 822 823 # Format 824 if len(args) == 1: 825 return self.list_completion(text, ['options']) 826 827 # Directory continuation 828 if args[-1].endswith(os.path.sep): 829 return self.path_completion(text, 830 pjoin('.',*[a for a in args if a.endswith(os.path.sep)]), 831 only_dirs = True) 832 833 # Filename if directory is not given 834 if len(args) == 2: 835 return self.path_completion(text)
836
837 -class Cmd(CheckCmd, HelpCmd, CompleteCmd, BasicCmd):
838 """Extension of the cmd.Cmd command line. 839 This extensions supports line breaking, history, comments, 840 internal call to cmdline, path completion,... 841 this class should be MG5 independent""" 842 843 #suggested list of command 844 next_possibility = {} # command : [list of suggested command] 845 history_header = "" 846 847 _display_opts = ['options','variable'] 848 allow_notification_center = True 849
850 - class InvalidCmd(Exception):
851 """expected error for wrong command""" 852 pass
853 854 ConfigurationError = InvalidCmd 855 856 debug_output = 'debug' 857 error_debug = """Please report this bug to developers\n 858 More information is found in '%(debug)s'.\n 859 Please attach this file to your report.""" 860 config_debug = error_debug 861 862 keyboard_stop_msg = """stopping all current operation 863 in order to quit the program please enter exit""" 864 865 if MADEVENT: 866 plugin_path = [] 867 else: 868 plugin_path = [pjoin(MG5DIR, 'PLUGIN')] 869 if 'PYTHONPATH' in os.environ: 870 for PluginCandidate in os.environ['PYTHONPATH'].split(':'): 871 try: 872 dirlist = os.listdir(PluginCandidate) 873 except OSError: 874 continue 875 for onedir in dirlist: 876 if onedir == 'MG5aMC_PLUGIN': 877 plugin_path.append(pjoin(PluginCandidate, 'MG5aMC_PLUGIN')) 878 break 879 else: 880 continue 881 break 882
883 - def __init__(self, *arg, **opt):
884 """Init history and line continuation""" 885 886 self.log = True 887 self.history = [] 888 self.save_line = '' # for line splitting 889 super(Cmd, self).__init__(*arg, **opt) 890 self.__initpos = os.path.abspath(os.getcwd()) 891 self.child = None # sub CMD interface call from this one 892 self.mother = None #This CMD interface was called from another one 893 self.inputfile = None # input file (in non interactive mode) 894 self.haspiping = not sys.stdin.isatty() # check if mg5 is piped 895 self.stored_line = '' # for be able to treat answer to question in input file 896 # answer which are not required. 897 if not hasattr(self, 'helporder'): 898 self.helporder = ['Documented commands']
899
900 - def preloop(self):
901 """Hook method executed once when the cmdloop() method is called.""" 902 if self.completekey: 903 try: 904 import readline 905 self.old_completer = readline.get_completer() 906 readline.set_completer(self.complete) 907 readline.parse_and_bind(self.completekey+": complete") 908 except ImportError: 909 readline = None 910 pass 911 if readline and not 'libedit' in readline.__doc__: 912 readline.set_completion_display_matches_hook(self.print_suggestions)
913 914
915 - def cmdloop(self, intro=None):
916 917 self.preloop() 918 if intro is not None: 919 self.intro = intro 920 if self.intro: 921 print self.intro 922 stop = None 923 while not stop: 924 if self.cmdqueue: 925 line = self.cmdqueue[0] 926 del self.cmdqueue[0] 927 else: 928 if self.use_rawinput: 929 try: 930 line = raw_input(self.prompt) 931 except EOFError: 932 line = 'EOF' 933 else: 934 sys.stdout.write(self.prompt) 935 sys.stdout.flush() 936 line = sys.stdin.readline() 937 if not len(line): 938 line = 'EOF' 939 else: 940 line = line[:-1] # chop \n 941 try: 942 line = self.precmd(line) 943 stop = self.onecmd(line) 944 except BaseException, error: 945 self.error_handling(error, line) 946 if isinstance(error, KeyboardInterrupt): 947 stop = True 948 finally: 949 stop = self.postcmd(stop, line) 950 self.postloop()
951
952 - def no_notification(self):
953 """avoid to have html opening / notification""" 954 self.allow_notification_center = False 955 try: 956 self.options['automatic_html_opening'] = False 957 self.options['notification_center'] = False 958 959 except: 960 pass
961 962
963 - def precmd(self, line):
964 """ A suite of additional function needed for in the cmd 965 this implement history, line breaking, comment treatment,... 966 """ 967 968 if not line: 969 return line 970 971 # Check if we are continuing a line: 972 if self.save_line: 973 line = self.save_line + line 974 self.save_line = '' 975 976 line = line.lstrip() 977 # Check if the line is complete 978 if line.endswith('\\'): 979 self.save_line = line[:-1] 980 return '' # do nothing 981 982 # Remove comment 983 if '#' in line: 984 line = line.split('#')[0] 985 986 # Deal with line splitting 987 if ';' in line: 988 lines = line.split(';') 989 for subline in lines: 990 if not (subline.startswith("history") or subline.startswith('help') \ 991 or subline.startswith('#*')): 992 self.history.append(subline) 993 stop = self.onecmd_orig(subline) 994 stop = self.postcmd(stop, subline) 995 return '' 996 997 # execute the line command 998 self.history.append(line) 999 return line
1000
1001 - def postcmd(self,stop, line):
1002 """ finishing a command 1003 This looks if the command add a special post part.""" 1004 1005 if line.strip(): 1006 try: 1007 cmd, subline = line.split(None, 1) 1008 except ValueError: 1009 pass 1010 else: 1011 if hasattr(self,'post_%s' %cmd): 1012 stop = getattr(self, 'post_%s' % cmd)(stop, subline) 1013 return stop
1014
1015 - def define_child_cmd_interface(self, obj_instance, interface=True):
1016 """Define a sub cmd_interface""" 1017 1018 # We are in a file reading mode. So we need to redirect the cmd 1019 self.child = obj_instance 1020 self.child.mother = self 1021 1022 #ensure that notification are sync: 1023 self.child.allow_notification_center = self.allow_notification_center 1024 1025 if self.use_rawinput and interface: 1026 # We are in interactive mode -> simply call the child 1027 obj_instance.cmdloop() 1028 stop = obj_instance.postloop() 1029 return stop 1030 if self.inputfile: 1031 # we are in non interactive mode -> so pass the line information 1032 obj_instance.inputfile = self.inputfile 1033 1034 obj_instance.haspiping = self.haspiping 1035 1036 if not interface: 1037 return self.child
1038 1039 #=============================================================================== 1040 # Ask a question with nice options handling 1041 #===============================================================================
1042 - def ask(self, question, default, choices=[], path_msg=None, 1043 timeout = True, fct_timeout=None, ask_class=None, alias={}, 1044 first_cmd=None, text_format='4', **opt):
1045 """ ask a question with some pre-define possibility 1046 path info is 1047 """ 1048 1049 if path_msg: 1050 path_msg = [path_msg] 1051 else: 1052 path_msg = [] 1053 1054 if timeout is True: 1055 try: 1056 timeout = self.options['timeout'] 1057 except Exception: 1058 pass 1059 1060 # add choice info to the question 1061 if choices + path_msg: 1062 question += ' [' 1063 question += "\033[%sm%s\033[0m, " % (text_format, default) 1064 for data in choices[:9] + path_msg: 1065 if default == data: 1066 continue 1067 else: 1068 question += "%s, " % data 1069 1070 if len(choices) > 9: 1071 question += '... , ' 1072 question = question[:-2]+']' 1073 else: 1074 question += "[\033[%sm%s\033[0m] " % (text_format, default) 1075 if ask_class: 1076 obj = ask_class 1077 elif path_msg: 1078 obj = OneLinePathCompletion 1079 else: 1080 obj = SmartQuestion 1081 1082 if alias: 1083 choices += alias.keys() 1084 1085 question_instance = obj(question, allow_arg=choices, default=default, 1086 mother_interface=self, **opt) 1087 1088 if first_cmd: 1089 if isinstance(first_cmd, str): 1090 question_instance.onecmd(first_cmd) 1091 else: 1092 for line in first_cmd: 1093 question_instance.onecmd(line) 1094 if not self.haspiping: 1095 if hasattr(obj, "haspiping"): 1096 obj.haspiping = self.haspiping 1097 1098 1099 1100 1101 answer = self.check_answer_in_input_file(question_instance, default, path_msg) 1102 if answer is not None: 1103 if answer in alias: 1104 answer = alias[answer] 1105 if ask_class: 1106 line=answer 1107 answer = question_instance.default(line) 1108 question_instance.postcmd(answer, line) 1109 return question_instance.answer 1110 if hasattr(question_instance, 'check_answer_consistency'): 1111 question_instance.check_answer_consistency() 1112 return answer 1113 1114 question = question_instance.question 1115 value = Cmd.timed_input(question, default, timeout=timeout, 1116 fct=question_instance, fct_timeout=fct_timeout) 1117 1118 try: 1119 if value in alias: 1120 value = alias[value] 1121 except TypeError: 1122 pass 1123 1124 if value == default and ask_class: 1125 value = question_instance.default(default) 1126 return value
1127
1128 - def do_import(self, line):
1129 """Advanced commands: Import command files""" 1130 1131 args = self.split_arg(line) 1132 # Check argument's validity 1133 self.check_import(args) 1134 1135 # Execute the card 1136 self.import_command_file(args[1])
1137 1138
1139 - def check_import(self, args):
1140 """check import command""" 1141 1142 if '-f' in args: 1143 self.force = True 1144 args.remove('-f') 1145 if args[0] != 'command': 1146 args.set(0, 'command') 1147 if len(args) != 2: 1148 raise self.InvalidCmd('import command requires one filepath argument') 1149 if not os.path.exists(args[1]): 1150 raise 'No such file or directory %s' % args[1]
1151 1152
1153 - def check_answer_in_input_file(self, question_instance, default, path=False, line=None):
1154 """Questions can have answer in output file (or not)""" 1155 1156 if not self.inputfile: 1157 return None# interactive mode 1158 1159 if line is None: 1160 line = self.get_stored_line() 1161 # line define if a previous answer was not answer correctly 1162 if not line: 1163 try: 1164 line = self.inputfile.next() 1165 except StopIteration: 1166 if self.haspiping: 1167 logger.debug('piping') 1168 self.store_line(line) 1169 return None # print the question and use the pipe 1170 logger.info(question_instance.question) 1171 logger.info('The answer to the previous question is not set in your input file', '$MG:color:BLACK') 1172 logger.info('Use %s value' % default, '$MG:color:BLACK') 1173 return str(default) 1174 1175 line = line.replace('\n','').strip() 1176 if '#' in line: 1177 line = line.split('#')[0] 1178 if not line: 1179 # Comment or empty line, pass to the next one 1180 return self.check_answer_in_input_file(question_instance, default, path) 1181 options = question_instance.allow_arg 1182 if line in options or line.lower() in options: 1183 return line 1184 elif hasattr(question_instance, 'do_%s' % line.split()[0]): 1185 #This is a command line, exec it and check next line 1186 logger.info(line) 1187 fct = getattr(question_instance, 'do_%s' % line.split()[0]) 1188 fct(' '.join(line.split()[1:])) 1189 return self.check_answer_in_input_file(question_instance, default, path) 1190 elif path: 1191 line = os.path.expanduser(os.path.expandvars(line)) 1192 if os.path.isfile(line): 1193 return line 1194 elif hasattr(question_instance, 'casesensitive') and not question_instance.casesensitive: 1195 for entry in question_instance.allow_arg: 1196 if line.lower() == entry.lower(): 1197 return entry 1198 elif any(line.lower()==opt.lower() for opt in options): 1199 possibility = [opt for opt in options if line.lower()==opt.lower()] 1200 if len (possibility)==1: 1201 return possibility[0] 1202 if '=' in line and ' ' in line.strip(): 1203 leninit = len(line) 1204 line,n = re.subn('\s*=\s*','=', line) 1205 if n and len(line) != leninit: 1206 return self.check_answer_in_input_file(question_instance, default, path=path, line=line) 1207 1208 1209 1210 # No valid answer provides 1211 if self.haspiping: 1212 self.store_line(line) 1213 return None # print the question and use the pipe 1214 else: 1215 logger.info(question_instance.question) 1216 logger.warning('found line : %s' % line) 1217 logger.warning('This answer is not valid for current question. Keep it for next question and use here default: %s', default) 1218 self.store_line(line) 1219 return str(default)
1220
1221 - def store_line(self, line):
1222 """store a line of the input file which should be executed by the higher mother""" 1223 1224 if self.mother: 1225 self.mother.store_line(line) 1226 else: 1227 self.stored_line = line
1228
1229 - def get_stored_line(self):
1230 """return stored line and clean it""" 1231 if self.mother: 1232 value = self.mother.get_stored_line() 1233 self.mother.stored_line = None 1234 else: 1235 value = self.stored_line 1236 self.stored_line = None 1237 return value
1238 1239 1240
1241 - def nice_error_handling(self, error, line):
1242 """ """ 1243 # Make sure that we are at the initial position 1244 if self.child: 1245 return self.child.nice_error_handling(error, line) 1246 1247 os.chdir(self.__initpos) 1248 # Create the debug files 1249 self.log = False 1250 if os.path.exists(self.debug_output): 1251 os.remove(self.debug_output) 1252 try: 1253 super(Cmd,self).onecmd('history %s' % self.debug_output.replace(' ', '\ ')) 1254 except Exception, error: 1255 logger.error(error) 1256 1257 debug_file = open(self.debug_output, 'a') 1258 traceback.print_exc(file=debug_file) 1259 if hasattr(error, 'filename'): 1260 debug_file.write("Related File: %s\n" % error.filename) 1261 # Create a nice error output 1262 if self.history and line == self.history[-1]: 1263 error_text = 'Command \"%s\" interrupted with error:\n' % line 1264 elif self.history: 1265 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1266 error_text += '\"%s\" with error:\n' % self.history[-1] 1267 else: 1268 error_text = '' 1269 error_text += '%s : %s\n' % (error.__class__.__name__, 1270 str(error).replace('\n','\n\t')) 1271 error_text += self.error_debug % {'debug':self.debug_output} 1272 logger_stderr.critical(error_text) 1273 1274 1275 # Add options status to the debug file 1276 try: 1277 self.do_display('options', debug_file) 1278 except Exception, error: 1279 debug_file.write('Fail to write options with error %s' % error) 1280 1281 #add the cards: 1282 for card in ['proc_card_mg5.dat','param_card.dat', 'run_card.dat']: 1283 try: 1284 ff = open(pjoin(self.me_dir, 'Cards', card)) 1285 debug_file.write(ff.read()) 1286 ff.close() 1287 except Exception: 1288 pass 1289 1290 1291 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1292 self.options['crash_on_error']: 1293 logger.info('stop computation due to crash_on_error=True') 1294 sys.exit(str(error)) 1295 #stop the execution if on a non interactive mode 1296 if self.use_rawinput == False: 1297 return True 1298 return False
1299 1300 1301
1302 - def nice_user_error(self, error, line):
1303 if self.child: 1304 return self.child.nice_user_error(error, line) 1305 # Make sure that we are at the initial position 1306 os.chdir(self.__initpos) 1307 if not self.history or line == self.history[-1]: 1308 error_text = 'Command \"%s\" interrupted with error:\n' % line 1309 else: 1310 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1311 error_text += '\"%s\" with error:\n' % self.history[-1] 1312 error_text += '%s : %s' % (error.__class__.__name__, 1313 str(error).replace('\n','\n\t')) 1314 logger_stderr.error(error_text) 1315 1316 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1317 self.options['crash_on_error']: 1318 logger.info('stop computation due to crash_on_error=True') 1319 sys.exit(str(error)) 1320 #stop the execution if on a non interactive mode 1321 if self.use_rawinput == False: 1322 return True 1323 # Remove failed command from history 1324 self.history.pop() 1325 return False
1326
1327 - def nice_config_error(self, error, line):
1328 if self.child: 1329 return self.child.nice_user_error(error, line) 1330 # Make sure that we are at the initial position 1331 os.chdir(self.__initpos) 1332 if not self.history or line == self.history[-1]: 1333 error_text = 'Error detected in \"%s\"\n' % line 1334 else: 1335 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 1336 error_text += 'write debug file %s \n' % self.debug_output 1337 self.log = False 1338 super(Cmd,self).onecmd('history %s' % self.debug_output) 1339 debug_file = open(self.debug_output, 'a') 1340 traceback.print_exc(file=debug_file) 1341 error_text += self.config_debug % {'debug' :self.debug_output} 1342 error_text += '%s : %s' % (error.__class__.__name__, 1343 str(error).replace('\n','\n\t')) 1344 logger_stderr.error(error_text) 1345 1346 # Add options status to the debug file 1347 try: 1348 self.do_display('options', debug_file) 1349 except Exception, error: 1350 debug_file.write('Fail to write options with error %s' % error) 1351 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1352 self.options['crash_on_error']: 1353 logger.info('stop computation due to crash_on_error=True') 1354 sys.exit(str(error)) 1355 1356 #stop the execution if on a non interactive mode 1357 if self.use_rawinput == False: 1358 return True 1359 # Remove failed command from history 1360 if self.history: 1361 self.history.pop() 1362 return False
1363
1364 - def onecmd_orig(self, line, **opt):
1365 """Interpret the argument as though it had been typed in response 1366 to the prompt. 1367 1368 The return value is a flag indicating whether interpretation of 1369 commands by the interpreter should stop. 1370 1371 This allow to pass extra argument for internal call. 1372 """ 1373 if '~/' in line and os.environ.has_key('HOME'): 1374 line = line.replace('~/', '%s/' % os.environ['HOME']) 1375 if '#' in line: 1376 line = line.split('#')[0] 1377 1378 line = os.path.expandvars(line) 1379 cmd, arg, line = self.parseline(line) 1380 if not line: 1381 return self.emptyline() 1382 if cmd is None: 1383 return self.default(line) 1384 self.lastcmd = line 1385 if cmd == '': 1386 return self.default(line) 1387 else: 1388 try: 1389 func = getattr(self, 'do_' + cmd) 1390 except AttributeError: 1391 return self.default(line) 1392 return func(arg, **opt)
1393
1394 - def error_handling(self, error, line):
1395 1396 me_dir = '' 1397 if hasattr(self, 'me_dir'): 1398 me_dir = os.path.basename(me_dir) + ' ' 1399 1400 misc.EasterEgg('error') 1401 1402 try: 1403 raise 1404 except self.InvalidCmd as error: 1405 if __debug__: 1406 self.nice_error_handling(error, line) 1407 self.history.pop() 1408 else: 1409 self.nice_user_error(error, line) 1410 if self.allow_notification_center: 1411 misc.apple_notify('Run %sfailed' % me_dir, 1412 'Invalid Command: %s' % error.__class__.__name__) 1413 1414 except self.ConfigurationError as error: 1415 self.nice_config_error(error, line) 1416 if self.allow_notification_center: 1417 misc.apple_notify('Run %sfailed' % me_dir, 1418 'Configuration error') 1419 except Exception as error: 1420 self.nice_error_handling(error, line) 1421 if self.mother: 1422 self.do_quit('') 1423 if self.allow_notification_center: 1424 misc.apple_notify('Run %sfailed' % me_dir, 1425 'Exception: %s' % error.__class__.__name__) 1426 except KeyboardInterrupt as error: 1427 self.stop_on_keyboard_stop() 1428 if __debug__: 1429 self.nice_config_error(error, line) 1430 logger.error(self.keyboard_stop_msg)
1431 1432 1433
1434 - def onecmd(self, line, **opt):
1435 """catch all error and stop properly command accordingly""" 1436 1437 try: 1438 return self.onecmd_orig(line, **opt) 1439 except BaseException, error: 1440 self.error_handling(error, line)
1441 1442
1443 - def stop_on_keyboard_stop(self):
1444 """action to perform to close nicely on a keyboard interupt""" 1445 pass # dummy function
1446
1447 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 1448 precmd=False, postcmd=True, 1449 child=True, **opt):
1450 """for third party call, call the line with pre and postfix treatment 1451 without global error handling """ 1452 1453 1454 if printcmd and not line.startswith('#'): 1455 logger.info(line) 1456 if self.child and child: 1457 current_interface = self.child 1458 else: 1459 current_interface = self 1460 if precmd: 1461 line = current_interface.precmd(line) 1462 if errorhandling: 1463 stop = current_interface.onecmd(line, **opt) 1464 else: 1465 stop = Cmd.onecmd_orig(current_interface, line, **opt) 1466 if postcmd: 1467 stop = current_interface.postcmd(stop, line) 1468 return stop
1469
1470 - def run_cmd(self, line):
1471 """for third party call, call the line with pre and postfix treatment 1472 with global error handling""" 1473 1474 return self.exec_cmd(line, errorhandling=True, precmd=True)
1475
1476 - def emptyline(self):
1477 """If empty line, do nothing. Default is repeat previous command.""" 1478 pass
1479
1480 - def default(self, line, log=True):
1481 """Default action if line is not recognized""" 1482 1483 # Faulty command 1484 if log: 1485 logger.warning("Command \"%s\" not recognized, please try again" % \ 1486 line.split()[0]) 1487 if line.strip() in ['q', '.q', 'stop']: 1488 logger.info("If you want to quit mg5 please type \"exit\".") 1489 1490 if self.history and self.history[-1] == line: 1491 self.history.pop()
1492 1493 # Write the list of command line use in this session
1494 - def do_history(self, line):
1495 """write in a file the suite of command that was used""" 1496 1497 args = self.split_arg(line) 1498 # Check arguments validity 1499 self.check_history(args) 1500 1501 if len(args) == 0: 1502 logger.info('\n'.join(self.history)) 1503 return 1504 elif args[0] == 'clean': 1505 self.history = [] 1506 logger.info('History is cleaned') 1507 return 1508 elif args[0] == '.': 1509 output_file = os.path.join(self._export_dir, 'Cards', \ 1510 'proc_card_mg5.dat') 1511 output_file = open(output_file, 'w') 1512 else: 1513 output_file = open(args[0], 'w') 1514 1515 # Create the command file 1516 text = self.get_history_header() 1517 text += ('\n'.join(self.history) + '\n') 1518 1519 #write this information in a file 1520 output_file.write(text) 1521 output_file.close() 1522 1523 if self.log: 1524 logger.info("History written to " + output_file.name)
1525
1526 - def compile(self, *args, **opts):
1527 """ """ 1528 1529 return misc.compile(nb_core=self.options['nb_core'], *args, **opts)
1530
1531 - def avoid_history_duplicate(self, line, no_break=[]):
1532 """remove all line in history (but the last) starting with line. 1533 up to the point when a line didn't start by something in no_break. 1534 (reading in reverse order)""" 1535 1536 new_history = [] 1537 for i in range(1, len(self.history)+1): 1538 cur_line = self.history[-i] 1539 if i == 1: 1540 new_history.append(cur_line) 1541 elif not any((cur_line.startswith(text) for text in no_break)): 1542 to_add = self.history[:-i+1] 1543 to_add.reverse() 1544 new_history += to_add 1545 break 1546 elif cur_line.startswith(line): 1547 continue 1548 else: 1549 new_history.append(cur_line) 1550 1551 new_history.reverse() 1552 self.history[:] = new_history
1553 1554
1555 - def import_command_file(self, filepath):
1556 # remove this call from history 1557 if self.history: 1558 self.history.pop() 1559 1560 #avoid that command of other file interfere with this one. 1561 previous_store_line = self.get_stored_line() 1562 1563 # Read the lines of the file and execute them 1564 if isinstance(filepath, str): 1565 commandline = open(filepath).readlines() 1566 else: 1567 commandline = filepath 1568 oldinputfile = self.inputfile 1569 oldraw = self.use_rawinput 1570 self.inputfile = (l for l in commandline) # make a generator 1571 self.use_rawinput = False 1572 # Note using "for line in open(filepath)" is not safe since the file 1573 # filepath can be overwritten during the run (leading to weird results) 1574 # Note also that we need a generator and not a list. 1575 for line in self.inputfile: 1576 #remove pointless spaces and \n 1577 line = line.replace('\n', '').strip() 1578 # execute the line 1579 if line: 1580 self.exec_cmd(line, precmd=True) 1581 stored = self.get_stored_line() 1582 while stored: 1583 line = stored 1584 self.exec_cmd(line, precmd=True) 1585 stored = self.get_stored_line() 1586 1587 # If a child was open close it 1588 if self.child: 1589 self.child.exec_cmd('quit') 1590 self.inputfile = oldinputfile 1591 self.use_rawinput = oldraw 1592 1593 # restore original store line 1594 cmd = self 1595 while hasattr(cmd, 'mother') and cmd.mother: 1596 cmd = cmd.mother 1597 cmd.stored_line = previous_store_line 1598 return
1599
1600 - def get_history_header(self):
1601 """Default history header""" 1602 1603 return self.history_header
1604
1605 - def postloop(self):
1606 """ """ 1607 1608 if self.use_rawinput and self.completekey: 1609 try: 1610 import readline 1611 readline.set_completer(self.old_completer) 1612 del self.old_completer 1613 except ImportError: 1614 pass 1615 except AttributeError: 1616 pass 1617 1618 args = self.split_arg(self.lastcmd) 1619 if args and args[0] in ['quit','exit']: 1620 if 'all' in args: 1621 return True 1622 if len(args) >1 and args[1].isdigit(): 1623 if args[1] not in ['0', '1']: 1624 return True 1625 1626 return False
1627 1628 #=============================================================================== 1629 # Ask a question with a maximum amount of time to answer 1630 #=============================================================================== 1631 @staticmethod
1632 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 1633 fct_timeout=None):
1634 """ a question with a maximal time to answer take default otherwise""" 1635 1636 def handle_alarm(signum, frame): 1637 raise TimeOutError
1638 1639 signal.signal(signal.SIGALRM, handle_alarm) 1640 1641 if fct is None: 1642 fct = raw_input 1643 1644 if timeout: 1645 signal.alarm(timeout) 1646 question += '[%ss to answer] ' % (timeout) 1647 try: 1648 result = fct(question) 1649 except TimeOutError: 1650 if noerror: 1651 logger.info('\nuse %s' % default) 1652 if fct_timeout: 1653 fct_timeout(True) 1654 return default 1655 else: 1656 signal.alarm(0) 1657 raise 1658 finally: 1659 signal.alarm(0) 1660 if fct_timeout: 1661 fct_timeout(False) 1662 return result
1663 1664 1665 1666 1667 1668 1669 # Quit
1670 - def do_quit(self, line):
1671 """Not in help: exit the mainloop() """ 1672 1673 if self.child: 1674 self.child.exec_cmd('quit ' + line, printcmd=False) 1675 return 1676 elif self.mother: 1677 self.mother.child = None 1678 if line == 'all': 1679 pass 1680 elif line: 1681 level = int(line) - 1 1682 if level: 1683 self.mother.lastcmd = 'quit %s' % level 1684 logger.info(' ') 1685 return True
1686 1687 # Aliases 1688 do_EOF = do_quit 1689 do_exit = do_quit 1690
1691 - def do_help(self, line):
1692 """Not in help: propose some usefull possible action """ 1693 1694 # if they are an argument use the default help 1695 if line: 1696 return super(Cmd, self).do_help(line) 1697 1698 1699 names = self.get_names() 1700 cmds = {} 1701 names.sort() 1702 # There can be duplicates if routines overridden 1703 prevname = '' 1704 for name in names: 1705 if name[:3] == 'do_': 1706 if name == prevname: 1707 continue 1708 prevname = name 1709 cmdname=name[3:] 1710 try: 1711 doc = getattr(self.cmd, name).__doc__ 1712 except Exception: 1713 doc = None 1714 if not doc: 1715 doc = getattr(self, name).__doc__ 1716 if not doc: 1717 tag = "Documented commands" 1718 elif ':' in doc: 1719 tag = doc.split(':',1)[0] 1720 else: 1721 tag = "Documented commands" 1722 if tag in cmds: 1723 cmds[tag].append(cmdname) 1724 else: 1725 cmds[tag] = [cmdname] 1726 1727 self.stdout.write("%s\n"%str(self.doc_leader)) 1728 for tag in self.helporder: 1729 if tag not in cmds: 1730 continue 1731 header = "%s (type help <topic>):" % tag 1732 self.print_topics(header, cmds[tag], 15,80) 1733 for name, item in cmds.items(): 1734 if name in self.helporder: 1735 continue 1736 if name == "Not in help": 1737 continue 1738 header = "%s (type help <topic>):" % name 1739 self.print_topics(header, item, 15,80) 1740 1741 1742 ## Add contextual help 1743 if len(self.history) == 0: 1744 last_action_2 = last_action = 'start' 1745 else: 1746 last_action_2 = last_action = 'none' 1747 1748 pos = 0 1749 authorize = self.next_possibility.keys() 1750 while last_action_2 not in authorize and last_action not in authorize: 1751 pos += 1 1752 if pos > len(self.history): 1753 last_action_2 = last_action = 'start' 1754 break 1755 1756 args = self.history[-1 * pos].split() 1757 last_action = args[0] 1758 if len(args)>1: 1759 last_action_2 = '%s %s' % (last_action, args[1]) 1760 else: 1761 last_action_2 = 'none' 1762 1763 logger.info('Contextual Help') 1764 logger.info('===============') 1765 if last_action_2 in authorize: 1766 options = self.next_possibility[last_action_2] 1767 elif last_action in authorize: 1768 options = self.next_possibility[last_action] 1769 else: 1770 return 1771 text = 'The following command(s) may be useful in order to continue.\n' 1772 for option in options: 1773 text+='\t %s \n' % option 1774 logger.info(text)
1775
1776 - def do_display(self, line, output=sys.stdout):
1777 """Advanced commands: basic display""" 1778 1779 args = self.split_arg(line) 1780 #check the validity of the arguments 1781 1782 if len(args) == 0: 1783 self.help_display() 1784 raise self.InvalidCmd, 'display require at least one argument' 1785 1786 if args[0] == "options": 1787 outstr = "Value of current Options:\n" 1788 for key, value in self.options.items(): 1789 outstr += '%25s \t:\t%s\n' %(key,value) 1790 output.write(outstr) 1791 1792 elif args[0] == "variable": 1793 outstr = "Value of Internal Variable:\n" 1794 try: 1795 var = eval(args[1]) 1796 except Exception: 1797 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1798 else: 1799 outstr += 'GLOBAL:\n' 1800 outstr += misc.nice_representation(var, nb_space=4) 1801 1802 try: 1803 var = eval('self.%s' % args[1]) 1804 except Exception: 1805 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1806 else: 1807 outstr += 'LOCAL:\n' 1808 outstr += misc.nice_representation(var, nb_space=4) 1809 split = args[1].split('.') 1810 for i, name in enumerate(split): 1811 try: 1812 __import__('.'.join(split[:i+1])) 1813 exec('%s=sys.modules[\'%s\']' % (split[i], '.'.join(split[:i+1]))) 1814 except ImportError: 1815 try: 1816 var = eval(args[1]) 1817 except Exception, error: 1818 outstr += 'EXTERNAL:\nVariable %s is not a external variable\n' % args[1] 1819 break 1820 else: 1821 outstr += 'EXTERNAL:\n' 1822 outstr += misc.nice_representation(var, nb_space=4) 1823 else: 1824 var = eval(args[1]) 1825 outstr += 'EXTERNAL:\n' 1826 outstr += misc.nice_representation(var, nb_space=4) 1827 1828 pydoc.pager(outstr)
1829 1830
1831 - def do_save(self, line, check=True):
1832 """Save the configuration file""" 1833 1834 args = self.split_arg(line) 1835 # Check argument validity 1836 if check: 1837 Cmd.check_save(self, args) 1838 1839 # find base file for the configuration 1840 if 'HOME' in os.environ and os.environ['HOME'] and \ 1841 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1842 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1843 if hasattr(self, 'me_dir'): 1844 basedir = self.me_dir 1845 elif not MADEVENT: 1846 basedir = MG5DIR 1847 else: 1848 basedir = os.getcwd() 1849 elif MADEVENT: 1850 # launch via ./bin/madevent 1851 for config_file in ['me5_configuration.txt', 'amcatnlo_configuration.txt']: 1852 if os.path.exists(pjoin(self.me_dir, 'Cards', config_file)): 1853 base = pjoin(self.me_dir, 'Cards', config_file) 1854 basedir = self.me_dir 1855 else: 1856 if hasattr(self, 'me_dir'): 1857 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1858 if len(args) == 0 and os.path.exists(base): 1859 self.write_configuration(base, base, self.me_dir) 1860 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1861 basedir = MG5DIR 1862 1863 if len(args) == 0: 1864 args.append(base) 1865 self.write_configuration(args[0], base, basedir, self.options)
1866
1867 - def write_configuration(self, filepath, basefile, basedir, to_keep):
1868 """Write the configuration file""" 1869 # We use the default configuration file as a template. 1870 # to ensure that all configuration information are written we 1871 # keep track of all key that we need to write. 1872 1873 logger.info('save configuration file to %s' % filepath) 1874 to_write = to_keep.keys() 1875 text = "" 1876 has_mg5_path = False 1877 # Use local configuration => Need to update the path 1878 for line in file(basefile): 1879 if '=' in line: 1880 data, value = line.split('=',1) 1881 else: 1882 text += line 1883 continue 1884 data = data.strip() 1885 if data.startswith('#'): 1886 key = data[1:].strip() 1887 else: 1888 key = data 1889 if '#' in value: 1890 value, comment = value.split('#',1) 1891 else: 1892 comment = '' 1893 if key in to_keep: 1894 value = str(to_keep[key]) 1895 else: 1896 text += line 1897 continue 1898 if key == 'mg5_path': 1899 has_mg5_path = True 1900 try: 1901 to_write.remove(key) 1902 except Exception: 1903 pass 1904 if '_path' in key: 1905 # special case need to update path 1906 # check if absolute path 1907 if not os.path.isabs(value): 1908 value = os.path.realpath(os.path.join(basedir, value)) 1909 text += '%s = %s # %s \n' % (key, value, comment) 1910 for key in to_write: 1911 if key in to_keep: 1912 text += '%s = %s \n' % (key, to_keep[key]) 1913 1914 if not MADEVENT and not has_mg5_path: 1915 text += """\n# MG5 MAIN DIRECTORY\n""" 1916 text += "mg5_path = %s\n" % MG5DIR 1917 1918 writer = open(filepath,'w') 1919 writer.write(text) 1920 writer.close()
1921
1922 1923 1924 1925 -class CmdShell(Cmd):
1926 """CMD command with shell activate""" 1927 1928 # Access to shell
1929 - def do_shell(self, line):
1930 "Run a shell command" 1931 1932 if line.strip() is '': 1933 self.help_shell() 1934 else: 1935 logging.info("running shell command: " + line) 1936 subprocess.call(line, shell=True)
1937
1938 - def complete_shell(self, text, line, begidx, endidx):
1939 """ add path for shell """ 1940 1941 # Filename if directory is given 1942 # 1943 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 1944 if not text: 1945 text = '' 1946 output = self.path_completion(text, 1947 base_dir=\ 1948 self.split_arg(line[0:begidx])[-1]) 1949 else: 1950 output = self.path_completion(text) 1951 return output
1952
1953 - def help_shell(self):
1954 """help for the shell""" 1955 logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE') 1956 logger.info("syntax: shell CMD (or ! CMD)",'$MG:color:BLACK')
1957
1958 1959 1960 1961 #=============================================================================== 1962 # Question with auto-completion 1963 #=============================================================================== 1964 -class SmartQuestion(BasicCmd):
1965 """ a class for answering a question with the path autocompletion""" 1966 1967 allowpath = False
1968 - def preloop(self):
1969 """Initializing before starting the main loop""" 1970 self.prompt = '>' 1971 self.value = None 1972 BasicCmd.preloop(self)
1973 1974 @property
1975 - def answer(self):
1976 return self.value
1977
1978 - def __init__(self, question, allow_arg=[], default=None, 1979 mother_interface=None, *arg, **opt):
1980 self.question = question 1981 self.wrong_answer = 0 # forbids infinite loop 1982 self.allow_arg = [str(a) for a in allow_arg] 1983 self.history_header = '' 1984 self.default_value = str(default) 1985 self.mother_interface = mother_interface 1986 1987 if 'case' in opt: 1988 self.casesensitive = opt['case'] 1989 del opt['case'] 1990 elif 'casesensitive' in opt: 1991 self.casesensitive = opt['casesensitive'] 1992 del opt['casesensitive'] 1993 else: 1994 self.casesensistive = True 1995 super(SmartQuestion, self).__init__(*arg, **opt)
1996
1997 - def __call__(self, question, reprint_opt=True, **opts):
1998 1999 self.question = question 2000 for key,value in opts: 2001 setattr(self, key, value) 2002 if reprint_opt: 2003 print question 2004 return self.cmdloop()
2005 2006
2007 - def completenames(self, text, line, *ignored):
2008 prev_timer = signal.alarm(0) # avoid timer if any 2009 if prev_timer: 2010 nb_back = len(line) 2011 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2012 self.stdout.write(line) 2013 self.stdout.flush() 2014 try: 2015 out = {} 2016 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2017 out[' Recognized command'] = BasicCmd.completenames(self, text) 2018 2019 return self.deal_multiple_categories(out) 2020 except Exception, error: 2021 print error
2022 2023 completedefault = completenames 2024
2025 - def get_names(self):
2026 # This method used to pull in base class attributes 2027 # at a time dir() didn't do it yet. 2028 return dir(self)
2029
2030 - def onecmd(self, line, **opt):
2031 """catch all error and stop properly command accordingly 2032 Interpret the argument as though it had been typed in response 2033 to the prompt. 2034 2035 The return value is a flag indicating whether interpretation of 2036 commands by the interpreter should stop. 2037 2038 This allow to pass extra argument for internal call. 2039 """ 2040 try: 2041 if '~/' in line and os.environ.has_key('HOME'): 2042 line = line.replace('~/', '%s/' % os.environ['HOME']) 2043 line = os.path.expandvars(line) 2044 cmd, arg, line = self.parseline(line) 2045 if not line: 2046 return self.emptyline() 2047 if cmd is None: 2048 return self.default(line) 2049 self.lastcmd = line 2050 if cmd == '': 2051 return self.default(line) 2052 else: 2053 try: 2054 func = getattr(self, 'do_' + cmd) 2055 except AttributeError: 2056 return self.default(line) 2057 return func(arg, **opt) 2058 except Exception as error: 2059 logger.warning(error) 2060 if __debug__: 2061 raise
2062
2063 - def reask(self, reprint_opt=True):
2064 pat = re.compile('\[(\d*)s to answer\]') 2065 prev_timer = signal.alarm(0) # avoid timer if any 2066 2067 if prev_timer: 2068 if pat.search(self.question): 2069 timeout = int(pat.search(self.question).groups()[0]) 2070 else: 2071 timeout=20 2072 print 2073 signal.alarm(timeout) 2074 if reprint_opt: 2075 if not prev_timer: 2076 self.question = pat.sub('',self.question) 2077 print self.question 2078 2079 if self.mother_interface: 2080 answer = self.mother_interface.check_answer_in_input_file(self, 'EOF', 2081 path=self.allowpath) 2082 if answer: 2083 stop = self.default(answer) 2084 self.postcmd(stop, answer) 2085 return False 2086 2087 return False
2088
2089 - def do_help(self, line):
2090 2091 text=line 2092 out ={} 2093 out['Options'] = Cmd.list_completion(text, self.allow_arg) 2094 out['command'] = BasicCmd.completenames(self, text) 2095 2096 if not text: 2097 if out['Options']: 2098 logger.info( "Here is the list of all valid options:", '$MG:color:BLACK') 2099 logger.info( " "+ "\n ".join(out['Options'])) 2100 if out['command']: 2101 logger.info( "Here is the list of command available:", '$MG:color:BLACK') 2102 logger.info( " "+ "\n ".join(out['command'])) 2103 else: 2104 if out['Options']: 2105 logger.info( "Here is the list of all valid options starting with \'%s\'" % text, '$MG:color:BLACK') 2106 logger.info( " "+ "\n ".join(out['Options'])) 2107 if out['command']: 2108 logger.info( "Here is the list of command available starting with \'%s\':" % text, '$MG:color:BLACK') 2109 logger.info( " "+ "\n ".join(out['command'])) 2110 elif not out['Options']: 2111 logger.info( "No possibility starting with \'%s\'" % text, '$MG:color:BLACK') 2112 logger.info( "You can type help XXX, to see all command starting with XXX", '$MG:color:BLACK')
2113 - def complete_help(self, text, line, begidx, endidx):
2114 """ """ 2115 return self.completenames(text, line)
2116
2117 - def default(self, line):
2118 """Default action if line is not recognized""" 2119 2120 if line.strip() == '' and self.default_value is not None: 2121 self.value = self.default_value 2122 else: 2123 self.value = line
2124
2125 - def emptyline(self):
2126 """If empty line, return default""" 2127 2128 if self.default_value is not None: 2129 self.value = self.default_value
2130 2131
2132 - def postcmd(self, stop, line):
2133 2134 try: 2135 if self.value in self.allow_arg: 2136 return True 2137 elif str(self.value) == 'EOF': 2138 self.value = self.default_value 2139 return True 2140 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2141 return self.reask() 2142 elif self.value == 'repeat': 2143 return self.reask() 2144 elif len(self.allow_arg)==0: 2145 return True 2146 elif ' ' in line.strip() and '=' in self.value: 2147 line,n = re.subn(r'\s*=\s*', '=', line) 2148 if n: 2149 self.default(line) 2150 return self.postcmd(stop, line) 2151 if not self.casesensitive: 2152 for ans in self.allow_arg: 2153 if ans.lower() == self.value.lower(): 2154 self.value = ans 2155 return True 2156 break 2157 else: 2158 raise Exception 2159 2160 2161 else: 2162 raise Exception 2163 except Exception,error: 2164 if self.wrong_answer < 100: 2165 self.wrong_answer += 1 2166 logger.warning("""%s not valid argument. Valid argument are in (%s).""" \ 2167 % (self.value,','.join(self.allow_arg))) 2168 logger.warning('please retry') 2169 return False 2170 else: 2171 self.value = self.default_value 2172 return True
2173
2174 - def cmdloop(self, intro=None):
2175 super(SmartQuestion,self).cmdloop(intro) 2176 return self.answer
2177
2178 # a function helper 2179 -def smart_input(input_text, allow_arg=[], default=None):
2180 print input_text 2181 obj = SmartQuestion(allow_arg=allow_arg, default=default) 2182 return obj.cmdloop()
2183
2184 #=============================================================================== 2185 # Question in order to return a path with auto-completion 2186 #=============================================================================== 2187 -class OneLinePathCompletion(SmartQuestion):
2188 """ a class for answering a question with the path autocompletion""" 2189 2190 completion_prefix='' 2191 allowpath=True 2192
2193 - def completenames(self, text, line, begidx, endidx, formatting=True):
2194 prev_timer = signal.alarm(0) # avoid timer if any 2195 if prev_timer: 2196 nb_back = len(line) 2197 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2198 self.stdout.write(line) 2199 self.stdout.flush() 2200 2201 try: 2202 out = {} 2203 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2204 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 2205 out[' Recognized command'] = BasicCmd.completenames(self, text) 2206 2207 return self.deal_multiple_categories(out, formatting) 2208 except Exception, error: 2209 print error
2210
2211 - def precmd(self, *args):
2212 """ """ 2213 2214 signal.alarm(0) 2215 return SmartQuestion.precmd(self, *args)
2216
2217 - def completedefault(self,text, line, begidx, endidx):
2218 prev_timer = signal.alarm(0) # avoid timer if any 2219 if prev_timer: 2220 nb_back = len(line) 2221 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2222 self.stdout.write(line) 2223 self.stdout.flush() 2224 try: 2225 args = Cmd.split_arg(line[0:begidx]) 2226 except Exception, error: 2227 print error 2228 2229 # Directory continuation 2230 if args[-1].endswith(os.path.sep): 2231 2232 return Cmd.path_completion(text, 2233 os.path.join('.',*[a for a in args \ 2234 if a.endswith(os.path.sep)])) 2235 self.completenames(line+text)
2236 2237
2238 - def postcmd(self, stop, line):
2239 try: 2240 if self.value in self.allow_arg: 2241 return True 2242 elif self.value and os.path.isfile(self.value): 2243 return os.path.relpath(self.value) 2244 elif self.value and str(self.value) == 'EOF': 2245 self.value = self.default_value 2246 return True 2247 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2248 # go to retry 2249 reprint_opt = True 2250 elif self.value == 'repeat': 2251 reprint_opt = True 2252 else: 2253 raise Exception 2254 except Exception, error: 2255 print """not valid argument. Valid argument are file path or value in (%s).""" \ 2256 % ','.join(self.allow_arg) 2257 print 'please retry' 2258 reprint_opt = False 2259 2260 if line != 'EOF': 2261 return self.reask(reprint_opt)
2262
2263 2264 # a function helper 2265 -def raw_path_input(input_text, allow_arg=[], default=None):
2266 print input_text 2267 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 2268 return obj.cmdloop()
2269
2270 #=============================================================================== 2271 # 2272 #=============================================================================== 2273 -class CmdFile(file):
2274 """ a class for command input file -in order to debug cmd \n problem""" 2275
2276 - def __init__(self, name, opt='rU'):
2277 2278 file.__init__(self, name, opt) 2279 self.text = file.read(self) 2280 self.close() 2281 self.lines = self.text.split('\n')
2282
2283 - def readline(self, *arg, **opt):
2284 """readline method treating correctly a line whithout \n at the end 2285 (add it) 2286 """ 2287 if self.lines: 2288 line = self.lines.pop(0) 2289 else: 2290 return '' 2291 2292 if line.endswith('\n'): 2293 return line 2294 else: 2295 return line + '\n'
2296
2297 - def __next__(self):
2298 return self.lines.__next__()
2299
2300 - def __iter__(self):
2301 return self.lines.__iter__()
2302