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 and not 'libedit' in readline.__doc__: 448 readline.set_completion_display_matches_hook(self.print_suggestions) 449 else: 450 readline.set_completion_display_matches_hook()
451
452 - def preloop(self):
455
456 - def deal_multiple_categories(self, dico, formatting=True, forceCategory=False):
457 """convert the multiple category in a formatted list understand by our 458 specific readline parser""" 459 460 if not formatting: 461 return dico 462 463 if 'libedit' in readline.__doc__: 464 # No parser in this case, just send all the valid options 465 out = [] 466 for name, opt in dico.items(): 467 out += opt 468 return out 469 470 # check if more than one categories but only one value: 471 if not forceCategory and all(len(s) <= 1 for s in dico.values() ): 472 values = set((s[0] for s in dico.values() if len(s)==1)) 473 if len(values) == 1: 474 return values 475 476 # That's the real work 477 out = [] 478 valid=0 479 # if the key starts with number order the key with that number. 480 for name, opt in dico.items(): 481 if not opt: 482 continue 483 name = name.replace(' ', '_') 484 valid += 1 485 out.append(opt[0].rstrip()+'@@'+name+'@@') 486 # Remove duplicate 487 d = {} 488 for x in opt: 489 d[x] = 1 490 opt = list(d.keys()) 491 opt.sort() 492 out += opt 493 494 if not forceCategory and valid == 1: 495 out = out[1:] 496 497 return out
498 499 @debug()
500 - def print_suggestions(self, substitution, matches, longest_match_length) :
501 """print auto-completions by category""" 502 if not hasattr(self, 'completion_prefix'): 503 self.completion_prefix = '' 504 longest_match_length += len(self.completion_prefix) 505 try: 506 if len(matches) == 1: 507 self.stdout.write(matches[0]+' ') 508 return 509 self.stdout.write('\n') 510 l2 = [a[-2:] for a in matches] 511 if '@@' in l2: 512 nb_column = self.getTerminalSize()//(longest_match_length+1) 513 pos=0 514 for val in self.completion_matches: 515 if val.endswith('@@'): 516 category = val.rsplit('@@',2)[1] 517 category = category.replace('_',' ') 518 self.stdout.write('\n %s:\n%s\n' % (category, '=' * (len(category)+2))) 519 start = 0 520 pos = 0 521 continue 522 elif pos and pos % nb_column ==0: 523 self.stdout.write('\n') 524 self.stdout.write(self.completion_prefix + val + \ 525 ' ' * (longest_match_length +1 -len(val))) 526 pos +=1 527 self.stdout.write('\n') 528 else: 529 # nb column 530 nb_column = self.getTerminalSize()//(longest_match_length+1) 531 for i,val in enumerate(matches): 532 if i and i%nb_column ==0: 533 self.stdout.write('\n') 534 self.stdout.write(self.completion_prefix + val + \ 535 ' ' * (longest_match_length +1 -len(val))) 536 self.stdout.write('\n') 537 538 self.stdout.write(self.prompt+readline.get_line_buffer()) 539 self.stdout.flush() 540 except Exception, error: 541 if __debug__: 542 logger.error(error)
543
544 - def getTerminalSize(self):
545 def ioctl_GWINSZ(fd): 546 try: 547 import fcntl, termios, struct 548 cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 549 '1234')) 550 except Exception: 551 return None 552 return cr
553 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 554 if not cr: 555 try: 556 fd = os.open(os.ctermid(), os.O_RDONLY) 557 cr = ioctl_GWINSZ(fd) 558 os.close(fd) 559 except Exception: 560 pass 561 if not cr: 562 try: 563 cr = (os.environ['LINES'], os.environ['COLUMNS']) 564 except Exception: 565 cr = (25, 80) 566 return int(cr[1])
567
568 - def complete(self, text, state):
569 """Return the next possible completion for 'text'. 570 If a command has not been entered, then complete against command list. 571 Otherwise try to call complete_<command> to get list of completions. 572 """ 573 if state == 0: 574 import readline 575 origline = readline.get_line_buffer() 576 line = origline.lstrip() 577 stripped = len(origline) - len(line) 578 begidx = readline.get_begidx() - stripped 579 endidx = readline.get_endidx() - stripped 580 581 if ';' in line: 582 begin, line = line.rsplit(';',1) 583 begidx = begidx - len(begin) - 1 584 endidx = endidx - len(begin) - 1 585 if line[:begidx] == ' ' * begidx: 586 begidx=0 587 588 if begidx>0: 589 cmd, args, foo = self.parseline(line) 590 if cmd == '': 591 compfunc = self.completedefault 592 else: 593 try: 594 compfunc = getattr(self, 'complete_' + cmd) 595 except AttributeError, error: 596 compfunc = self.completedefault 597 except Exception, error: 598 misc.sprint(error) 599 else: 600 compfunc = self.completenames 601 602 # correct wrong splittion with '\ ' 603 if line and begidx > 2 and line[begidx-2:begidx] == '\ ': 604 Ntext = line.split(os.path.sep)[-1] 605 self.completion_prefix = Ntext.rsplit('\ ', 1)[0] + '\ ' 606 to_rm = len(self.completion_prefix) - 1 607 Nbegidx = len(line.rsplit(os.path.sep, 1)[0]) + 1 608 data = compfunc(Ntext.replace('\ ', ' '), line, Nbegidx, endidx) 609 self.completion_matches = [p[to_rm:] for p in data 610 if len(p)>to_rm] 611 # correct wrong splitting with '-'/"=" 612 elif line and line[begidx-1] in ['-',"=",':']: 613 try: 614 sep = line[begidx-1] 615 Ntext = line.split()[-1] 616 self.completion_prefix = Ntext.rsplit(sep,1)[0] + sep 617 to_rm = len(self.completion_prefix) 618 Nbegidx = len(line.rsplit(None, 1)[0]) 619 data = compfunc(Ntext, line, Nbegidx, endidx) 620 self.completion_matches = [p[to_rm:] for p in data 621 if len(p)>to_rm] 622 except Exception, error: 623 print error 624 else: 625 self.completion_prefix = '' 626 self.completion_matches = compfunc(text, line, begidx, endidx) 627 628 self.completion_matches = [ l if l[-1] in [' ','@','=',os.path.sep] 629 else ((l + ' ') if not l.endswith('\\$') else l[:-2]) 630 for l in self.completion_matches if l] 631 632 try: 633 return self.completion_matches[state] 634 except IndexError, error: 635 # if __debug__: 636 # logger.error('\n Completion ERROR:') 637 # logger.error( error) 638 return None
639 640 @staticmethod
641 - def split_arg(line):
642 """Split a line of arguments""" 643 644 split = line.split() 645 out=[] 646 tmp='' 647 for data in split: 648 if data[-1] == '\\': 649 tmp += data[:-1]+' ' 650 elif tmp: 651 tmp += data 652 tmp = os.path.expanduser(os.path.expandvars(tmp)) 653 out.append(tmp) 654 # Reinitialize tmp in case there is another differen argument 655 # containing escape characters 656 tmp = '' 657 else: 658 out.append(data) 659 return out
660 661 @staticmethod
662 - def list_completion(text, list, line=''):
663 """Propose completions of text in list""" 664 665 if not text: 666 completions = list 667 else: 668 completions = [ f 669 for f in list 670 if f.startswith(text) 671 ] 672 673 return completions
674 675 676 @staticmethod
677 - def path_completion(text, base_dir = None, only_dirs = False, 678 relative=True):
679 """Propose completions of text to compose a valid path""" 680 681 if base_dir is None: 682 base_dir = os.getcwd() 683 base_dir = os.path.expanduser(os.path.expandvars(base_dir)) 684 685 if text == '~': 686 text = '~/' 687 prefix, text = os.path.split(text) 688 prefix = os.path.expanduser(os.path.expandvars(prefix)) 689 base_dir = os.path.join(base_dir, prefix) 690 if prefix: 691 prefix += os.path.sep 692 693 if only_dirs: 694 completion = [prefix + f + os.path.sep 695 for f in os.listdir(base_dir) 696 if f.startswith(text) and \ 697 os.path.isdir(os.path.join(base_dir, f)) and \ 698 (not f.startswith('.') or text.startswith('.')) 699 ] 700 else: 701 completion = [ prefix + f 702 for f in os.listdir(base_dir) 703 if f.startswith(text) and \ 704 os.path.isfile(os.path.join(base_dir, f)) and \ 705 (not f.startswith('.') or text.startswith('.')) 706 ] 707 708 completion = completion + \ 709 [prefix + f + os.path.sep 710 for f in os.listdir(base_dir) 711 if f.startswith(text) and \ 712 os.path.isdir(os.path.join(base_dir, f)) and \ 713 (not f.startswith('.') or text.startswith('.')) 714 ] 715 716 if relative: 717 completion += [prefix + f for f in ['.'+os.path.sep, '..'+os.path.sep] if \ 718 f.startswith(text) and not prefix.startswith('.')] 719 720 completion = [a.replace(' ','\ ') for a in completion] 721 return completion
722
723 724 725 726 -class CheckCmd(object):
727 """Extension of the cmd object for only the check command""" 728
729 - def check_history(self, args):
730 """check the validity of line""" 731 732 if len(args) > 1: 733 self.help_history() 734 raise self.InvalidCmd('\"history\" command takes at most one argument') 735 736 if not len(args): 737 return 738 739 if args[0] =='.': 740 if not self._export_dir: 741 raise self.InvalidCmd("No default directory is defined for \'.\' option") 742 elif args[0] != 'clean': 743 dirpath = os.path.dirname(args[0]) 744 if dirpath and not os.path.exists(dirpath) or \ 745 os.path.isdir(args[0]): 746 raise self.InvalidCmd("invalid path %s " % dirpath)
747
748 - def check_save(self, args):
749 """check that the line is compatible with save options""" 750 751 if len(args) > 2: 752 self.help_save() 753 raise self.InvalidCmd, 'too many arguments for save command.' 754 755 if len(args) == 2: 756 if args[0] != 'options': 757 self.help_save() 758 raise self.InvalidCmd, '\'%s\' is not recognized as first argument.' % \ 759 args[0] 760 else: 761 args.pop(0)
762
763 -class HelpCmd(object):
764 """Extension of the cmd object for only the help command""" 765
766 - def help_quit(self):
767 logger.info("-- terminates the application",'$MG:color:BLUE') 768 logger.info("syntax: quit",'$MG:color:BLACK')
769 770 help_EOF = help_quit 771
772 - def help_history(self):
773 logger.info("-- interact with the command history.",'$MG:color:BLUE') 774 logger.info("syntax: history [FILEPATH|clean|.] ",'$MG:color:BLACK') 775 logger.info(" > If FILEPATH is \'.\' and \'output\' is done,") 776 logger.info(" Cards/proc_card_mg5.dat will be used.") 777 logger.info(" > If FILEPATH is omitted, the history will be output to stdout.") 778 logger.info(" \"clean\" will remove all entries from the history.")
779
780 - def help_help(self):
781 logger.info("-- access to the in-line help",'$MG:color:BLUE') 782 logger.info("syntax: help",'$MG:color:BLACK')
783
784 - def help_save(self):
785 """help text for save""" 786 logger.info("-- save options configuration to filepath.",'$MG:color:BLUE') 787 logger.info("syntax: save [options] [FILEPATH]",'$MG:color:BLACK')
788
789 - def help_display(self):
790 """help for display command""" 791 logger.info("-- display a the status of various internal state variables",'$MG:color:BLUE') 792 logger.info("syntax: display " + "|".join(self._display_opts),'$MG:color:BLACK')
793
794 -class CompleteCmd(object):
795 """Extension of the cmd object for only the complete command""" 796
797 - def complete_display(self,text, line, begidx, endidx):
798 args = self.split_arg(line[0:begidx]) 799 # Format 800 if len(args) == 1: 801 return self.list_completion(text, self._display_opts)
802
803 - def complete_history(self, text, line, begidx, endidx):
804 "Complete the history command" 805 806 args = self.split_arg(line[0:begidx]) 807 808 # Directory continuation 809 if args[-1].endswith(os.path.sep): 810 return self.path_completion(text, 811 os.path.join('.',*[a for a in args \ 812 if a.endswith(os.path.sep)])) 813 814 if len(args) == 1: 815 return self.path_completion(text)
816
817 - def complete_save(self, text, line, begidx, endidx):
818 "Complete the save command" 819 820 args = self.split_arg(line[0:begidx]) 821 822 # Format 823 if len(args) == 1: 824 return self.list_completion(text, ['options']) 825 826 # Directory continuation 827 if args[-1].endswith(os.path.sep): 828 return self.path_completion(text, 829 pjoin('.',*[a for a in args if a.endswith(os.path.sep)]), 830 only_dirs = True) 831 832 # Filename if directory is not given 833 if len(args) == 2: 834 return self.path_completion(text)
835
836 -class Cmd(CheckCmd, HelpCmd, CompleteCmd, BasicCmd):
837 """Extension of the cmd.Cmd command line. 838 This extensions supports line breaking, history, comments, 839 internal call to cmdline, path completion,... 840 this class should be MG5 independent""" 841 842 #suggested list of command 843 next_possibility = {} # command : [list of suggested command] 844 history_header = "" 845 846 _display_opts = ['options','variable'] 847 allow_notification_center = True 848
849 - class InvalidCmd(Exception):
850 """expected error for wrong command""" 851 pass
852 853 ConfigurationError = InvalidCmd 854 855 debug_output = 'debug' 856 error_debug = """Please report this bug to developers\n 857 More information is found in '%(debug)s'.\n 858 Please attach this file to your report.""" 859 config_debug = error_debug 860 861 keyboard_stop_msg = """stopping all current operation 862 in order to quit the program please enter exit""" 863 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 pass 910 if readline and not 'libedit' in readline.__doc__: 911 readline.set_completion_display_matches_hook(self.print_suggestions)
912 913
914 - def cmdloop(self, intro=None):
915 916 self.preloop() 917 if intro is not None: 918 self.intro = intro 919 if self.intro: 920 print self.intro 921 stop = None 922 while not stop: 923 if self.cmdqueue: 924 line = self.cmdqueue[0] 925 del self.cmdqueue[0] 926 else: 927 if self.use_rawinput: 928 try: 929 line = raw_input(self.prompt) 930 except EOFError: 931 line = 'EOF' 932 else: 933 sys.stdout.write(self.prompt) 934 sys.stdout.flush() 935 line = sys.stdin.readline() 936 if not len(line): 937 line = 'EOF' 938 else: 939 line = line[:-1] # chop \n 940 try: 941 line = self.precmd(line) 942 stop = self.onecmd(line) 943 except BaseException, error: 944 self.error_handling(error, line) 945 if isinstance(error, KeyboardInterrupt): 946 stop = True 947 finally: 948 stop = self.postcmd(stop, line) 949 self.postloop()
950
951 - def no_notification(self):
952 """avoid to have html opening / notification""" 953 self.allow_notification_center = False 954 try: 955 self.options['automatic_html_opening'] = False 956 self.options['notification_center'] = False 957 958 except: 959 pass
960 961
962 - def precmd(self, line):
963 """ A suite of additional function needed for in the cmd 964 this implement history, line breaking, comment treatment,... 965 """ 966 967 if not line: 968 return line 969 970 # Check if we are continuing a line: 971 if self.save_line: 972 line = self.save_line + line 973 self.save_line = '' 974 975 line = line.lstrip() 976 # Check if the line is complete 977 if line.endswith('\\'): 978 self.save_line = line[:-1] 979 return '' # do nothing 980 981 # Remove comment 982 if '#' in line: 983 line = line.split('#')[0] 984 985 # Deal with line splitting 986 if ';' in line: 987 lines = line.split(';') 988 for subline in lines: 989 if not (subline.startswith("history") or subline.startswith('help') \ 990 or subline.startswith('#*')): 991 self.history.append(subline) 992 stop = self.onecmd_orig(subline) 993 stop = self.postcmd(stop, subline) 994 return '' 995 996 # execute the line command 997 self.history.append(line) 998 return line
999
1000 - def postcmd(self,stop, line):
1001 """ finishing a command 1002 This looks if the command add a special post part.""" 1003 1004 if line.strip(): 1005 try: 1006 cmd, subline = line.split(None, 1) 1007 except ValueError: 1008 pass 1009 else: 1010 if hasattr(self,'post_%s' %cmd): 1011 stop = getattr(self, 'post_%s' % cmd)(stop, subline) 1012 return stop
1013
1014 - def define_child_cmd_interface(self, obj_instance, interface=True):
1015 """Define a sub cmd_interface""" 1016 1017 # We are in a file reading mode. So we need to redirect the cmd 1018 self.child = obj_instance 1019 self.child.mother = self 1020 1021 #ensure that notification are sync: 1022 self.child.allow_notification_center = self.allow_notification_center 1023 1024 if self.use_rawinput and interface: 1025 # We are in interactive mode -> simply call the child 1026 obj_instance.cmdloop() 1027 stop = obj_instance.postloop() 1028 return stop 1029 if self.inputfile: 1030 # we are in non interactive mode -> so pass the line information 1031 obj_instance.inputfile = self.inputfile 1032 1033 obj_instance.haspiping = self.haspiping 1034 1035 if not interface: 1036 return self.child
1037 1038 #=============================================================================== 1039 # Ask a question with nice options handling 1040 #===============================================================================
1041 - def ask(self, question, default, choices=[], path_msg=None, 1042 timeout = True, fct_timeout=None, ask_class=None, alias={}, 1043 first_cmd=None, text_format='4', **opt):
1044 """ ask a question with some pre-define possibility 1045 path info is 1046 """ 1047 1048 if path_msg: 1049 path_msg = [path_msg] 1050 else: 1051 path_msg = [] 1052 1053 if timeout is True: 1054 try: 1055 timeout = self.options['timeout'] 1056 except Exception: 1057 pass 1058 1059 # add choice info to the question 1060 if choices + path_msg: 1061 question += ' [' 1062 question += "\033[%sm%s\033[0m, " % (text_format, default) 1063 for data in choices[:9] + path_msg: 1064 if default == data: 1065 continue 1066 else: 1067 question += "%s, " % data 1068 1069 if len(choices) > 9: 1070 question += '... , ' 1071 question = question[:-2]+']' 1072 else: 1073 question += "[\033[%sm%s\033[0m] " % (text_format, default) 1074 if ask_class: 1075 obj = ask_class 1076 elif path_msg: 1077 obj = OneLinePathCompletion 1078 else: 1079 obj = SmartQuestion 1080 1081 if alias: 1082 choices += alias.keys() 1083 1084 question_instance = obj(question, allow_arg=choices, default=default, 1085 mother_interface=self, **opt) 1086 1087 if first_cmd: 1088 if isinstance(first_cmd, str): 1089 question_instance.onecmd(first_cmd) 1090 else: 1091 for line in first_cmd: 1092 question_instance.onecmd(line) 1093 if not self.haspiping: 1094 if hasattr(obj, "haspiping"): 1095 obj.haspiping = self.haspiping 1096 1097 1098 1099 1100 answer = self.check_answer_in_input_file(question_instance, default, path_msg) 1101 if answer is not None: 1102 if answer in alias: 1103 answer = alias[answer] 1104 if ask_class: 1105 line=answer 1106 answer = question_instance.default(line) 1107 question_instance.postcmd(answer, line) 1108 return question_instance.answer 1109 if hasattr(question_instance, 'check_answer_consistency'): 1110 question_instance.check_answer_consistency() 1111 return answer 1112 1113 question = question_instance.question 1114 value = Cmd.timed_input(question, default, timeout=timeout, 1115 fct=question_instance, fct_timeout=fct_timeout) 1116 1117 try: 1118 if value in alias: 1119 value = alias[value] 1120 except TypeError: 1121 pass 1122 1123 if value == default and ask_class: 1124 value = question_instance.default(default) 1125 return value
1126
1127 - def do_import(self, line):
1128 """Advanced commands: Import command files""" 1129 1130 args = self.split_arg(line) 1131 # Check argument's validity 1132 self.check_import(args) 1133 1134 # Execute the card 1135 self.import_command_file(args[1])
1136
1137 - def check_import(self, args):
1138 """check import command""" 1139 1140 if '-f' in args: 1141 self.force = True 1142 args.remove('-f') 1143 if args[0] != 'command': 1144 args.set(0, 'command') 1145 if len(args) != 2: 1146 raise self.InvalidCmd('import command requires one filepath argument') 1147 if not os.path.exists(args[1]): 1148 raise 'No such file or directory %s' % args[1]
1149 1150
1151 - def check_answer_in_input_file(self, question_instance, default, path=False):
1152 """Questions can have answer in output file (or not)""" 1153 1154 if not self.inputfile: 1155 return None# interactive mode 1156 1157 line = self.get_stored_line() 1158 # line define if a previous answer was not answer correctly 1159 if not line: 1160 try: 1161 line = self.inputfile.next() 1162 except StopIteration: 1163 if self.haspiping: 1164 logger.debug('piping') 1165 self.store_line(line) 1166 return None # print the question and use the pipe 1167 logger.info(question_instance.question) 1168 logger.info('The answer to the previous question is not set in your input file', '$MG:color:BLACK') 1169 logger.info('Use %s value' % default, '$MG:color:BLACK') 1170 return str(default) 1171 1172 line = line.replace('\n','').strip() 1173 if '#' in line: 1174 line = line.split('#')[0] 1175 if not line: 1176 # Comment or empty line, pass to the next one 1177 return self.check_answer_in_input_file(question_instance, default, path) 1178 options = question_instance.allow_arg 1179 if line in options or line.lower() in options: 1180 return line 1181 elif hasattr(question_instance, 'do_%s' % line.split()[0]): 1182 #This is a command line, exec it and check next line 1183 logger.info(line) 1184 fct = getattr(question_instance, 'do_%s' % line.split()[0]) 1185 fct(' '.join(line.split()[1:])) 1186 return self.check_answer_in_input_file(question_instance, default, path) 1187 elif path: 1188 line = os.path.expanduser(os.path.expandvars(line)) 1189 if os.path.isfile(line): 1190 return line 1191 elif hasattr(question_instance, 'casesensitive') and not question_instance.casesensitive: 1192 for entry in question_instance.allow_arg: 1193 if line.lower() == entry.lower(): 1194 return entry 1195 elif any(line.lower()==opt.lower() for opt in options): 1196 possibility = [opt for opt in options if line.lower()==opt.lower()] 1197 if len (possibility)==1: 1198 return possibility[0] 1199 1200 # No valid answer provides 1201 if self.haspiping: 1202 self.store_line(line) 1203 return None # print the question and use the pipe 1204 else: 1205 logger.info(question_instance.question) 1206 logger.warning('The answer to the previous question is not set in your input file') 1207 logger.warning('Use %s value' % default) 1208 self.store_line(line) 1209 return str(default)
1210
1211 - def store_line(self, line):
1212 """store a line of the input file which should be executed by the higher mother""" 1213 1214 if self.mother: 1215 self.mother.store_line(line) 1216 else: 1217 self.stored_line = line
1218
1219 - def get_stored_line(self):
1220 """return stored line and clean it""" 1221 if self.mother: 1222 value = self.mother.get_stored_line() 1223 self.mother.stored_line = None 1224 else: 1225 value = self.stored_line 1226 self.stored_line = None 1227 return value
1228 1229 1230
1231 - def nice_error_handling(self, error, line):
1232 """ """ 1233 # Make sure that we are at the initial position 1234 if self.child: 1235 return self.child.nice_error_handling(error, line) 1236 1237 os.chdir(self.__initpos) 1238 # Create the debug files 1239 self.log = False 1240 if os.path.exists(self.debug_output): 1241 os.remove(self.debug_output) 1242 try: 1243 super(Cmd,self).onecmd('history %s' % self.debug_output.replace(' ', '\ ')) 1244 except Exception, error: 1245 logger.error(error) 1246 1247 debug_file = open(self.debug_output, 'a') 1248 traceback.print_exc(file=debug_file) 1249 if hasattr(error, 'filename'): 1250 debug_file.write("Related File: %s\n" % error.filename) 1251 # Create a nice error output 1252 if self.history and line == self.history[-1]: 1253 error_text = 'Command \"%s\" interrupted with error:\n' % line 1254 elif self.history: 1255 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1256 error_text += '\"%s\" with error:\n' % self.history[-1] 1257 else: 1258 error_text = '' 1259 error_text += '%s : %s\n' % (error.__class__.__name__, 1260 str(error).replace('\n','\n\t')) 1261 error_text += self.error_debug % {'debug':self.debug_output} 1262 logger_stderr.critical(error_text) 1263 1264 1265 # Add options status to the debug file 1266 try: 1267 self.do_display('options', debug_file) 1268 except Exception, error: 1269 debug_file.write('Fail to write options with error %s' % error) 1270 1271 #add the cards: 1272 for card in ['proc_card_mg5.dat','param_card.dat', 'run_card.dat']: 1273 try: 1274 ff = open(pjoin(self.me_dir, 'Cards', card)) 1275 debug_file.write(ff.read()) 1276 ff.close() 1277 except Exception: 1278 pass 1279 1280 #stop the execution if on a non interactive mode 1281 if self.use_rawinput == False: 1282 return True 1283 return False
1284 1285 1286
1287 - def nice_user_error(self, error, line):
1288 if self.child: 1289 return self.child.nice_user_error(error, line) 1290 # Make sure that we are at the initial position 1291 os.chdir(self.__initpos) 1292 if not self.history or line == self.history[-1]: 1293 error_text = 'Command \"%s\" interrupted with error:\n' % line 1294 else: 1295 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1296 error_text += '\"%s\" with error:\n' % self.history[-1] 1297 error_text += '%s : %s' % (error.__class__.__name__, 1298 str(error).replace('\n','\n\t')) 1299 logger_stderr.error(error_text) 1300 #stop the execution if on a non interactive mode 1301 if self.use_rawinput == False: 1302 return True 1303 # Remove failed command from history 1304 self.history.pop() 1305 return False
1306
1307 - def nice_config_error(self, error, line):
1308 if self.child: 1309 return self.child.nice_user_error(error, line) 1310 # Make sure that we are at the initial position 1311 os.chdir(self.__initpos) 1312 if not self.history or line == self.history[-1]: 1313 error_text = 'Error detected in \"%s\"\n' % line 1314 else: 1315 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 1316 error_text += 'write debug file %s \n' % self.debug_output 1317 self.log = False 1318 super(Cmd,self).onecmd('history %s' % self.debug_output) 1319 debug_file = open(self.debug_output, 'a') 1320 traceback.print_exc(file=debug_file) 1321 error_text += self.config_debug % {'debug' :self.debug_output} 1322 error_text += '%s : %s' % (error.__class__.__name__, 1323 str(error).replace('\n','\n\t')) 1324 logger_stderr.error(error_text) 1325 1326 # Add options status to the debug file 1327 try: 1328 self.do_display('options', debug_file) 1329 except Exception, error: 1330 debug_file.write('Fail to write options with error %s' % error) 1331 #stop the execution if on a non interactive mode 1332 if self.use_rawinput == False: 1333 return True 1334 # Remove failed command from history 1335 if self.history: 1336 self.history.pop() 1337 return False
1338
1339 - def onecmd_orig(self, line, **opt):
1340 """Interpret the argument as though it had been typed in response 1341 to the prompt. 1342 1343 The return value is a flag indicating whether interpretation of 1344 commands by the interpreter should stop. 1345 1346 This allow to pass extra argument for internal call. 1347 """ 1348 if '~/' in line and os.environ.has_key('HOME'): 1349 line = line.replace('~/', '%s/' % os.environ['HOME']) 1350 if '#' in line: 1351 line = line.split('#')[0] 1352 1353 line = os.path.expandvars(line) 1354 cmd, arg, line = self.parseline(line) 1355 if not line: 1356 return self.emptyline() 1357 if cmd is None: 1358 return self.default(line) 1359 self.lastcmd = line 1360 if cmd == '': 1361 return self.default(line) 1362 else: 1363 try: 1364 func = getattr(self, 'do_' + cmd) 1365 except AttributeError: 1366 return self.default(line) 1367 return func(arg, **opt)
1368
1369 - def error_handling(self, error, line):
1370 1371 me_dir = '' 1372 if hasattr(self, 'me_dir'): 1373 me_dir = os.path.basename(me_dir) + ' ' 1374 1375 1376 try: 1377 raise 1378 except self.InvalidCmd as error: 1379 if __debug__: 1380 self.nice_error_handling(error, line) 1381 self.history.pop() 1382 else: 1383 self.nice_user_error(error, line) 1384 if self.allow_notification_center: 1385 misc.apple_notify('Run %sfailed' % me_dir, 1386 'Invalid Command: %s' % error.__class__.__name__) 1387 1388 except self.ConfigurationError as error: 1389 self.nice_config_error(error, line) 1390 if self.allow_notification_center: 1391 misc.apple_notify('Run %sfailed' % me_dir, 1392 'Configuration error') 1393 except Exception as error: 1394 self.nice_error_handling(error, line) 1395 if self.mother: 1396 self.do_quit('') 1397 if self.allow_notification_center: 1398 misc.apple_notify('Run %sfailed' % me_dir, 1399 'Exception: %s' % error.__class__.__name__) 1400 except KeyboardInterrupt as error: 1401 self.stop_on_keyboard_stop() 1402 if __debug__: 1403 self.nice_config_error(error, line) 1404 logger.error(self.keyboard_stop_msg)
1405 1406 1407
1408 - def onecmd(self, line, **opt):
1409 """catch all error and stop properly command accordingly""" 1410 1411 try: 1412 return self.onecmd_orig(line, **opt) 1413 except BaseException, error: 1414 self.error_handling(error, line)
1415 1416
1417 - def stop_on_keyboard_stop(self):
1418 """action to perform to close nicely on a keyboard interupt""" 1419 pass # dummy function
1420
1421 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 1422 precmd=False, postcmd=True, 1423 child=True, **opt):
1424 """for third party call, call the line with pre and postfix treatment 1425 without global error handling """ 1426 1427 1428 if printcmd and not line.startswith('#'): 1429 logger.info(line) 1430 if self.child and child: 1431 current_interface = self.child 1432 else: 1433 current_interface = self 1434 if precmd: 1435 line = current_interface.precmd(line) 1436 if errorhandling: 1437 stop = current_interface.onecmd(line, **opt) 1438 else: 1439 stop = Cmd.onecmd_orig(current_interface, line, **opt) 1440 if postcmd: 1441 stop = current_interface.postcmd(stop, line) 1442 return stop
1443
1444 - def run_cmd(self, line):
1445 """for third party call, call the line with pre and postfix treatment 1446 with global error handling""" 1447 1448 return self.exec_cmd(line, errorhandling=True, precmd=True)
1449
1450 - def emptyline(self):
1451 """If empty line, do nothing. Default is repeat previous command.""" 1452 pass
1453
1454 - def default(self, line, log=True):
1455 """Default action if line is not recognized""" 1456 1457 # Faulty command 1458 if log: 1459 logger.warning("Command \"%s\" not recognized, please try again" % \ 1460 line.split()[0]) 1461 if line.strip() in ['q', '.q', 'stop']: 1462 logger.info("If you want to quit mg5 please type \"exit\".") 1463 1464 if self.history and self.history[-1] == line: 1465 self.history.pop()
1466 1467 # Write the list of command line use in this session
1468 - def do_history(self, line):
1469 """write in a file the suite of command that was used""" 1470 1471 args = self.split_arg(line) 1472 # Check arguments validity 1473 self.check_history(args) 1474 1475 if len(args) == 0: 1476 logger.info('\n'.join(self.history)) 1477 return 1478 elif args[0] == 'clean': 1479 self.history = [] 1480 logger.info('History is cleaned') 1481 return 1482 elif args[0] == '.': 1483 output_file = os.path.join(self._export_dir, 'Cards', \ 1484 'proc_card_mg5.dat') 1485 output_file = open(output_file, 'w') 1486 else: 1487 output_file = open(args[0], 'w') 1488 1489 # Create the command file 1490 text = self.get_history_header() 1491 text += ('\n'.join(self.history) + '\n') 1492 1493 #write this information in a file 1494 output_file.write(text) 1495 output_file.close() 1496 1497 if self.log: 1498 logger.info("History written to " + output_file.name)
1499
1500 - def compile(self, *args, **opts):
1501 """ """ 1502 1503 return misc.compile(nb_core=self.options['nb_core'], *args, **opts)
1504
1505 - def avoid_history_duplicate(self, line, no_break=[]):
1506 """remove all line in history (but the last) starting with line. 1507 up to the point when a line didn't start by something in no_break. 1508 (reading in reverse order)""" 1509 1510 new_history = [] 1511 for i in range(1, len(self.history)+1): 1512 cur_line = self.history[-i] 1513 if i == 1: 1514 new_history.append(cur_line) 1515 elif not any((cur_line.startswith(text) for text in no_break)): 1516 to_add = self.history[:-i+1] 1517 to_add.reverse() 1518 new_history += to_add 1519 break 1520 elif cur_line.startswith(line): 1521 continue 1522 else: 1523 new_history.append(cur_line) 1524 1525 new_history.reverse() 1526 self.history[:] = new_history
1527 1528
1529 - def import_command_file(self, filepath):
1530 # remove this call from history 1531 if self.history: 1532 self.history.pop() 1533 1534 #avoid that command of other file interfere with this one. 1535 previous_store_line = self.get_stored_line() 1536 1537 # Read the lines of the file and execute them 1538 if isinstance(filepath, str): 1539 commandline = open(filepath).readlines() 1540 else: 1541 commandline = filepath 1542 oldinputfile = self.inputfile 1543 oldraw = self.use_rawinput 1544 self.inputfile = (l for l in commandline) # make a generator 1545 self.use_rawinput = False 1546 # Note using "for line in open(filepath)" is not safe since the file 1547 # filepath can be overwritten during the run (leading to weird results) 1548 # Note also that we need a generator and not a list. 1549 for line in self.inputfile: 1550 #remove pointless spaces and \n 1551 line = line.replace('\n', '').strip() 1552 # execute the line 1553 if line: 1554 self.exec_cmd(line, precmd=True) 1555 stored = self.get_stored_line() 1556 while stored: 1557 line = stored 1558 self.exec_cmd(line, precmd=True) 1559 stored = self.get_stored_line() 1560 1561 # If a child was open close it 1562 if self.child: 1563 self.child.exec_cmd('quit') 1564 self.inputfile = oldinputfile 1565 self.use_rawinput = oldraw 1566 1567 # restore original store line 1568 cmd = self 1569 while hasattr(cmd, 'mother') and cmd.mother: 1570 cmd = cmd.mother 1571 cmd.stored_line = previous_store_line 1572 return
1573
1574 - def get_history_header(self):
1575 """Default history header""" 1576 1577 return self.history_header
1578
1579 - def postloop(self):
1580 """ """ 1581 1582 if self.use_rawinput and self.completekey: 1583 try: 1584 import readline 1585 readline.set_completer(self.old_completer) 1586 del self.old_completer 1587 except ImportError: 1588 pass 1589 except AttributeError: 1590 pass 1591 1592 args = self.split_arg(self.lastcmd) 1593 if args and args[0] in ['quit','exit']: 1594 if 'all' in args: 1595 return True 1596 if len(args) >1 and args[1].isdigit(): 1597 if args[1] not in ['0', '1']: 1598 return True 1599 1600 return False
1601 1602 #=============================================================================== 1603 # Ask a question with a maximum amount of time to answer 1604 #=============================================================================== 1605 @staticmethod
1606 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 1607 fct_timeout=None):
1608 """ a question with a maximal time to answer take default otherwise""" 1609 1610 def handle_alarm(signum, frame): 1611 raise TimeOutError
1612 1613 signal.signal(signal.SIGALRM, handle_alarm) 1614 1615 if fct is None: 1616 fct = raw_input 1617 1618 if timeout: 1619 signal.alarm(timeout) 1620 question += '[%ss to answer] ' % (timeout) 1621 try: 1622 result = fct(question) 1623 except TimeOutError: 1624 if noerror: 1625 logger.info('\nuse %s' % default) 1626 if fct_timeout: 1627 fct_timeout(True) 1628 return default 1629 else: 1630 signal.alarm(0) 1631 raise 1632 finally: 1633 signal.alarm(0) 1634 if fct_timeout: 1635 fct_timeout(False) 1636 return result
1637 1638 1639 1640 1641 1642 1643 # Quit
1644 - def do_quit(self, line):
1645 """Not in help: exit the mainloop() """ 1646 1647 if self.child: 1648 self.child.exec_cmd('quit ' + line, printcmd=False) 1649 return 1650 elif self.mother: 1651 self.mother.child = None 1652 if line == 'all': 1653 pass 1654 elif line: 1655 level = int(line) - 1 1656 if level: 1657 self.mother.lastcmd = 'quit %s' % level 1658 logger.info(' ') 1659 return True
1660 1661 # Aliases 1662 do_EOF = do_quit 1663 do_exit = do_quit 1664
1665 - def do_help(self, line):
1666 """Not in help: propose some usefull possible action """ 1667 1668 # if they are an argument use the default help 1669 if line: 1670 return super(Cmd, self).do_help(line) 1671 1672 1673 names = self.get_names() 1674 cmds = {} 1675 names.sort() 1676 # There can be duplicates if routines overridden 1677 prevname = '' 1678 for name in names: 1679 if name[:3] == 'do_': 1680 if name == prevname: 1681 continue 1682 prevname = name 1683 cmdname=name[3:] 1684 try: 1685 doc = getattr(self.cmd, name).__doc__ 1686 except Exception: 1687 doc = None 1688 if not doc: 1689 doc = getattr(self, name).__doc__ 1690 if not doc: 1691 tag = "Documented commands" 1692 elif ':' in doc: 1693 tag = doc.split(':',1)[0] 1694 else: 1695 tag = "Documented commands" 1696 if tag in cmds: 1697 cmds[tag].append(cmdname) 1698 else: 1699 cmds[tag] = [cmdname] 1700 1701 self.stdout.write("%s\n"%str(self.doc_leader)) 1702 for tag in self.helporder: 1703 if tag not in cmds: 1704 continue 1705 header = "%s (type help <topic>):" % tag 1706 self.print_topics(header, cmds[tag], 15,80) 1707 for name, item in cmds.items(): 1708 if name in self.helporder: 1709 continue 1710 if name == "Not in help": 1711 continue 1712 header = "%s (type help <topic>):" % name 1713 self.print_topics(header, item, 15,80) 1714 1715 1716 ## Add contextual help 1717 if len(self.history) == 0: 1718 last_action_2 = last_action = 'start' 1719 else: 1720 last_action_2 = last_action = 'none' 1721 1722 pos = 0 1723 authorize = self.next_possibility.keys() 1724 while last_action_2 not in authorize and last_action not in authorize: 1725 pos += 1 1726 if pos > len(self.history): 1727 last_action_2 = last_action = 'start' 1728 break 1729 1730 args = self.history[-1 * pos].split() 1731 last_action = args[0] 1732 if len(args)>1: 1733 last_action_2 = '%s %s' % (last_action, args[1]) 1734 else: 1735 last_action_2 = 'none' 1736 1737 logger.info('Contextual Help') 1738 logger.info('===============') 1739 if last_action_2 in authorize: 1740 options = self.next_possibility[last_action_2] 1741 elif last_action in authorize: 1742 options = self.next_possibility[last_action] 1743 else: 1744 return 1745 text = 'The following command(s) may be useful in order to continue.\n' 1746 for option in options: 1747 text+='\t %s \n' % option 1748 logger.info(text)
1749
1750 - def do_display(self, line, output=sys.stdout):
1751 """Advanced commands: basic display""" 1752 1753 args = self.split_arg(line) 1754 #check the validity of the arguments 1755 1756 if len(args) == 0: 1757 self.help_display() 1758 raise self.InvalidCmd, 'display require at least one argument' 1759 1760 if args[0] == "options": 1761 outstr = "Value of current Options:\n" 1762 for key, value in self.options.items(): 1763 outstr += '%25s \t:\t%s\n' %(key,value) 1764 output.write(outstr) 1765 1766 elif args[0] == "variable": 1767 outstr = "Value of Internal Variable:\n" 1768 try: 1769 var = eval(args[1]) 1770 except Exception: 1771 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1772 else: 1773 outstr += 'GLOBAL:\n' 1774 outstr += misc.nice_representation(var, nb_space=4) 1775 1776 try: 1777 var = eval('self.%s' % args[1]) 1778 except Exception: 1779 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1780 else: 1781 outstr += 'LOCAL:\n' 1782 outstr += misc.nice_representation(var, nb_space=4) 1783 split = args[1].split('.') 1784 for i, name in enumerate(split): 1785 try: 1786 __import__('.'.join(split[:i+1])) 1787 exec('%s=sys.modules[\'%s\']' % (split[i], '.'.join(split[:i+1]))) 1788 except ImportError: 1789 try: 1790 var = eval(args[1]) 1791 except Exception, error: 1792 outstr += 'EXTERNAL:\nVariable %s is not a external variable\n' % args[1] 1793 break 1794 else: 1795 outstr += 'EXTERNAL:\n' 1796 outstr += misc.nice_representation(var, nb_space=4) 1797 else: 1798 var = eval(args[1]) 1799 outstr += 'EXTERNAL:\n' 1800 outstr += misc.nice_representation(var, nb_space=4) 1801 1802 pydoc.pager(outstr)
1803 1804
1805 - def do_save(self, line, check=True):
1806 """Save the configuration file""" 1807 1808 args = self.split_arg(line) 1809 # Check argument validity 1810 if check: 1811 Cmd.check_save(self, args) 1812 1813 # find base file for the configuration 1814 if 'HOME' in os.environ and os.environ['HOME'] and \ 1815 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1816 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1817 if hasattr(self, 'me_dir'): 1818 basedir = self.me_dir 1819 elif not MADEVENT: 1820 basedir = MG5DIR 1821 else: 1822 basedir = os.getcwd() 1823 elif MADEVENT: 1824 # launch via ./bin/madevent 1825 for config_file in ['me5_configuration.txt', 'amcatnlo_configuration.txt']: 1826 if os.path.exists(pjoin(self.me_dir, 'Cards', config_file)): 1827 base = pjoin(self.me_dir, 'Cards', config_file) 1828 basedir = self.me_dir 1829 else: 1830 if hasattr(self, 'me_dir'): 1831 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1832 if len(args) == 0 and os.path.exists(base): 1833 self.write_configuration(base, base, self.me_dir) 1834 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1835 basedir = MG5DIR 1836 1837 if len(args) == 0: 1838 args.append(base) 1839 self.write_configuration(args[0], base, basedir, self.options)
1840
1841 - def write_configuration(self, filepath, basefile, basedir, to_keep):
1842 """Write the configuration file""" 1843 # We use the default configuration file as a template. 1844 # to ensure that all configuration information are written we 1845 # keep track of all key that we need to write. 1846 1847 logger.info('save configuration file to %s' % filepath) 1848 to_write = to_keep.keys() 1849 text = "" 1850 has_mg5_path = False 1851 # Use local configuration => Need to update the path 1852 for line in file(basefile): 1853 if '=' in line: 1854 data, value = line.split('=',1) 1855 else: 1856 text += line 1857 continue 1858 data = data.strip() 1859 if data.startswith('#'): 1860 key = data[1:].strip() 1861 else: 1862 key = data 1863 if '#' in value: 1864 value, comment = value.split('#',1) 1865 else: 1866 comment = '' 1867 if key in to_keep: 1868 value = str(to_keep[key]) 1869 else: 1870 text += line 1871 continue 1872 if key == 'mg5_path': 1873 has_mg5_path = True 1874 try: 1875 to_write.remove(key) 1876 except Exception: 1877 pass 1878 if '_path' in key: 1879 # special case need to update path 1880 # check if absolute path 1881 if not os.path.isabs(value): 1882 value = os.path.realpath(os.path.join(basedir, value)) 1883 text += '%s = %s # %s \n' % (key, value, comment) 1884 1885 for key in to_write: 1886 if key in to_keep: 1887 text += '%s = %s \n' % (key, to_keep[key]) 1888 1889 if not MADEVENT and not has_mg5_path: 1890 text += """\n# MG5 MAIN DIRECTORY\n""" 1891 text += "mg5_path = %s\n" % MG5DIR 1892 1893 writer = open(filepath,'w') 1894 writer.write(text) 1895 writer.close()
1896
1897 1898 1899 1900 -class CmdShell(Cmd):
1901 """CMD command with shell activate""" 1902 1903 # Access to shell
1904 - def do_shell(self, line):
1905 "Run a shell command" 1906 1907 if line.strip() is '': 1908 self.help_shell() 1909 else: 1910 logging.info("running shell command: " + line) 1911 subprocess.call(line, shell=True)
1912
1913 - def complete_shell(self, text, line, begidx, endidx):
1914 """ add path for shell """ 1915 1916 # Filename if directory is given 1917 # 1918 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 1919 if not text: 1920 text = '' 1921 output = self.path_completion(text, 1922 base_dir=\ 1923 self.split_arg(line[0:begidx])[-1]) 1924 else: 1925 output = self.path_completion(text) 1926 return output
1927
1928 - def help_shell(self):
1929 """help for the shell""" 1930 logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE') 1931 logger.info("syntax: shell CMD (or ! CMD)",'$MG:color:BLACK')
1932
1933 1934 1935 1936 #=============================================================================== 1937 # Question with auto-completion 1938 #=============================================================================== 1939 -class SmartQuestion(BasicCmd):
1940 """ a class for answering a question with the path autocompletion""" 1941 1942 allowpath = False
1943 - def preloop(self):
1944 """Initializing before starting the main loop""" 1945 self.prompt = '>' 1946 self.value = None 1947 BasicCmd.preloop(self)
1948 1949 @property
1950 - def answer(self):
1951 return self.value
1952
1953 - def __init__(self, question, allow_arg=[], default=None, 1954 mother_interface=None, *arg, **opt):
1955 self.question = question 1956 self.wrong_answer = 0 # forbids infinite loop 1957 self.allow_arg = [str(a) for a in allow_arg] 1958 self.history_header = '' 1959 self.default_value = str(default) 1960 self.mother_interface = mother_interface 1961 1962 if 'case' in opt: 1963 self.casesensitive = opt['case'] 1964 del opt['case'] 1965 elif 'casesensitive' in opt: 1966 self.casesensitive = opt['casesensitive'] 1967 del opt['casesensitive'] 1968 else: 1969 self.casesensistive = True 1970 super(SmartQuestion, self).__init__(*arg, **opt)
1971
1972 - def __call__(self, question, reprint_opt=True, **opts):
1973 1974 self.question = question 1975 for key,value in opts: 1976 setattr(self, key, value) 1977 if reprint_opt: 1978 print question 1979 return self.cmdloop()
1980 1981
1982 - def completenames(self, text, line, *ignored):
1983 prev_timer = signal.alarm(0) # avoid timer if any 1984 if prev_timer: 1985 nb_back = len(line) 1986 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 1987 self.stdout.write(line) 1988 self.stdout.flush() 1989 try: 1990 out = {} 1991 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 1992 out[' Recognized command'] = BasicCmd.completenames(self, text) 1993 1994 return self.deal_multiple_categories(out) 1995 except Exception, error: 1996 print error
1997 1998 completedefault = completenames 1999
2000 - def get_names(self):
2001 # This method used to pull in base class attributes 2002 # at a time dir() didn't do it yet. 2003 return dir(self)
2004
2005 - def onecmd(self, line, **opt):
2006 """catch all error and stop properly command accordingly 2007 Interpret the argument as though it had been typed in response 2008 to the prompt. 2009 2010 The return value is a flag indicating whether interpretation of 2011 commands by the interpreter should stop. 2012 2013 This allow to pass extra argument for internal call. 2014 """ 2015 try: 2016 if '~/' in line and os.environ.has_key('HOME'): 2017 line = line.replace('~/', '%s/' % os.environ['HOME']) 2018 line = os.path.expandvars(line) 2019 cmd, arg, line = self.parseline(line) 2020 if not line: 2021 return self.emptyline() 2022 if cmd is None: 2023 return self.default(line) 2024 self.lastcmd = line 2025 if cmd == '': 2026 return self.default(line) 2027 else: 2028 try: 2029 func = getattr(self, 'do_' + cmd) 2030 except AttributeError: 2031 return self.default(line) 2032 return func(arg, **opt) 2033 except Exception as error: 2034 logger.warning(error) 2035 if __debug__: 2036 raise
2037
2038 - def reask(self, reprint_opt=True):
2039 pat = re.compile('\[(\d*)s to answer\]') 2040 prev_timer = signal.alarm(0) # avoid timer if any 2041 2042 if prev_timer: 2043 if pat.search(self.question): 2044 timeout = int(pat.search(self.question).groups()[0]) 2045 else: 2046 timeout=20 2047 print 2048 signal.alarm(timeout) 2049 if reprint_opt: 2050 if not prev_timer: 2051 self.question = pat.sub('',self.question) 2052 print self.question 2053 2054 if self.mother_interface: 2055 answer = self.mother_interface.check_answer_in_input_file(self, 'EOF', 2056 path=self.allowpath) 2057 if answer: 2058 stop = self.default(answer) 2059 self.postcmd(stop, answer) 2060 return False 2061 2062 return False
2063
2064 - def do_help(self, line):
2065 2066 text=line 2067 out ={} 2068 out['Options'] = Cmd.list_completion(text, self.allow_arg) 2069 out['command'] = BasicCmd.completenames(self, text) 2070 2071 if not text: 2072 if out['Options']: 2073 logger.info( "Here is the list of all valid options:", '$MG:color:BLACK') 2074 logger.info( " "+ "\n ".join(out['Options'])) 2075 if out['command']: 2076 logger.info( "Here is the list of command available:", '$MG:color:BLACK') 2077 logger.info( " "+ "\n ".join(out['command'])) 2078 else: 2079 if out['Options']: 2080 logger.info( "Here is the list of all valid options starting with \'%s\'" % text, '$MG:color:BLACK') 2081 logger.info( " "+ "\n ".join(out['Options'])) 2082 if out['command']: 2083 logger.info( "Here is the list of command available starting with \'%s\':" % text, '$MG:color:BLACK') 2084 logger.info( " "+ "\n ".join(out['command'])) 2085 elif not out['Options']: 2086 logger.info( "No possibility starting with \'%s\'" % text, '$MG:color:BLACK') 2087 logger.info( "You can type help XXX, to see all command starting with XXX", '$MG:color:BLACK')
2088 - def complete_help(self, text, line, begidx, endidx):
2089 """ """ 2090 return self.completenames(text, line)
2091
2092 - def default(self, line):
2093 """Default action if line is not recognized""" 2094 2095 if line.strip() == '' and self.default_value is not None: 2096 self.value = self.default_value 2097 else: 2098 self.value = line
2099
2100 - def emptyline(self):
2101 """If empty line, return default""" 2102 2103 if self.default_value is not None: 2104 self.value = self.default_value
2105 2106
2107 - def postcmd(self, stop, line):
2108 2109 try: 2110 if self.value in self.allow_arg: 2111 return True 2112 elif str(self.value) == 'EOF': 2113 self.value = self.default_value 2114 return True 2115 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2116 return self.reask() 2117 elif self.value == 'repeat': 2118 return self.reask() 2119 elif len(self.allow_arg)==0: 2120 return True 2121 elif not self.casesensitive: 2122 for ans in self.allow_arg: 2123 if ans.lower() == self.value.lower(): 2124 self.value = ans 2125 return True 2126 break 2127 else: 2128 raise Exception 2129 else: 2130 raise Exception 2131 except Exception,error: 2132 if self.wrong_answer < 100: 2133 self.wrong_answer += 1 2134 logger.warning("""%s not valid argument. Valid argument are in (%s).""" \ 2135 % (self.value,','.join(self.allow_arg))) 2136 logger.warning('please retry') 2137 return False 2138 else: 2139 self.value = self.default_value 2140 return True
2141
2142 - def cmdloop(self, intro=None):
2143 super(SmartQuestion,self).cmdloop(intro) 2144 return self.answer
2145
2146 # a function helper 2147 -def smart_input(input_text, allow_arg=[], default=None):
2148 print input_text 2149 obj = SmartQuestion(allow_arg=allow_arg, default=default) 2150 return obj.cmdloop()
2151
2152 #=============================================================================== 2153 # Question in order to return a path with auto-completion 2154 #=============================================================================== 2155 -class OneLinePathCompletion(SmartQuestion):
2156 """ a class for answering a question with the path autocompletion""" 2157 2158 completion_prefix='' 2159 allowpath=True 2160
2161 - def completenames(self, text, line, begidx, endidx, formatting=True):
2162 prev_timer = signal.alarm(0) # avoid timer if any 2163 if prev_timer: 2164 nb_back = len(line) 2165 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2166 self.stdout.write(line) 2167 self.stdout.flush() 2168 2169 try: 2170 out = {} 2171 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2172 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 2173 out[' Recognized command'] = BasicCmd.completenames(self, text) 2174 2175 return self.deal_multiple_categories(out, formatting) 2176 except Exception, error: 2177 print error
2178
2179 - def precmd(self, *args):
2180 """ """ 2181 2182 signal.alarm(0) 2183 return SmartQuestion.precmd(self, *args)
2184
2185 - def completedefault(self,text, line, begidx, endidx):
2186 prev_timer = signal.alarm(0) # avoid timer if any 2187 if prev_timer: 2188 nb_back = len(line) 2189 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2190 self.stdout.write(line) 2191 self.stdout.flush() 2192 try: 2193 args = Cmd.split_arg(line[0:begidx]) 2194 except Exception, error: 2195 print error 2196 2197 # Directory continuation 2198 if args[-1].endswith(os.path.sep): 2199 2200 return Cmd.path_completion(text, 2201 os.path.join('.',*[a for a in args \ 2202 if a.endswith(os.path.sep)])) 2203 self.completenames(line+text)
2204 2205
2206 - def postcmd(self, stop, line):
2207 try: 2208 if self.value in self.allow_arg: 2209 return True 2210 elif self.value and os.path.isfile(self.value): 2211 return os.path.relpath(self.value) 2212 elif self.value and str(self.value) == 'EOF': 2213 self.value = self.default_value 2214 return True 2215 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2216 # go to retry 2217 reprint_opt = True 2218 elif self.value == 'repeat': 2219 reprint_opt = True 2220 else: 2221 raise Exception 2222 except Exception, error: 2223 print """not valid argument. Valid argument are file path or value in (%s).""" \ 2224 % ','.join(self.allow_arg) 2225 print 'please retry' 2226 reprint_opt = False 2227 2228 if line != 'EOF': 2229 return self.reask(reprint_opt)
2230
2231 2232 # a function helper 2233 -def raw_path_input(input_text, allow_arg=[], default=None):
2234 print input_text 2235 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 2236 return obj.cmdloop()
2237
2238 #=============================================================================== 2239 # 2240 #=============================================================================== 2241 -class CmdFile(file):
2242 """ a class for command input file -in order to debug cmd \n problem""" 2243
2244 - def __init__(self, name, opt='rU'):
2245 2246 file.__init__(self, name, opt) 2247 self.text = file.read(self) 2248 self.close() 2249 self.lines = self.text.split('\n')
2250
2251 - def readline(self, *arg, **opt):
2252 """readline method treating correctly a line whithout \n at the end 2253 (add it) 2254 """ 2255 if self.lines: 2256 line = self.lines.pop(0) 2257 else: 2258 return '' 2259 2260 if line.endswith('\n'): 2261 return line 2262 else: 2263 return line + '\n'
2264
2265 - def __next__(self):
2266 return self.lines.__next__()
2267
2268 - def __iter__(self):
2269 return self.lines.__iter__()
2270