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 
  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 866 if MADEVENT: 867 plugin_path = [] 868 else: 869 plugin_path = [pjoin(MG5DIR, 'PLUGIN')] 870 if 'PYTHONPATH' in os.environ: 871 for PluginCandidate in os.environ['PYTHONPATH'].split(':'): 872 try: 873 dirlist = os.listdir(PluginCandidate) 874 except OSError: 875 continue 876 for onedir in dirlist: 877 if onedir == 'MG5aMC_PLUGIN': 878 plugin_path.append(pjoin(PluginCandidate, 'MG5aMC_PLUGIN')) 879 break 880 else: 881 continue 882 break 883
884 - def __init__(self, *arg, **opt):
885 """Init history and line continuation""" 886 887 self.log = True 888 self.history = [] 889 self.save_line = '' # for line splitting 890 super(Cmd, self).__init__(*arg, **opt) 891 self.__initpos = os.path.abspath(os.getcwd()) 892 self.child = None # sub CMD interface call from this one 893 self.mother = None #This CMD interface was called from another one 894 self.inputfile = None # input file (in non interactive mode) 895 self.haspiping = not sys.stdin.isatty() # check if mg5 is piped 896 self.stored_line = '' # for be able to treat answer to question in input file 897 # answer which are not required. 898 if not hasattr(self, 'helporder'): 899 self.helporder = ['Documented commands']
900
901 - def preloop(self):
902 """Hook method executed once when the cmdloop() method is called.""" 903 if self.completekey: 904 try: 905 import readline 906 self.old_completer = readline.get_completer() 907 readline.set_completer(self.complete) 908 readline.parse_and_bind(self.completekey+": complete") 909 except ImportError: 910 readline = None 911 pass 912 if readline and not 'libedit' in readline.__doc__: 913 readline.set_completion_display_matches_hook(self.print_suggestions)
914 915
916 - def cmdloop(self, intro=None):
917 918 self.preloop() 919 if intro is not None: 920 self.intro = intro 921 if self.intro: 922 print self.intro 923 stop = None 924 while not stop: 925 if self.cmdqueue: 926 line = self.cmdqueue[0] 927 del self.cmdqueue[0] 928 else: 929 if self.use_rawinput: 930 try: 931 line = raw_input(self.prompt) 932 except EOFError: 933 line = 'EOF' 934 else: 935 sys.stdout.write(self.prompt) 936 sys.stdout.flush() 937 line = sys.stdin.readline() 938 if not len(line): 939 line = 'EOF' 940 else: 941 line = line[:-1] # chop \n 942 try: 943 line = self.precmd(line) 944 stop = self.onecmd(line) 945 except BaseException, error: 946 self.error_handling(error, line) 947 if isinstance(error, KeyboardInterrupt): 948 stop = True 949 finally: 950 stop = self.postcmd(stop, line) 951 self.postloop()
952
953 - def no_notification(self):
954 """avoid to have html opening / notification""" 955 self.allow_notification_center = False 956 try: 957 self.options['automatic_html_opening'] = False 958 self.options['notification_center'] = False 959 960 except: 961 pass
962 963
964 - def precmd(self, line):
965 """ A suite of additional function needed for in the cmd 966 this implement history, line breaking, comment treatment,... 967 """ 968 969 if not line: 970 return line 971 972 # Check if we are continuing a line: 973 if self.save_line: 974 line = self.save_line + line 975 self.save_line = '' 976 977 line = line.lstrip() 978 # Check if the line is complete 979 if line.endswith('\\'): 980 self.save_line = line[:-1] 981 return '' # do nothing 982 983 # Remove comment 984 if '#' in line: 985 line = line.split('#')[0] 986 987 # Deal with line splitting 988 if ';' in line: 989 lines = line.split(';') 990 for subline in lines: 991 if not (subline.startswith("history") or subline.startswith('help') \ 992 or subline.startswith('#*')): 993 self.history.append(subline) 994 stop = self.onecmd_orig(subline) 995 stop = self.postcmd(stop, subline) 996 return '' 997 998 # execute the line command 999 self.history.append(line) 1000 return line
1001
1002 - def postcmd(self,stop, line):
1003 """ finishing a command 1004 This looks if the command add a special post part.""" 1005 1006 if line.strip(): 1007 try: 1008 cmd, subline = line.split(None, 1) 1009 except ValueError: 1010 pass 1011 else: 1012 if hasattr(self,'post_%s' %cmd): 1013 stop = getattr(self, 'post_%s' % cmd)(stop, subline) 1014 return stop
1015
1016 - def define_child_cmd_interface(self, obj_instance, interface=True):
1017 """Define a sub cmd_interface""" 1018 1019 # We are in a file reading mode. So we need to redirect the cmd 1020 self.child = obj_instance 1021 self.child.mother = self 1022 1023 #ensure that notification are sync: 1024 self.child.allow_notification_center = self.allow_notification_center 1025 1026 if self.use_rawinput and interface: 1027 # We are in interactive mode -> simply call the child 1028 obj_instance.cmdloop() 1029 stop = obj_instance.postloop() 1030 return stop 1031 if self.inputfile: 1032 # we are in non interactive mode -> so pass the line information 1033 obj_instance.inputfile = self.inputfile 1034 1035 obj_instance.haspiping = self.haspiping 1036 1037 if not interface: 1038 return self.child
1039 1040 #=============================================================================== 1041 # Ask a question with nice options handling 1042 #===============================================================================
1043 - def ask(self, question, default, choices=[], path_msg=None, 1044 timeout = True, fct_timeout=None, ask_class=None, alias={}, 1045 first_cmd=None, text_format='4', **opt):
1046 """ ask a question with some pre-define possibility 1047 path info is 1048 """ 1049 1050 if path_msg: 1051 path_msg = [path_msg] 1052 else: 1053 path_msg = [] 1054 1055 if timeout is True: 1056 try: 1057 timeout = self.options['timeout'] 1058 except Exception: 1059 pass 1060 1061 # add choice info to the question 1062 if choices + path_msg: 1063 question += ' [' 1064 question += "\033[%sm%s\033[0m, " % (text_format, default) 1065 for data in choices[:9] + path_msg: 1066 if default == data: 1067 continue 1068 else: 1069 question += "%s, " % data 1070 1071 if len(choices) > 9: 1072 question += '... , ' 1073 question = question[:-2]+']' 1074 else: 1075 question += "[\033[%sm%s\033[0m] " % (text_format, default) 1076 if ask_class: 1077 obj = ask_class 1078 elif path_msg: 1079 obj = OneLinePathCompletion 1080 else: 1081 obj = SmartQuestion 1082 1083 if alias: 1084 choices += alias.keys() 1085 1086 question_instance = obj(question, allow_arg=choices, default=default, 1087 mother_interface=self, **opt) 1088 1089 if first_cmd: 1090 if isinstance(first_cmd, str): 1091 question_instance.onecmd(first_cmd) 1092 else: 1093 for line in first_cmd: 1094 question_instance.onecmd(line) 1095 if not self.haspiping: 1096 if hasattr(obj, "haspiping"): 1097 obj.haspiping = self.haspiping 1098 1099 1100 1101 1102 answer = self.check_answer_in_input_file(question_instance, default, path_msg) 1103 if answer is not None: 1104 if answer in alias: 1105 answer = alias[answer] 1106 if ask_class: 1107 line=answer 1108 answer = question_instance.default(line) 1109 question_instance.postcmd(answer, line) 1110 return question_instance.answer 1111 if hasattr(question_instance, 'check_answer_consistency'): 1112 question_instance.check_answer_consistency() 1113 return answer 1114 1115 question = question_instance.question 1116 value = Cmd.timed_input(question, default, timeout=timeout, 1117 fct=question_instance, fct_timeout=fct_timeout) 1118 1119 try: 1120 if value in alias: 1121 value = alias[value] 1122 except TypeError: 1123 pass 1124 1125 if value == default and ask_class: 1126 value = question_instance.default(default) 1127 return value
1128
1129 - def do_import(self, line):
1130 """Advanced commands: Import command files""" 1131 1132 args = self.split_arg(line) 1133 # Check argument's validity 1134 self.check_import(args) 1135 1136 # Execute the card 1137 self.import_command_file(args[1])
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):
1154 """Questions can have answer in output file (or not)""" 1155 1156 if not self.inputfile: 1157 return None# interactive mode 1158 1159 line = self.get_stored_line() 1160 # line define if a previous answer was not answer correctly 1161 if not line: 1162 try: 1163 line = self.inputfile.next() 1164 except StopIteration: 1165 if self.haspiping: 1166 logger.debug('piping') 1167 self.store_line(line) 1168 return None # print the question and use the pipe 1169 logger.info(question_instance.question) 1170 logger.info('The answer to the previous question is not set in your input file', '$MG:color:BLACK') 1171 logger.info('Use %s value' % default, '$MG:color:BLACK') 1172 return str(default) 1173 1174 line = line.replace('\n','').strip() 1175 if '#' in line: 1176 line = line.split('#')[0] 1177 if not line: 1178 # Comment or empty line, pass to the next one 1179 return self.check_answer_in_input_file(question_instance, default, path) 1180 options = question_instance.allow_arg 1181 if line in options or line.lower() in options: 1182 return line 1183 elif hasattr(question_instance, 'do_%s' % line.split()[0]): 1184 #This is a command line, exec it and check next line 1185 logger.info(line) 1186 fct = getattr(question_instance, 'do_%s' % line.split()[0]) 1187 fct(' '.join(line.split()[1:])) 1188 return self.check_answer_in_input_file(question_instance, default, path) 1189 elif path: 1190 line = os.path.expanduser(os.path.expandvars(line)) 1191 if os.path.isfile(line): 1192 return line 1193 elif hasattr(question_instance, 'casesensitive') and not question_instance.casesensitive: 1194 for entry in question_instance.allow_arg: 1195 if line.lower() == entry.lower(): 1196 return entry 1197 elif any(line.lower()==opt.lower() for opt in options): 1198 possibility = [opt for opt in options if line.lower()==opt.lower()] 1199 if len (possibility)==1: 1200 return possibility[0] 1201 1202 # No valid answer provides 1203 if self.haspiping: 1204 self.store_line(line) 1205 return None # print the question and use the pipe 1206 else: 1207 logger.info(question_instance.question) 1208 logger.warning('The answer to the previous question is not set in your input file') 1209 logger.warning('Use %s value' % default) 1210 self.store_line(line) 1211 return str(default)
1212
1213 - def store_line(self, line):
1214 """store a line of the input file which should be executed by the higher mother""" 1215 1216 if self.mother: 1217 self.mother.store_line(line) 1218 else: 1219 self.stored_line = line
1220
1221 - def get_stored_line(self):
1222 """return stored line and clean it""" 1223 if self.mother: 1224 value = self.mother.get_stored_line() 1225 self.mother.stored_line = None 1226 else: 1227 value = self.stored_line 1228 self.stored_line = None 1229 return value
1230 1231 1232
1233 - def nice_error_handling(self, error, line):
1234 """ """ 1235 # Make sure that we are at the initial position 1236 if self.child: 1237 return self.child.nice_error_handling(error, line) 1238 1239 os.chdir(self.__initpos) 1240 # Create the debug files 1241 self.log = False 1242 if os.path.exists(self.debug_output): 1243 os.remove(self.debug_output) 1244 try: 1245 super(Cmd,self).onecmd('history %s' % self.debug_output.replace(' ', '\ ')) 1246 except Exception, error: 1247 logger.error(error) 1248 1249 debug_file = open(self.debug_output, 'a') 1250 traceback.print_exc(file=debug_file) 1251 if hasattr(error, 'filename'): 1252 debug_file.write("Related File: %s\n" % error.filename) 1253 # Create a nice error output 1254 if self.history and line == self.history[-1]: 1255 error_text = 'Command \"%s\" interrupted with error:\n' % line 1256 elif self.history: 1257 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1258 error_text += '\"%s\" with error:\n' % self.history[-1] 1259 else: 1260 error_text = '' 1261 error_text += '%s : %s\n' % (error.__class__.__name__, 1262 str(error).replace('\n','\n\t')) 1263 error_text += self.error_debug % {'debug':self.debug_output} 1264 logger_stderr.critical(error_text) 1265 1266 1267 # Add options status to the debug file 1268 try: 1269 self.do_display('options', debug_file) 1270 except Exception, error: 1271 debug_file.write('Fail to write options with error %s' % error) 1272 1273 #add the cards: 1274 for card in ['proc_card_mg5.dat','param_card.dat', 'run_card.dat']: 1275 try: 1276 ff = open(pjoin(self.me_dir, 'Cards', card)) 1277 debug_file.write(ff.read()) 1278 ff.close() 1279 except Exception: 1280 pass 1281 1282 1283 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1284 self.options['crash_on_error']: 1285 logger.info('stop computation due to crash_on_error=True') 1286 sys.exit(str(error)) 1287 #stop the execution if on a non interactive mode 1288 if self.use_rawinput == False: 1289 return True 1290 return False
1291 1292 1293
1294 - def nice_user_error(self, error, line):
1295 if self.child: 1296 return self.child.nice_user_error(error, line) 1297 # Make sure that we are at the initial position 1298 os.chdir(self.__initpos) 1299 if not self.history or line == self.history[-1]: 1300 error_text = 'Command \"%s\" interrupted with error:\n' % line 1301 else: 1302 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1303 error_text += '\"%s\" with error:\n' % self.history[-1] 1304 error_text += '%s : %s' % (error.__class__.__name__, 1305 str(error).replace('\n','\n\t')) 1306 logger_stderr.error(error_text) 1307 1308 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1309 self.options['crash_on_error']: 1310 logger.info('stop computation due to crash_on_error=True') 1311 sys.exit(str(error)) 1312 #stop the execution if on a non interactive mode 1313 if self.use_rawinput == False: 1314 return True 1315 # Remove failed command from history 1316 self.history.pop() 1317 return False
1318
1319 - def nice_config_error(self, error, line):
1320 if self.child: 1321 return self.child.nice_user_error(error, line) 1322 # Make sure that we are at the initial position 1323 os.chdir(self.__initpos) 1324 if not self.history or line == self.history[-1]: 1325 error_text = 'Error detected in \"%s\"\n' % line 1326 else: 1327 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 1328 error_text += 'write debug file %s \n' % self.debug_output 1329 self.log = False 1330 super(Cmd,self).onecmd('history %s' % self.debug_output) 1331 debug_file = open(self.debug_output, 'a') 1332 traceback.print_exc(file=debug_file) 1333 error_text += self.config_debug % {'debug' :self.debug_output} 1334 error_text += '%s : %s' % (error.__class__.__name__, 1335 str(error).replace('\n','\n\t')) 1336 logger_stderr.error(error_text) 1337 1338 # Add options status to the debug file 1339 try: 1340 self.do_display('options', debug_file) 1341 except Exception, error: 1342 debug_file.write('Fail to write options with error %s' % error) 1343 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1344 self.options['crash_on_error']: 1345 logger.info('stop computation due to crash_on_error=True') 1346 sys.exit(str(error)) 1347 1348 #stop the execution if on a non interactive mode 1349 if self.use_rawinput == False: 1350 return True 1351 # Remove failed command from history 1352 if self.history: 1353 self.history.pop() 1354 return False
1355
1356 - def onecmd_orig(self, line, **opt):
1357 """Interpret the argument as though it had been typed in response 1358 to the prompt. 1359 1360 The return value is a flag indicating whether interpretation of 1361 commands by the interpreter should stop. 1362 1363 This allow to pass extra argument for internal call. 1364 """ 1365 if '~/' in line and os.environ.has_key('HOME'): 1366 line = line.replace('~/', '%s/' % os.environ['HOME']) 1367 if '#' in line: 1368 line = line.split('#')[0] 1369 1370 line = os.path.expandvars(line) 1371 cmd, arg, line = self.parseline(line) 1372 if not line: 1373 return self.emptyline() 1374 if cmd is None: 1375 return self.default(line) 1376 self.lastcmd = line 1377 if cmd == '': 1378 return self.default(line) 1379 else: 1380 try: 1381 func = getattr(self, 'do_' + cmd) 1382 except AttributeError: 1383 return self.default(line) 1384 return func(arg, **opt)
1385
1386 - def error_handling(self, error, line):
1387 1388 me_dir = '' 1389 if hasattr(self, 'me_dir'): 1390 me_dir = os.path.basename(me_dir) + ' ' 1391 1392 misc.EasterEgg('error') 1393 1394 try: 1395 raise 1396 except self.InvalidCmd as error: 1397 if __debug__: 1398 self.nice_error_handling(error, line) 1399 self.history.pop() 1400 else: 1401 self.nice_user_error(error, line) 1402 if self.allow_notification_center: 1403 misc.apple_notify('Run %sfailed' % me_dir, 1404 'Invalid Command: %s' % error.__class__.__name__) 1405 1406 except self.ConfigurationError as error: 1407 self.nice_config_error(error, line) 1408 if self.allow_notification_center: 1409 misc.apple_notify('Run %sfailed' % me_dir, 1410 'Configuration error') 1411 except Exception as error: 1412 self.nice_error_handling(error, line) 1413 if self.mother: 1414 self.do_quit('') 1415 if self.allow_notification_center: 1416 misc.apple_notify('Run %sfailed' % me_dir, 1417 'Exception: %s' % error.__class__.__name__) 1418 except KeyboardInterrupt as error: 1419 self.stop_on_keyboard_stop() 1420 if __debug__: 1421 self.nice_config_error(error, line) 1422 logger.error(self.keyboard_stop_msg)
1423 1424 1425
1426 - def onecmd(self, line, **opt):
1427 """catch all error and stop properly command accordingly""" 1428 1429 try: 1430 return self.onecmd_orig(line, **opt) 1431 except BaseException, error: 1432 self.error_handling(error, line)
1433 1434
1435 - def stop_on_keyboard_stop(self):
1436 """action to perform to close nicely on a keyboard interupt""" 1437 pass # dummy function
1438
1439 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 1440 precmd=False, postcmd=True, 1441 child=True, **opt):
1442 """for third party call, call the line with pre and postfix treatment 1443 without global error handling """ 1444 1445 1446 if printcmd and not line.startswith('#'): 1447 logger.info(line) 1448 if self.child and child: 1449 current_interface = self.child 1450 else: 1451 current_interface = self 1452 if precmd: 1453 line = current_interface.precmd(line) 1454 if errorhandling: 1455 stop = current_interface.onecmd(line, **opt) 1456 else: 1457 stop = Cmd.onecmd_orig(current_interface, line, **opt) 1458 if postcmd: 1459 stop = current_interface.postcmd(stop, line) 1460 return stop
1461
1462 - def run_cmd(self, line):
1463 """for third party call, call the line with pre and postfix treatment 1464 with global error handling""" 1465 1466 return self.exec_cmd(line, errorhandling=True, precmd=True)
1467
1468 - def emptyline(self):
1469 """If empty line, do nothing. Default is repeat previous command.""" 1470 pass
1471
1472 - def default(self, line, log=True):
1473 """Default action if line is not recognized""" 1474 1475 # Faulty command 1476 if log: 1477 logger.warning("Command \"%s\" not recognized, please try again" % \ 1478 line.split()[0]) 1479 if line.strip() in ['q', '.q', 'stop']: 1480 logger.info("If you want to quit mg5 please type \"exit\".") 1481 1482 if self.history and self.history[-1] == line: 1483 self.history.pop()
1484 1485 # Write the list of command line use in this session
1486 - def do_history(self, line):
1487 """write in a file the suite of command that was used""" 1488 1489 args = self.split_arg(line) 1490 # Check arguments validity 1491 self.check_history(args) 1492 1493 if len(args) == 0: 1494 logger.info('\n'.join(self.history)) 1495 return 1496 elif args[0] == 'clean': 1497 self.history = [] 1498 logger.info('History is cleaned') 1499 return 1500 elif args[0] == '.': 1501 output_file = os.path.join(self._export_dir, 'Cards', \ 1502 'proc_card_mg5.dat') 1503 output_file = open(output_file, 'w') 1504 else: 1505 output_file = open(args[0], 'w') 1506 1507 # Create the command file 1508 text = self.get_history_header() 1509 text += ('\n'.join(self.history) + '\n') 1510 1511 #write this information in a file 1512 output_file.write(text) 1513 output_file.close() 1514 1515 if self.log: 1516 logger.info("History written to " + output_file.name)
1517
1518 - def compile(self, *args, **opts):
1519 """ """ 1520 1521 return misc.compile(nb_core=self.options['nb_core'], *args, **opts)
1522
1523 - def avoid_history_duplicate(self, line, no_break=[]):
1524 """remove all line in history (but the last) starting with line. 1525 up to the point when a line didn't start by something in no_break. 1526 (reading in reverse order)""" 1527 1528 new_history = [] 1529 for i in range(1, len(self.history)+1): 1530 cur_line = self.history[-i] 1531 if i == 1: 1532 new_history.append(cur_line) 1533 elif not any((cur_line.startswith(text) for text in no_break)): 1534 to_add = self.history[:-i+1] 1535 to_add.reverse() 1536 new_history += to_add 1537 break 1538 elif cur_line.startswith(line): 1539 continue 1540 else: 1541 new_history.append(cur_line) 1542 1543 new_history.reverse() 1544 self.history[:] = new_history
1545 1546
1547 - def import_command_file(self, filepath):
1548 # remove this call from history 1549 if self.history: 1550 self.history.pop() 1551 1552 #avoid that command of other file interfere with this one. 1553 previous_store_line = self.get_stored_line() 1554 1555 # Read the lines of the file and execute them 1556 if isinstance(filepath, str): 1557 commandline = open(filepath).readlines() 1558 else: 1559 commandline = filepath 1560 oldinputfile = self.inputfile 1561 oldraw = self.use_rawinput 1562 self.inputfile = (l for l in commandline) # make a generator 1563 self.use_rawinput = False 1564 # Note using "for line in open(filepath)" is not safe since the file 1565 # filepath can be overwritten during the run (leading to weird results) 1566 # Note also that we need a generator and not a list. 1567 for line in self.inputfile: 1568 #remove pointless spaces and \n 1569 line = line.replace('\n', '').strip() 1570 # execute the line 1571 if line: 1572 self.exec_cmd(line, precmd=True) 1573 stored = self.get_stored_line() 1574 while stored: 1575 line = stored 1576 self.exec_cmd(line, precmd=True) 1577 stored = self.get_stored_line() 1578 1579 # If a child was open close it 1580 if self.child: 1581 self.child.exec_cmd('quit') 1582 self.inputfile = oldinputfile 1583 self.use_rawinput = oldraw 1584 1585 # restore original store line 1586 cmd = self 1587 while hasattr(cmd, 'mother') and cmd.mother: 1588 cmd = cmd.mother 1589 cmd.stored_line = previous_store_line 1590 return
1591
1592 - def get_history_header(self):
1593 """Default history header""" 1594 1595 return self.history_header
1596
1597 - def postloop(self):
1598 """ """ 1599 1600 if self.use_rawinput and self.completekey: 1601 try: 1602 import readline 1603 readline.set_completer(self.old_completer) 1604 del self.old_completer 1605 except ImportError: 1606 pass 1607 except AttributeError: 1608 pass 1609 1610 args = self.split_arg(self.lastcmd) 1611 if args and args[0] in ['quit','exit']: 1612 if 'all' in args: 1613 return True 1614 if len(args) >1 and args[1].isdigit(): 1615 if args[1] not in ['0', '1']: 1616 return True 1617 1618 return False
1619 1620 #=============================================================================== 1621 # Ask a question with a maximum amount of time to answer 1622 #=============================================================================== 1623 @staticmethod
1624 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 1625 fct_timeout=None):
1626 """ a question with a maximal time to answer take default otherwise""" 1627 1628 def handle_alarm(signum, frame): 1629 raise TimeOutError
1630 1631 signal.signal(signal.SIGALRM, handle_alarm) 1632 1633 if fct is None: 1634 fct = raw_input 1635 1636 if timeout: 1637 signal.alarm(timeout) 1638 question += '[%ss to answer] ' % (timeout) 1639 try: 1640 result = fct(question) 1641 except TimeOutError: 1642 if noerror: 1643 logger.info('\nuse %s' % default) 1644 if fct_timeout: 1645 fct_timeout(True) 1646 return default 1647 else: 1648 signal.alarm(0) 1649 raise 1650 finally: 1651 signal.alarm(0) 1652 if fct_timeout: 1653 fct_timeout(False) 1654 return result
1655 1656 1657 1658 1659 1660 1661 # Quit
1662 - def do_quit(self, line):
1663 """Not in help: exit the mainloop() """ 1664 1665 if self.child: 1666 self.child.exec_cmd('quit ' + line, printcmd=False) 1667 return 1668 elif self.mother: 1669 self.mother.child = None 1670 if line == 'all': 1671 pass 1672 elif line: 1673 level = int(line) - 1 1674 if level: 1675 self.mother.lastcmd = 'quit %s' % level 1676 logger.info(' ') 1677 return True
1678 1679 # Aliases 1680 do_EOF = do_quit 1681 do_exit = do_quit 1682
1683 - def do_help(self, line):
1684 """Not in help: propose some usefull possible action """ 1685 1686 # if they are an argument use the default help 1687 if line: 1688 return super(Cmd, self).do_help(line) 1689 1690 1691 names = self.get_names() 1692 cmds = {} 1693 names.sort() 1694 # There can be duplicates if routines overridden 1695 prevname = '' 1696 for name in names: 1697 if name[:3] == 'do_': 1698 if name == prevname: 1699 continue 1700 prevname = name 1701 cmdname=name[3:] 1702 try: 1703 doc = getattr(self.cmd, name).__doc__ 1704 except Exception: 1705 doc = None 1706 if not doc: 1707 doc = getattr(self, name).__doc__ 1708 if not doc: 1709 tag = "Documented commands" 1710 elif ':' in doc: 1711 tag = doc.split(':',1)[0] 1712 else: 1713 tag = "Documented commands" 1714 if tag in cmds: 1715 cmds[tag].append(cmdname) 1716 else: 1717 cmds[tag] = [cmdname] 1718 1719 self.stdout.write("%s\n"%str(self.doc_leader)) 1720 for tag in self.helporder: 1721 if tag not in cmds: 1722 continue 1723 header = "%s (type help <topic>):" % tag 1724 self.print_topics(header, cmds[tag], 15,80) 1725 for name, item in cmds.items(): 1726 if name in self.helporder: 1727 continue 1728 if name == "Not in help": 1729 continue 1730 header = "%s (type help <topic>):" % name 1731 self.print_topics(header, item, 15,80) 1732 1733 1734 ## Add contextual help 1735 if len(self.history) == 0: 1736 last_action_2 = last_action = 'start' 1737 else: 1738 last_action_2 = last_action = 'none' 1739 1740 pos = 0 1741 authorize = self.next_possibility.keys() 1742 while last_action_2 not in authorize and last_action not in authorize: 1743 pos += 1 1744 if pos > len(self.history): 1745 last_action_2 = last_action = 'start' 1746 break 1747 1748 args = self.history[-1 * pos].split() 1749 last_action = args[0] 1750 if len(args)>1: 1751 last_action_2 = '%s %s' % (last_action, args[1]) 1752 else: 1753 last_action_2 = 'none' 1754 1755 logger.info('Contextual Help') 1756 logger.info('===============') 1757 if last_action_2 in authorize: 1758 options = self.next_possibility[last_action_2] 1759 elif last_action in authorize: 1760 options = self.next_possibility[last_action] 1761 else: 1762 return 1763 text = 'The following command(s) may be useful in order to continue.\n' 1764 for option in options: 1765 text+='\t %s \n' % option 1766 logger.info(text)
1767
1768 - def do_display(self, line, output=sys.stdout):
1769 """Advanced commands: basic display""" 1770 1771 args = self.split_arg(line) 1772 #check the validity of the arguments 1773 1774 if len(args) == 0: 1775 self.help_display() 1776 raise self.InvalidCmd, 'display require at least one argument' 1777 1778 if args[0] == "options": 1779 outstr = "Value of current Options:\n" 1780 for key, value in self.options.items(): 1781 outstr += '%25s \t:\t%s\n' %(key,value) 1782 output.write(outstr) 1783 1784 elif args[0] == "variable": 1785 outstr = "Value of Internal Variable:\n" 1786 try: 1787 var = eval(args[1]) 1788 except Exception: 1789 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1790 else: 1791 outstr += 'GLOBAL:\n' 1792 outstr += misc.nice_representation(var, nb_space=4) 1793 1794 try: 1795 var = eval('self.%s' % args[1]) 1796 except Exception: 1797 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1798 else: 1799 outstr += 'LOCAL:\n' 1800 outstr += misc.nice_representation(var, nb_space=4) 1801 split = args[1].split('.') 1802 for i, name in enumerate(split): 1803 try: 1804 __import__('.'.join(split[:i+1])) 1805 exec('%s=sys.modules[\'%s\']' % (split[i], '.'.join(split[:i+1]))) 1806 except ImportError: 1807 try: 1808 var = eval(args[1]) 1809 except Exception, error: 1810 outstr += 'EXTERNAL:\nVariable %s is not a external variable\n' % args[1] 1811 break 1812 else: 1813 outstr += 'EXTERNAL:\n' 1814 outstr += misc.nice_representation(var, nb_space=4) 1815 else: 1816 var = eval(args[1]) 1817 outstr += 'EXTERNAL:\n' 1818 outstr += misc.nice_representation(var, nb_space=4) 1819 1820 pydoc.pager(outstr)
1821 1822
1823 - def do_save(self, line, check=True):
1824 """Save the configuration file""" 1825 1826 args = self.split_arg(line) 1827 # Check argument validity 1828 if check: 1829 Cmd.check_save(self, args) 1830 1831 # find base file for the configuration 1832 if 'HOME' in os.environ and os.environ['HOME'] and \ 1833 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1834 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1835 if hasattr(self, 'me_dir'): 1836 basedir = self.me_dir 1837 elif not MADEVENT: 1838 basedir = MG5DIR 1839 else: 1840 basedir = os.getcwd() 1841 elif MADEVENT: 1842 # launch via ./bin/madevent 1843 for config_file in ['me5_configuration.txt', 'amcatnlo_configuration.txt']: 1844 if os.path.exists(pjoin(self.me_dir, 'Cards', config_file)): 1845 base = pjoin(self.me_dir, 'Cards', config_file) 1846 basedir = self.me_dir 1847 else: 1848 if hasattr(self, 'me_dir'): 1849 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1850 if len(args) == 0 and os.path.exists(base): 1851 self.write_configuration(base, base, self.me_dir) 1852 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1853 basedir = MG5DIR 1854 1855 if len(args) == 0: 1856 args.append(base) 1857 self.write_configuration(args[0], base, basedir, self.options)
1858
1859 - def write_configuration(self, filepath, basefile, basedir, to_keep):
1860 """Write the configuration file""" 1861 # We use the default configuration file as a template. 1862 # to ensure that all configuration information are written we 1863 # keep track of all key that we need to write. 1864 1865 logger.info('save configuration file to %s' % filepath) 1866 to_write = to_keep.keys() 1867 text = "" 1868 has_mg5_path = False 1869 # Use local configuration => Need to update the path 1870 for line in file(basefile): 1871 if '=' in line: 1872 data, value = line.split('=',1) 1873 else: 1874 text += line 1875 continue 1876 data = data.strip() 1877 if data.startswith('#'): 1878 key = data[1:].strip() 1879 else: 1880 key = data 1881 if '#' in value: 1882 value, comment = value.split('#',1) 1883 else: 1884 comment = '' 1885 if key in to_keep: 1886 value = str(to_keep[key]) 1887 else: 1888 text += line 1889 continue 1890 if key == 'mg5_path': 1891 has_mg5_path = True 1892 try: 1893 to_write.remove(key) 1894 except Exception: 1895 pass 1896 if '_path' in key: 1897 # special case need to update path 1898 # check if absolute path 1899 if not os.path.isabs(value): 1900 value = os.path.realpath(os.path.join(basedir, value)) 1901 text += '%s = %s # %s \n' % (key, value, comment) 1902 for key in to_write: 1903 if key in to_keep: 1904 text += '%s = %s \n' % (key, to_keep[key]) 1905 1906 if not MADEVENT and not has_mg5_path: 1907 text += """\n# MG5 MAIN DIRECTORY\n""" 1908 text += "mg5_path = %s\n" % MG5DIR 1909 1910 writer = open(filepath,'w') 1911 writer.write(text) 1912 writer.close()
1913
1914 1915 1916 1917 -class CmdShell(Cmd):
1918 """CMD command with shell activate""" 1919 1920 # Access to shell
1921 - def do_shell(self, line):
1922 "Run a shell command" 1923 1924 if line.strip() is '': 1925 self.help_shell() 1926 else: 1927 logging.info("running shell command: " + line) 1928 subprocess.call(line, shell=True)
1929
1930 - def complete_shell(self, text, line, begidx, endidx):
1931 """ add path for shell """ 1932 1933 # Filename if directory is given 1934 # 1935 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 1936 if not text: 1937 text = '' 1938 output = self.path_completion(text, 1939 base_dir=\ 1940 self.split_arg(line[0:begidx])[-1]) 1941 else: 1942 output = self.path_completion(text) 1943 return output
1944
1945 - def help_shell(self):
1946 """help for the shell""" 1947 logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE') 1948 logger.info("syntax: shell CMD (or ! CMD)",'$MG:color:BLACK')
1949
1950 1951 1952 1953 #=============================================================================== 1954 # Question with auto-completion 1955 #=============================================================================== 1956 -class SmartQuestion(BasicCmd):
1957 """ a class for answering a question with the path autocompletion""" 1958 1959 allowpath = False
1960 - def preloop(self):
1961 """Initializing before starting the main loop""" 1962 self.prompt = '>' 1963 self.value = None 1964 BasicCmd.preloop(self)
1965 1966 @property
1967 - def answer(self):
1968 return self.value
1969
1970 - def __init__(self, question, allow_arg=[], default=None, 1971 mother_interface=None, *arg, **opt):
1972 self.question = question 1973 self.wrong_answer = 0 # forbids infinite loop 1974 self.allow_arg = [str(a) for a in allow_arg] 1975 self.history_header = '' 1976 self.default_value = str(default) 1977 self.mother_interface = mother_interface 1978 1979 if 'case' in opt: 1980 self.casesensitive = opt['case'] 1981 del opt['case'] 1982 elif 'casesensitive' in opt: 1983 self.casesensitive = opt['casesensitive'] 1984 del opt['casesensitive'] 1985 else: 1986 self.casesensistive = True 1987 super(SmartQuestion, self).__init__(*arg, **opt)
1988
1989 - def __call__(self, question, reprint_opt=True, **opts):
1990 1991 self.question = question 1992 for key,value in opts: 1993 setattr(self, key, value) 1994 if reprint_opt: 1995 print question 1996 return self.cmdloop()
1997 1998
1999 - def completenames(self, text, line, *ignored):
2000 prev_timer = signal.alarm(0) # avoid timer if any 2001 if prev_timer: 2002 nb_back = len(line) 2003 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2004 self.stdout.write(line) 2005 self.stdout.flush() 2006 try: 2007 out = {} 2008 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2009 out[' Recognized command'] = BasicCmd.completenames(self, text) 2010 2011 return self.deal_multiple_categories(out) 2012 except Exception, error: 2013 print error
2014 2015 completedefault = completenames 2016
2017 - def get_names(self):
2018 # This method used to pull in base class attributes 2019 # at a time dir() didn't do it yet. 2020 return dir(self)
2021
2022 - def onecmd(self, line, **opt):
2023 """catch all error and stop properly command accordingly 2024 Interpret the argument as though it had been typed in response 2025 to the prompt. 2026 2027 The return value is a flag indicating whether interpretation of 2028 commands by the interpreter should stop. 2029 2030 This allow to pass extra argument for internal call. 2031 """ 2032 try: 2033 if '~/' in line and os.environ.has_key('HOME'): 2034 line = line.replace('~/', '%s/' % os.environ['HOME']) 2035 line = os.path.expandvars(line) 2036 cmd, arg, line = self.parseline(line) 2037 if not line: 2038 return self.emptyline() 2039 if cmd is None: 2040 return self.default(line) 2041 self.lastcmd = line 2042 if cmd == '': 2043 return self.default(line) 2044 else: 2045 try: 2046 func = getattr(self, 'do_' + cmd) 2047 except AttributeError: 2048 return self.default(line) 2049 return func(arg, **opt) 2050 except Exception as error: 2051 logger.warning(error) 2052 if __debug__: 2053 raise
2054
2055 - def reask(self, reprint_opt=True):
2056 pat = re.compile('\[(\d*)s to answer\]') 2057 prev_timer = signal.alarm(0) # avoid timer if any 2058 2059 if prev_timer: 2060 if pat.search(self.question): 2061 timeout = int(pat.search(self.question).groups()[0]) 2062 else: 2063 timeout=20 2064 print 2065 signal.alarm(timeout) 2066 if reprint_opt: 2067 if not prev_timer: 2068 self.question = pat.sub('',self.question) 2069 print self.question 2070 2071 if self.mother_interface: 2072 answer = self.mother_interface.check_answer_in_input_file(self, 'EOF', 2073 path=self.allowpath) 2074 if answer: 2075 stop = self.default(answer) 2076 self.postcmd(stop, answer) 2077 return False 2078 2079 return False
2080
2081 - def do_help(self, line):
2082 2083 text=line 2084 out ={} 2085 out['Options'] = Cmd.list_completion(text, self.allow_arg) 2086 out['command'] = BasicCmd.completenames(self, text) 2087 2088 if not text: 2089 if out['Options']: 2090 logger.info( "Here is the list of all valid options:", '$MG:color:BLACK') 2091 logger.info( " "+ "\n ".join(out['Options'])) 2092 if out['command']: 2093 logger.info( "Here is the list of command available:", '$MG:color:BLACK') 2094 logger.info( " "+ "\n ".join(out['command'])) 2095 else: 2096 if out['Options']: 2097 logger.info( "Here is the list of all valid options starting with \'%s\'" % text, '$MG:color:BLACK') 2098 logger.info( " "+ "\n ".join(out['Options'])) 2099 if out['command']: 2100 logger.info( "Here is the list of command available starting with \'%s\':" % text, '$MG:color:BLACK') 2101 logger.info( " "+ "\n ".join(out['command'])) 2102 elif not out['Options']: 2103 logger.info( "No possibility starting with \'%s\'" % text, '$MG:color:BLACK') 2104 logger.info( "You can type help XXX, to see all command starting with XXX", '$MG:color:BLACK')
2105 - def complete_help(self, text, line, begidx, endidx):
2106 """ """ 2107 return self.completenames(text, line)
2108
2109 - def default(self, line):
2110 """Default action if line is not recognized""" 2111 2112 if line.strip() == '' and self.default_value is not None: 2113 self.value = self.default_value 2114 else: 2115 self.value = line
2116
2117 - def emptyline(self):
2118 """If empty line, return default""" 2119 2120 if self.default_value is not None: 2121 self.value = self.default_value
2122 2123
2124 - def postcmd(self, stop, line):
2125 2126 try: 2127 if self.value in self.allow_arg: 2128 return True 2129 elif str(self.value) == 'EOF': 2130 self.value = self.default_value 2131 return True 2132 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2133 return self.reask() 2134 elif self.value == 'repeat': 2135 return self.reask() 2136 elif len(self.allow_arg)==0: 2137 return True 2138 elif not self.casesensitive: 2139 for ans in self.allow_arg: 2140 if ans.lower() == self.value.lower(): 2141 self.value = ans 2142 return True 2143 break 2144 else: 2145 raise Exception 2146 else: 2147 raise Exception 2148 except Exception,error: 2149 if self.wrong_answer < 100: 2150 self.wrong_answer += 1 2151 logger.warning("""%s not valid argument. Valid argument are in (%s).""" \ 2152 % (self.value,','.join(self.allow_arg))) 2153 logger.warning('please retry') 2154 return False 2155 else: 2156 self.value = self.default_value 2157 return True
2158
2159 - def cmdloop(self, intro=None):
2160 super(SmartQuestion,self).cmdloop(intro) 2161 return self.answer
2162
2163 # a function helper 2164 -def smart_input(input_text, allow_arg=[], default=None):
2165 print input_text 2166 obj = SmartQuestion(allow_arg=allow_arg, default=default) 2167 return obj.cmdloop()
2168
2169 #=============================================================================== 2170 # Question in order to return a path with auto-completion 2171 #=============================================================================== 2172 -class OneLinePathCompletion(SmartQuestion):
2173 """ a class for answering a question with the path autocompletion""" 2174 2175 completion_prefix='' 2176 allowpath=True 2177
2178 - def completenames(self, text, line, begidx, endidx, formatting=True):
2179 prev_timer = signal.alarm(0) # avoid timer if any 2180 if prev_timer: 2181 nb_back = len(line) 2182 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2183 self.stdout.write(line) 2184 self.stdout.flush() 2185 2186 try: 2187 out = {} 2188 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2189 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 2190 out[' Recognized command'] = BasicCmd.completenames(self, text) 2191 2192 return self.deal_multiple_categories(out, formatting) 2193 except Exception, error: 2194 print error
2195
2196 - def precmd(self, *args):
2197 """ """ 2198 2199 signal.alarm(0) 2200 return SmartQuestion.precmd(self, *args)
2201
2202 - def completedefault(self,text, line, begidx, endidx):
2203 prev_timer = signal.alarm(0) # avoid timer if any 2204 if prev_timer: 2205 nb_back = len(line) 2206 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2207 self.stdout.write(line) 2208 self.stdout.flush() 2209 try: 2210 args = Cmd.split_arg(line[0:begidx]) 2211 except Exception, error: 2212 print error 2213 2214 # Directory continuation 2215 if args[-1].endswith(os.path.sep): 2216 2217 return Cmd.path_completion(text, 2218 os.path.join('.',*[a for a in args \ 2219 if a.endswith(os.path.sep)])) 2220 self.completenames(line+text)
2221 2222
2223 - def postcmd(self, stop, line):
2224 try: 2225 if self.value in self.allow_arg: 2226 return True 2227 elif self.value and os.path.isfile(self.value): 2228 return os.path.relpath(self.value) 2229 elif self.value and str(self.value) == 'EOF': 2230 self.value = self.default_value 2231 return True 2232 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2233 # go to retry 2234 reprint_opt = True 2235 elif self.value == 'repeat': 2236 reprint_opt = True 2237 else: 2238 raise Exception 2239 except Exception, error: 2240 print """not valid argument. Valid argument are file path or value in (%s).""" \ 2241 % ','.join(self.allow_arg) 2242 print 'please retry' 2243 reprint_opt = False 2244 2245 if line != 'EOF': 2246 return self.reask(reprint_opt)
2247
2248 2249 # a function helper 2250 -def raw_path_input(input_text, allow_arg=[], default=None):
2251 print input_text 2252 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 2253 return obj.cmdloop()
2254
2255 #=============================================================================== 2256 # 2257 #=============================================================================== 2258 -class CmdFile(file):
2259 """ a class for command input file -in order to debug cmd \n problem""" 2260
2261 - def __init__(self, name, opt='rU'):
2262 2263 file.__init__(self, name, opt) 2264 self.text = file.read(self) 2265 self.close() 2266 self.lines = self.text.split('\n')
2267
2268 - def readline(self, *arg, **opt):
2269 """readline method treating correctly a line whithout \n at the end 2270 (add it) 2271 """ 2272 if self.lines: 2273 line = self.lines.pop(0) 2274 else: 2275 return '' 2276 2277 if line.endswith('\n'): 2278 return line 2279 else: 2280 return line + '\n'
2281
2282 - def __next__(self):
2283 return self.lines.__next__()
2284
2285 - def __iter__(self):
2286 return self.lines.__iter__()
2287