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 math 
  20  import os 
  21  import pydoc 
  22  import re 
  23  import signal 
  24  import subprocess 
  25  import sys 
  26  import traceback 
  27  try: 
  28      import readline 
  29      GNU_SPLITTING = ('GNU' in readline.__doc__) 
  30  except: 
  31      readline = None 
  32      GNU_SPLITTING = True 
  33   
  34   
  35  logger = logging.getLogger('cmdprint') # for stdout 
  36  logger_stderr = logging.getLogger('fatalerror') # for stderr 
  37  logger_tuto = logging.getLogger('tutorial') # for stdout 
  38  logger_plugin = logging.getLogger('tutorial_plugin') # for stdout 
  39   
  40  try: 
  41      import madgraph.various.misc as misc 
  42      from madgraph import MG5DIR, MadGraph5Error 
  43      MADEVENT = False 
  44  except ImportError, error: 
  45      try: 
  46          import internal.misc as misc 
  47      except: 
  48          raise error 
  49       
  50      MADEVENT = True 
  51   
  52   
  53  pjoin = os.path.join 
54 55 -class TimeOutError(Exception):
56 """Class for run-time error"""
57
58 -def debug(debug_only=True):
59 60 def deco_debug(f): 61 62 if debug_only and not __debug__: 63 return f 64 65 def deco_f(*args, **opt): 66 try: 67 return f(*args, **opt) 68 except Exception, error: 69 logger.error(error) 70 logger.error(traceback.print_exc(file=sys.stdout)) 71 return
72 return deco_f 73 return deco_debug 74 75 import string 76 77 # The following is copy from the standard cmd routine but pass in new class type 78 __all__ = ["Cmd"] 79 PROMPT = '(Cmd) ' 80 IDENTCHARS = string.ascii_letters + string.digits + '_'
81 -class OriginalCmd(object):
82 """A simple framework for writing line-oriented command interpreters. 83 84 These are often useful for test harnesses, administrative tools, and 85 prototypes that will later be wrapped in a more sophisticated interface. 86 87 A Cmd instance or subclass instance is a line-oriented interpreter 88 framework. There is no good reason to instantiate Cmd itself; rather, 89 it's useful as a superclass of an interpreter class you define yourself 90 in order to inherit Cmd's methods and encapsulate action methods. 91 92 """ 93 prompt = PROMPT 94 identchars = IDENTCHARS 95 ruler = '=' 96 lastcmd = '' 97 intro = None 98 doc_leader = "" 99 doc_header = "Documented commands (type help <topic>):" 100 misc_header = "Miscellaneous help topics:" 101 undoc_header = "Undocumented commands:" 102 nohelp = "*** No help on %s" 103 use_rawinput = 1 104
105 - def __init__(self, completekey='tab', stdin=None, stdout=None,**opt):
106 """Instantiate a line-oriented interpreter framework. 107 108 The optional argument 'completekey' is the readline name of a 109 completion key; it defaults to the Tab key. If completekey is 110 not None and the readline module is available, command completion 111 is done automatically. The optional arguments stdin and stdout 112 specify alternate input and output file objects; if not specified, 113 sys.stdin and sys.stdout are used. 114 115 """ 116 import sys 117 if stdin is not None: 118 self.stdin = stdin 119 else: 120 self.stdin = sys.stdin 121 if stdout is not None: 122 self.stdout = stdout 123 else: 124 self.stdout = sys.stdout 125 self.cmdqueue = [] 126 self.completekey = completekey 127 self.cmd_options = opt
128
129 - def cmdloop(self, intro=None):
130 """Repeatedly issue a prompt, accept input, parse an initial prefix 131 off the received input, and dispatch to action methods, passing them 132 the remainder of the line as argument. 133 134 """ 135 136 self.preloop() 137 if self.use_rawinput and self.completekey: 138 try: 139 import readline 140 self.old_completer = readline.get_completer() 141 readline.set_completer(self.complete) 142 readline.parse_and_bind(self.completekey+": complete") 143 except ImportError: 144 pass 145 try: 146 if intro is not None: 147 self.intro = intro 148 if self.intro: 149 self.stdout.write(str(self.intro)+"\n") 150 stop = None 151 while not stop: 152 if self.cmdqueue: 153 line = self.cmdqueue.pop(0) 154 else: 155 if self.use_rawinput: 156 try: 157 line = raw_input(self.prompt) 158 except EOFError: 159 line = 'EOF' 160 else: 161 self.stdout.write(self.prompt) 162 self.stdout.flush() 163 line = self.stdin.readline() 164 if not len(line): 165 line = 'EOF' 166 else: 167 line = line.rstrip('\r\n') 168 line = self.precmd(line) 169 stop = self.onecmd(line) 170 stop = self.postcmd(stop, line) 171 self.postloop() 172 finally: 173 if self.use_rawinput and self.completekey: 174 try: 175 import readline 176 readline.set_completer(self.old_completer) 177 except ImportError: 178 pass
179 180
181 - def precmd(self, line):
182 """Hook method executed just before the command line is 183 interpreted, but after the input prompt is generated and issued. 184 185 """ 186 return line
187
188 - def postcmd(self, stop, line):
189 """Hook method executed just after a command dispatch is finished.""" 190 return stop
191
192 - def preloop(self):
193 """Hook method executed once when the cmdloop() method is called.""" 194 pass
195
196 - def postloop(self):
197 """Hook method executed once when the cmdloop() method is about to 198 return. 199 200 """ 201 pass
202
203 - def parseline(self, line):
204 """Parse the line into a command name and a string containing 205 the arguments. Returns a tuple containing (command, args, line). 206 'command' and 'args' may be None if the line couldn't be parsed. 207 """ 208 line = line.strip() 209 if not line: 210 return None, None, line 211 elif line[0] == '?': 212 line = 'help ' + line[1:] 213 elif line[0] == '!': 214 if hasattr(self, 'do_shell'): 215 line = 'shell ' + line[1:] 216 else: 217 return None, None, line 218 i, n = 0, len(line) 219 while i < n and line[i] in self.identchars: i = i+1 220 cmd, arg = line[:i], line[i:].strip() 221 return cmd, arg, line
222
223 - def onecmd(self, line):
224 """Interpret the argument as though it had been typed in response 225 to the prompt. 226 227 This may be overridden, but should not normally need to be; 228 see the precmd() and postcmd() methods for useful execution hooks. 229 The return value is a flag indicating whether interpretation of 230 commands by the interpreter should stop. 231 232 """ 233 cmd, arg, line = self.parseline(line) 234 if not line: 235 return self.emptyline() 236 if cmd is None: 237 return self.default(line) 238 self.lastcmd = line 239 if cmd == '': 240 return self.default(line) 241 else: 242 try: 243 func = getattr(self, 'do_' + cmd) 244 except AttributeError: 245 return self.default(line) 246 return func(arg)
247
248 - def emptyline(self):
249 """Called when an empty line is entered in response to the prompt. 250 251 If this method is not overridden, it repeats the last nonempty 252 command entered. 253 254 """ 255 if self.lastcmd: 256 return self.onecmd(self.lastcmd)
257
258 - def default(self, line):
259 """Called on an input line when the command prefix is not recognized. 260 261 If this method is not overridden, it prints an error message and 262 returns. 263 264 """ 265 self.stdout.write('*** Unknown syntax: %s\n'%line)
266
267 - def completedefault(self, *ignored):
268 """Method called to complete an input line when no command-specific 269 complete_*() method is available. 270 271 By default, it returns an empty list. 272 273 """ 274 return []
275
276 - def completenames(self, text, *ignored):
277 dotext = 'do_'+text 278 279 done = set() # store the command already handle 280 281 return [a[3:] for a in self.get_names() 282 if a.startswith(dotext) and a not in done and not done.add(a)]
283
284 - def complete(self, text, state):
285 """Return the next possible completion for 'text'. 286 287 If a command has not been entered, then complete against command list. 288 Otherwise try to call complete_<command> to get list of completions. 289 """ 290 if state == 0: 291 import readline 292 origline = readline.get_line_buffer() 293 line = origline.lstrip() 294 stripped = len(origline) - len(line) 295 begidx = readline.get_begidx() - stripped 296 endidx = readline.get_endidx() - stripped 297 if begidx>0: 298 cmd, args, foo = self.parseline(line) 299 if cmd == '': 300 compfunc = self.completedefault 301 else: 302 try: 303 compfunc = getattr(self, 'complete_' + cmd) 304 except AttributeError: 305 compfunc = self.completedefault 306 else: 307 compfunc = self.completenames 308 self.completion_matches = compfunc(text, line, begidx, endidx) 309 try: 310 return self.completion_matches[state] 311 except IndexError: 312 return None
313
314 - def get_names(self):
315 # Inheritance says we have to look in class and 316 # base classes; order is not important. 317 names = [] 318 classes = [self.__class__] 319 while classes: 320 aclass = classes.pop(0) 321 if aclass.__bases__: 322 classes = classes + list(aclass.__bases__) 323 names = names + dir(aclass) 324 return names
325
326 - def complete_help(self, *args):
327 return self.completenames(*args)
328
329 - def do_help(self, arg):
330 if arg: 331 # XXX check arg syntax 332 try: 333 func = getattr(self, 'help_' + arg) 334 except AttributeError: 335 try: 336 doc=getattr(self, 'do_' + arg).__doc__ 337 if doc: 338 self.stdout.write("%s\n"%str(doc)) 339 return 340 except AttributeError: 341 pass 342 self.stdout.write("%s\n"%str(self.nohelp % (arg,))) 343 return 344 func() 345 else: 346 names = self.get_names() 347 cmds_doc = [] 348 cmds_undoc = [] 349 help = {} 350 for name in names: 351 if name[:5] == 'help_': 352 help[name[5:]]=1 353 names.sort() 354 # There can be duplicates if routines overridden 355 prevname = '' 356 for name in names: 357 if name[:3] == 'do_': 358 if name == prevname: 359 continue 360 prevname = name 361 cmd=name[3:] 362 if cmd in help: 363 cmds_doc.append(cmd) 364 del help[cmd] 365 elif getattr(self, name).__doc__: 366 cmds_doc.append(cmd) 367 else: 368 cmds_undoc.append(cmd) 369 self.stdout.write("%s\n"%str(self.doc_leader)) 370 self.print_topics(self.doc_header, cmds_doc, 15,80) 371 self.print_topics(self.misc_header, help.keys(),15,80) 372 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
373
374 - def print_topics(self, header, cmds, cmdlen, maxcol):
375 if cmds: 376 self.stdout.write("%s\n"%str(header)) 377 if self.ruler: 378 self.stdout.write("%s\n"%str(self.ruler * len(header))) 379 self.columnize(cmds, maxcol-1) 380 self.stdout.write("\n")
381
382 - def columnize(self, list, displaywidth=80):
383 """Display a list of strings as a compact set of columns. 384 385 Each column is only as wide as necessary. 386 Columns are separated by two spaces (one was not legible enough). 387 """ 388 if not list: 389 self.stdout.write("<empty>\n") 390 return 391 nonstrings = [i for i in range(len(list)) 392 if not isinstance(list[i], str)] 393 if nonstrings: 394 raise TypeError, ("list[i] not a string for i in %s" % 395 ", ".join(map(str, nonstrings))) 396 size = len(list) 397 if size == 1: 398 self.stdout.write('%s\n'%str(list[0])) 399 return 400 # Try every row count from 1 upwards 401 for nrows in range(1, len(list)): 402 ncols = (size+nrows-1) // nrows 403 colwidths = [] 404 totwidth = -2 405 for col in range(ncols): 406 colwidth = 0 407 for row in range(nrows): 408 i = row + nrows*col 409 if i >= size: 410 break 411 x = list[i] 412 colwidth = max(colwidth, len(x)) 413 colwidths.append(colwidth) 414 totwidth += colwidth + 2 415 if totwidth > displaywidth: 416 break 417 if totwidth <= displaywidth: 418 break 419 else: 420 nrows = len(list) 421 ncols = 1 422 colwidths = [0] 423 for row in range(nrows): 424 texts = [] 425 for col in range(ncols): 426 i = row + nrows*col 427 if i >= size: 428 x = "" 429 else: 430 x = list[i] 431 texts.append(x) 432 while texts and not texts[-1]: 433 del texts[-1] 434 for col in range(len(texts)): 435 texts[col] = texts[col].ljust(colwidths[col]) 436 self.stdout.write("%s\n"%str(" ".join(texts)))
437
438 439 440 441 #=============================================================================== 442 # CmdExtended 443 #=============================================================================== 444 -class BasicCmd(OriginalCmd):
445 """Simple extension for the readline""" 446
448 """ This has been refactorized here so that it can be called when another 449 program called by MG5 (such as MadAnalysis5) changes this attribute of readline""" 450 if readline: 451 if not 'libedit' in readline.__doc__: 452 readline.set_completion_display_matches_hook(self.print_suggestions) 453 else: 454 readline.set_completion_display_matches_hook()
455
456 - def preloop(self):
459
460 - def deal_multiple_categories(self, dico, formatting=True, forceCategory=False):
461 """convert the multiple category in a formatted list understand by our 462 specific readline parser""" 463 464 if not formatting: 465 return dico 466 467 if 'libedit' in readline.__doc__: 468 # No parser in this case, just send all the valid options 469 out = [] 470 for name, opt in dico.items(): 471 out += opt 472 return list(set(out)) 473 474 # check if more than one categories but only one value: 475 if not forceCategory and all(len(s) <= 1 for s in dico.values() ): 476 values = set((s[0] for s in dico.values() if len(s)==1)) 477 if len(values) == 1: 478 return values 479 480 # That's the real work 481 out = [] 482 valid=0 483 # if the key starts with number order the key with that number. 484 for name, opt in dico.items(): 485 if not opt: 486 continue 487 name = name.replace(' ', '_') 488 valid += 1 489 out.append(opt[0].rstrip()+'@@'+name+'@@') 490 # Remove duplicate 491 d = {} 492 for x in opt: 493 d[x] = 1 494 opt = list(d.keys()) 495 opt.sort() 496 out += opt 497 498 if not forceCategory and valid == 1: 499 out = out[1:] 500 501 return out
502 503 @debug()
504 - def print_suggestions(self, substitution, matches, longest_match_length) :
505 """print auto-completions by category""" 506 if not hasattr(self, 'completion_prefix'): 507 self.completion_prefix = '' 508 longest_match_length += len(self.completion_prefix) 509 try: 510 if len(matches) == 1: 511 self.stdout.write(matches[0]+' ') 512 return 513 self.stdout.write('\n') 514 l2 = [a[-2:] for a in matches] 515 if '@@' in l2: 516 nb_column = self.getTerminalSize()//(longest_match_length+1) 517 pos=0 518 for val in self.completion_matches: 519 if val.endswith('@@'): 520 category = val.rsplit('@@',2)[1] 521 category = category.replace('_',' ') 522 self.stdout.write('\n %s:\n%s\n' % (category, '=' * (len(category)+2))) 523 start = 0 524 pos = 0 525 continue 526 elif pos and pos % nb_column ==0: 527 self.stdout.write('\n') 528 self.stdout.write(self.completion_prefix + val + \ 529 ' ' * (longest_match_length +1 -len(val))) 530 pos +=1 531 self.stdout.write('\n') 532 else: 533 # nb column 534 nb_column = self.getTerminalSize()//(longest_match_length+1) 535 for i,val in enumerate(matches): 536 if i and i%nb_column ==0: 537 self.stdout.write('\n') 538 self.stdout.write(self.completion_prefix + val + \ 539 ' ' * (longest_match_length +1 -len(val))) 540 self.stdout.write('\n') 541 542 self.stdout.write(self.prompt+readline.get_line_buffer()) 543 self.stdout.flush() 544 except Exception, error: 545 if __debug__: 546 logger.error(error)
547
548 - def getTerminalSize(self):
549 def ioctl_GWINSZ(fd): 550 try: 551 import fcntl, termios, struct 552 cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 553 '1234')) 554 except Exception: 555 return None 556 return cr
557 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 558 if not cr: 559 try: 560 fd = os.open(os.ctermid(), os.O_RDONLY) 561 cr = ioctl_GWINSZ(fd) 562 os.close(fd) 563 except Exception: 564 pass 565 if not cr: 566 try: 567 cr = (os.environ['LINES'], os.environ['COLUMNS']) 568 except Exception: 569 cr = (25, 80) 570 return int(cr[1])
571
572 - def complete(self, text, state):
573 """Return the next possible completion for 'text'. 574 If a command has not been entered, then complete against command list. 575 Otherwise try to call complete_<command> to get list of completions. 576 """ 577 if state == 0: 578 import readline 579 origline = readline.get_line_buffer() 580 line = origline.lstrip() 581 stripped = len(origline) - len(line) 582 begidx = readline.get_begidx() - stripped 583 endidx = readline.get_endidx() - stripped 584 585 if ';' in line: 586 begin, line = line.rsplit(';',1) 587 begidx = begidx - len(begin) - 1 588 endidx = endidx - len(begin) - 1 589 if line[:begidx] == ' ' * begidx: 590 begidx=0 591 592 if begidx>0: 593 cmd, args, foo = self.parseline(line) 594 if cmd == '': 595 compfunc = self.completedefault 596 else: 597 try: 598 compfunc = getattr(self, 'complete_' + cmd) 599 except AttributeError, error: 600 compfunc = self.completedefault 601 except Exception, error: 602 misc.sprint(error) 603 else: 604 compfunc = self.completenames 605 606 # correct wrong splittion with '\ ' 607 if line and begidx > 2 and line[begidx-2:begidx] == '\ ': 608 Ntext = line.split(os.path.sep)[-1] 609 self.completion_prefix = Ntext.rsplit('\ ', 1)[0] + '\ ' 610 to_rm = len(self.completion_prefix) - 1 611 Nbegidx = len(line.rsplit(os.path.sep, 1)[0]) + 1 612 data = compfunc(Ntext.replace('\ ', ' '), line, Nbegidx, endidx) 613 self.completion_matches = [p[to_rm:] for p in data 614 if len(p)>to_rm] 615 # correct wrong splitting with '-'/"=" 616 elif line and line[begidx-1] in ['-',"=",':']: 617 try: 618 sep = line[begidx-1] 619 Ntext = line.split()[-1] 620 self.completion_prefix = Ntext.rsplit(sep,1)[0] + sep 621 to_rm = len(self.completion_prefix) 622 Nbegidx = len(line.rsplit(None, 1)[0]) 623 data = compfunc(Ntext, line, Nbegidx, endidx) 624 self.completion_matches = [p[to_rm:] for p in data 625 if len(p)>to_rm] 626 except Exception, error: 627 print error 628 else: 629 self.completion_prefix = '' 630 self.completion_matches = compfunc(text, line, begidx, endidx) 631 632 self.completion_matches = [ l if l[-1] in [' ','@','=',os.path.sep] 633 else ((l + ' ') if not l.endswith('\\$') else l[:-2]) 634 for l in self.completion_matches if l] 635 636 try: 637 return self.completion_matches[state] 638 except IndexError, error: 639 # if __debug__: 640 # logger.error('\n Completion ERROR:') 641 # logger.error( error) 642 return None
643 644 @staticmethod
645 - def split_arg(line):
646 """Split a line of arguments""" 647 648 split = line.split() 649 out=[] 650 tmp='' 651 for data in split: 652 if data[-1] == '\\': 653 tmp += data[:-1]+' ' 654 elif tmp: 655 tmp += data 656 tmp = os.path.expanduser(os.path.expandvars(tmp)) 657 out.append(tmp) 658 # Reinitialize tmp in case there is another differen argument 659 # containing escape characters 660 tmp = '' 661 else: 662 out.append(data) 663 return out
664 665 @staticmethod
666 - def list_completion(text, list, line=''):
667 """Propose completions of text in list""" 668 669 if not text: 670 completions = list 671 else: 672 completions = [ f 673 for f in list 674 if f.startswith(text) 675 ] 676 677 return completions
678 679 680 @staticmethod
681 - def path_completion(text, base_dir = None, only_dirs = False, 682 relative=True):
683 """Propose completions of text to compose a valid path""" 684 685 if base_dir is None: 686 base_dir = os.getcwd() 687 base_dir = os.path.expanduser(os.path.expandvars(base_dir)) 688 689 if text == '~': 690 text = '~/' 691 prefix, text = os.path.split(text) 692 prefix = os.path.expanduser(os.path.expandvars(prefix)) 693 base_dir = os.path.join(base_dir, prefix) 694 if prefix: 695 prefix += os.path.sep 696 697 if only_dirs: 698 completion = [prefix + f + os.path.sep 699 for f in os.listdir(base_dir) 700 if f.startswith(text) and \ 701 os.path.isdir(os.path.join(base_dir, f)) and \ 702 (not f.startswith('.') or text.startswith('.')) 703 ] 704 else: 705 completion = [ prefix + f 706 for f in os.listdir(base_dir) 707 if f.startswith(text) and \ 708 os.path.isfile(os.path.join(base_dir, f)) and \ 709 (not f.startswith('.') or text.startswith('.')) 710 ] 711 712 completion = completion + \ 713 [prefix + f + os.path.sep 714 for f in os.listdir(base_dir) 715 if f.startswith(text) and \ 716 os.path.isdir(os.path.join(base_dir, f)) and \ 717 (not f.startswith('.') or text.startswith('.')) 718 ] 719 720 if relative: 721 completion += [prefix + f for f in ['.'+os.path.sep, '..'+os.path.sep] if \ 722 f.startswith(text) and not prefix.startswith('.')] 723 724 completion = [a.replace(' ','\ ') for a in completion] 725 return completion
726
727 728 729 730 -class CheckCmd(object):
731 """Extension of the cmd object for only the check command""" 732
733 - def check_history(self, args):
734 """check the validity of line""" 735 736 if len(args) > 1: 737 self.help_history() 738 raise self.InvalidCmd('\"history\" command takes at most one argument') 739 740 if not len(args): 741 return 742 743 if args[0] =='.': 744 if not self._export_dir: 745 raise self.InvalidCmd("No default directory is defined for \'.\' option") 746 elif args[0] != 'clean': 747 dirpath = os.path.dirname(args[0]) 748 if dirpath and not os.path.exists(dirpath) or \ 749 os.path.isdir(args[0]): 750 raise self.InvalidCmd("invalid path %s " % dirpath)
751
752 - def check_save(self, args):
753 """check that the line is compatible with save options""" 754 755 if len(args) > 2: 756 self.help_save() 757 raise self.InvalidCmd, 'too many arguments for save command.' 758 759 if len(args) == 2: 760 if args[0] != 'options': 761 self.help_save() 762 raise self.InvalidCmd, '\'%s\' is not recognized as first argument.' % \ 763 args[0] 764 else: 765 args.pop(0)
766
767 -class HelpCmd(object):
768 """Extension of the cmd object for only the help command""" 769
770 - def help_quit(self):
771 logger.info("-- terminates the application",'$MG:color:BLUE') 772 logger.info("syntax: quit",'$MG:BOLD')
773 774 help_EOF = help_quit 775
776 - def help_history(self):
777 logger.info("-- interact with the command history.",'$MG:color:BLUE') 778 logger.info("syntax: history [FILEPATH|clean|.] ",'$MG:BOLD') 779 logger.info(" > If FILEPATH is \'.\' and \'output\' is done,") 780 logger.info(" Cards/proc_card_mg5.dat will be used.") 781 logger.info(" > If FILEPATH is omitted, the history will be output to stdout.") 782 logger.info(" \"clean\" will remove all entries from the history.")
783
784 - def help_help(self):
785 logger.info("-- access to the in-line help",'$MG:color:BLUE') 786 logger.info("syntax: help",'$MG:BOLD')
787
788 - def help_save(self):
789 """help text for save""" 790 logger.info("-- save options configuration to filepath.",'$MG:color:BLUE') 791 logger.info("syntax: save [options] [FILEPATH]",'$MG:BOLD')
792
793 - def help_display(self):
794 """help for display command""" 795 logger.info("-- display a the status of various internal state variables",'$MG:color:BLUE') 796 logger.info("syntax: display " + "|".join(self._display_opts),'$MG:BOLD')
797
798 -class CompleteCmd(object):
799 """Extension of the cmd object for only the complete command""" 800
801 - def complete_display(self,text, line, begidx, endidx):
802 args = self.split_arg(line[0:begidx]) 803 # Format 804 if len(args) == 1: 805 return self.list_completion(text, self._display_opts)
806
807 - def complete_history(self, text, line, begidx, endidx):
808 "Complete the history command" 809 810 args = self.split_arg(line[0:begidx]) 811 812 # Directory continuation 813 if args[-1].endswith(os.path.sep): 814 return self.path_completion(text, 815 os.path.join('.',*[a for a in args \ 816 if a.endswith(os.path.sep)])) 817 818 if len(args) == 1: 819 return self.path_completion(text)
820
821 - def complete_save(self, text, line, begidx, endidx):
822 "Complete the save command" 823 824 args = self.split_arg(line[0:begidx]) 825 826 # Format 827 if len(args) == 1: 828 return self.list_completion(text, ['options']) 829 830 # Directory continuation 831 if args[-1].endswith(os.path.sep): 832 return self.path_completion(text, 833 pjoin('.',*[a for a in args if a.endswith(os.path.sep)]), 834 only_dirs = True) 835 836 # Filename if directory is not given 837 if len(args) == 2: 838 return self.path_completion(text)
839
840 -class Cmd(CheckCmd, HelpCmd, CompleteCmd, BasicCmd):
841 """Extension of the cmd.Cmd command line. 842 This extensions supports line breaking, history, comments, 843 internal call to cmdline, path completion,... 844 this class should be MG5 independent""" 845 846 #suggested list of command 847 next_possibility = {} # command : [list of suggested command] 848 history_header = "" 849 850 _display_opts = ['options','variable'] 851 allow_notification_center = True 852
853 - class InvalidCmd(Exception):
854 """expected error for wrong command""" 855 pass
856 857 ConfigurationError = InvalidCmd 858 859 debug_output = 'debug' 860 error_debug = """Please report this bug to developers\n 861 More information is found in '%(debug)s'.\n 862 Please attach this file to your report.""" 863 config_debug = error_debug 864 865 keyboard_stop_msg = """stopping all current operation 866 in order to quit the program please enter exit""" 867 868 if MADEVENT: 869 plugin_path = [] 870 else: 871 plugin_path = [pjoin(MG5DIR, 'PLUGIN')] 872 if 'PYTHONPATH' in os.environ: 873 for PluginCandidate in os.environ['PYTHONPATH'].split(':'): 874 try: 875 dirlist = os.listdir(PluginCandidate) 876 except OSError: 877 continue 878 for onedir in dirlist: 879 if onedir == 'MG5aMC_PLUGIN': 880 plugin_path.append(pjoin(PluginCandidate, 'MG5aMC_PLUGIN')) 881 break 882 else: 883 continue 884 break 885
886 - def __init__(self, *arg, **opt):
887 """Init history and line continuation""" 888 889 self.log = True 890 self.history = [] 891 self.save_line = '' # for line splitting 892 super(Cmd, self).__init__(*arg, **opt) 893 self.__initpos = os.path.abspath(os.getcwd()) 894 self.child = None # sub CMD interface call from this one 895 self.mother = None #This CMD interface was called from another one 896 self.inputfile = None # input file (in non interactive mode) 897 self.haspiping = not sys.stdin.isatty() # check if mg5 is piped 898 self.stored_line = '' # for be able to treat answer to question in input file 899 # answer which are not required. 900 if not hasattr(self, 'helporder'): 901 self.helporder = ['Documented commands']
902
903 - def preloop(self):
904 """Hook method executed once when the cmdloop() method is called.""" 905 if self.completekey: 906 try: 907 import readline 908 self.old_completer = readline.get_completer() 909 readline.set_completer(self.complete) 910 readline.parse_and_bind(self.completekey+": complete") 911 except ImportError: 912 readline = None 913 pass 914 if readline and not 'libedit' in readline.__doc__: 915 readline.set_completion_display_matches_hook(self.print_suggestions)
916 917
918 - def cmdloop(self, intro=None):
919 920 self.preloop() 921 if intro is not None: 922 self.intro = intro 923 if self.intro: 924 print self.intro 925 stop = None 926 while not stop: 927 if self.cmdqueue: 928 line = self.cmdqueue[0] 929 del self.cmdqueue[0] 930 else: 931 if self.use_rawinput: 932 try: 933 line = raw_input(self.prompt) 934 except EOFError: 935 line = 'EOF' 936 else: 937 sys.stdout.write(self.prompt) 938 sys.stdout.flush() 939 line = sys.stdin.readline() 940 if not len(line): 941 line = 'EOF' 942 else: 943 line = line[:-1] # chop \n 944 try: 945 line = self.precmd(line) 946 stop = self.onecmd(line) 947 except BaseException, error: 948 self.error_handling(error, line) 949 if isinstance(error, KeyboardInterrupt): 950 stop = True 951 finally: 952 stop = self.postcmd(stop, line) 953 self.postloop()
954
955 - def no_notification(self):
956 """avoid to have html opening / notification""" 957 self.allow_notification_center = False 958 try: 959 self.options['automatic_html_opening'] = False 960 self.options['notification_center'] = False 961 962 except: 963 pass
964 965
966 - def precmd(self, line):
967 """ A suite of additional function needed for in the cmd 968 this implement history, line breaking, comment treatment,... 969 """ 970 971 if not line: 972 return line 973 974 # Check if we are continuing a line: 975 if self.save_line: 976 line = self.save_line + line 977 self.save_line = '' 978 979 line = line.lstrip() 980 # Check if the line is complete 981 if line.endswith('\\'): 982 self.save_line = line[:-1] 983 return '' # do nothing 984 985 # Remove comment 986 if '#' in line: 987 line = line.split('#')[0] 988 989 # Deal with line splitting 990 if ';' in line: 991 lines = line.split(';') 992 for subline in lines: 993 if not (subline.startswith("history") or subline.startswith('help') \ 994 or subline.startswith('#*')): 995 self.history.append(subline) 996 stop = self.onecmd_orig(subline) 997 stop = self.postcmd(stop, subline) 998 return '' 999 1000 # execute the line command 1001 self.history.append(line) 1002 return line
1003
1004 - def postcmd(self,stop, line):
1005 """ finishing a command 1006 This looks if the command add a special post part.""" 1007 1008 if line.strip(): 1009 try: 1010 cmd, subline = line.split(None, 1) 1011 except ValueError: 1012 pass 1013 else: 1014 if hasattr(self,'post_%s' %cmd): 1015 stop = getattr(self, 'post_%s' % cmd)(stop, subline) 1016 return stop
1017
1018 - def define_child_cmd_interface(self, obj_instance, interface=True):
1019 """Define a sub cmd_interface""" 1020 1021 # We are in a file reading mode. So we need to redirect the cmd 1022 self.child = obj_instance 1023 self.child.mother = self 1024 1025 1026 #ensure that notification are sync: 1027 self.child.allow_notification_center = self.allow_notification_center 1028 1029 if self.use_rawinput and interface: 1030 # We are in interactive mode -> simply call the child 1031 obj_instance.cmdloop() 1032 stop = obj_instance.postloop() 1033 return stop 1034 if self.inputfile: 1035 # we are in non interactive mode -> so pass the line information 1036 obj_instance.inputfile = self.inputfile 1037 1038 obj_instance.haspiping = self.haspiping 1039 1040 if not interface: 1041 return self.child
1042 1043 #=============================================================================== 1044 # Ask a question with nice options handling 1045 #===============================================================================
1046 - def ask(self, question, default, choices=[], path_msg=None, 1047 timeout = True, fct_timeout=None, ask_class=None, alias={}, 1048 first_cmd=None, text_format='4', force=False, 1049 return_instance=False, **opt):
1050 """ ask a question with some pre-define possibility 1051 path info is 1052 """ 1053 1054 if path_msg: 1055 path_msg = [path_msg] 1056 else: 1057 path_msg = [] 1058 1059 if timeout is True: 1060 try: 1061 timeout = self.options['timeout'] 1062 except Exception: 1063 pass 1064 1065 # add choice info to the question 1066 if choices + path_msg: 1067 question += ' [' 1068 question += "\033[%sm%s\033[0m, " % (text_format, default) 1069 for data in choices[:9] + path_msg: 1070 if default == data: 1071 continue 1072 else: 1073 question += "%s, " % data 1074 1075 if len(choices) > 9: 1076 question += '... , ' 1077 question = question[:-2]+']' 1078 else: 1079 question += "[\033[%sm%s\033[0m] " % (text_format, default) 1080 if ask_class: 1081 obj = ask_class 1082 elif path_msg: 1083 obj = OneLinePathCompletion 1084 else: 1085 obj = SmartQuestion 1086 1087 if alias: 1088 choices += alias.keys() 1089 1090 question_instance = obj(question, allow_arg=choices, default=default, 1091 mother_interface=self, **opt) 1092 1093 if first_cmd: 1094 if isinstance(first_cmd, str): 1095 question_instance.onecmd(first_cmd) 1096 else: 1097 for line in first_cmd: 1098 question_instance.onecmd(line) 1099 if not self.haspiping: 1100 if hasattr(obj, "haspiping"): 1101 obj.haspiping = self.haspiping 1102 1103 if force: 1104 answer = default 1105 else: 1106 answer = self.check_answer_in_input_file(question_instance, default, path_msg) 1107 if answer is not None: 1108 if answer in alias: 1109 answer = alias[answer] 1110 if ask_class: 1111 line=answer 1112 answer = question_instance.default(line) 1113 question_instance.postcmd(answer, line) 1114 if not return_instance: 1115 return question_instance.answer 1116 else: 1117 return question_instance.answer , question_instance 1118 if hasattr(question_instance, 'check_answer_consistency'): 1119 question_instance.check_answer_consistency() 1120 if not return_instance: 1121 return answer 1122 else: 1123 return answer, question_instance 1124 1125 question = question_instance.question 1126 if not force: 1127 value = Cmd.timed_input(question, default, timeout=timeout, 1128 fct=question_instance, fct_timeout=fct_timeout) 1129 else: 1130 value = default 1131 1132 try: 1133 if value in alias: 1134 value = alias[value] 1135 except TypeError: 1136 pass 1137 1138 if value == default and ask_class: 1139 value = question_instance.default(default) 1140 1141 if not return_instance: 1142 return value 1143 else: 1144 return value, question_instance
1145
1146 - def do_import(self, line):
1147 """Advanced commands: Import command files""" 1148 1149 args = self.split_arg(line) 1150 # Check argument's validity 1151 self.check_import(args) 1152 1153 # Execute the card 1154 self.import_command_file(args[1])
1155 1156
1157 - def check_import(self, args):
1158 """check import command""" 1159 1160 if '-f' in args: 1161 self.force = True 1162 args.remove('-f') 1163 if args[0] != 'command': 1164 args.set(0, 'command') 1165 if len(args) != 2: 1166 raise self.InvalidCmd('import command requires one filepath argument') 1167 if not os.path.exists(args[1]): 1168 raise 'No such file or directory %s' % args[1]
1169 1170
1171 - def check_answer_in_input_file(self, question_instance, default, path=False, line=None):
1172 """Questions can have answer in output file (or not)""" 1173 1174 1175 if not self.inputfile: 1176 return None# interactive mode 1177 1178 if line is None: 1179 line = self.get_stored_line() 1180 # line define if a previous answer was not answer correctly 1181 1182 if not line: 1183 try: 1184 line = self.inputfile.next() 1185 except StopIteration: 1186 if self.haspiping: 1187 logger.debug('piping') 1188 self.store_line(line) 1189 return None # print the question and use the pipe 1190 logger.info(question_instance.question) 1191 logger.info('The answer to the previous question is not set in your input file', '$MG:BOLD') 1192 logger.info('Use %s value' % default, '$MG:BOLD') 1193 return str(default) 1194 1195 line = line.replace('\n','').strip() 1196 if '#' in line: 1197 line = line.split('#')[0] 1198 if not line: 1199 # Comment or empty line, pass to the next one 1200 return self.check_answer_in_input_file(question_instance, default, path) 1201 1202 options = question_instance.allow_arg 1203 if line in options or line.lower() in options: 1204 return line 1205 elif '%s;' % line in options or '%s;' % line.lower() in options: 1206 return line 1207 elif hasattr(question_instance, 'do_%s' % line.split()[0]): 1208 #This is a command line, exec it and check next line 1209 logger.info(line) 1210 fct = getattr(question_instance, 'do_%s' % line.split()[0]) 1211 fct(' '.join(line.split()[1:])) 1212 return self.check_answer_in_input_file(question_instance, default, path) 1213 elif path: 1214 line = os.path.expanduser(os.path.expandvars(line)) 1215 if os.path.isfile(line): 1216 return line 1217 if line.startswith(('http', 'www')): 1218 return line 1219 elif hasattr(question_instance, 'casesensitive') and not question_instance.casesensitive: 1220 for entry in question_instance.allow_arg: 1221 if line.lower() == entry.lower(): 1222 return entry 1223 elif any(line.lower()==opt.lower() for opt in options): 1224 possibility = [opt for opt in options if line.lower()==opt.lower()] 1225 if len (possibility)==1: 1226 return possibility[0] 1227 if '=' in line and ' ' in line.strip(): 1228 leninit = len(line) 1229 line,n = re.subn('\s*=\s*','=', line) 1230 if n and len(line) != leninit: 1231 return self.check_answer_in_input_file(question_instance, default, path=path, line=line) 1232 1233 1234 if hasattr(question_instance, 'special_check_answer_in_input_file'): 1235 out = question_instance.special_check_answer_in_input_file(line, default) 1236 1237 if out is not None: 1238 return out 1239 1240 #else: 1241 # misc.sprint('No special check', type(question_instance)) 1242 1243 1244 # No valid answer provides 1245 if self.haspiping: 1246 self.store_line(line) 1247 return None # print the question and use the pipe 1248 else: 1249 logger.info(question_instance.question) 1250 logger.warning('found line : %s' % line) 1251 logger.warning('This answer is not valid for current question. Keep it for next question and use here default: %s', default) 1252 self.store_line(line) 1253 return str(default)
1254
1255 - def store_line(self, line):
1256 """store a line of the input file which should be executed by the higher mother""" 1257 1258 if self.mother: 1259 self.mother.store_line(line) 1260 else: 1261 self.stored_line = line
1262
1263 - def get_stored_line(self):
1264 """return stored line and clean it""" 1265 if self.mother: 1266 value = self.mother.get_stored_line() 1267 self.mother.stored_line = None 1268 else: 1269 value = self.stored_line 1270 self.stored_line = None 1271 return value
1272 1273 1274
1275 - def nice_error_handling(self, error, line):
1276 """ """ 1277 # Make sure that we are at the initial position 1278 if self.child: 1279 return self.child.nice_error_handling(error, line) 1280 1281 os.chdir(self.__initpos) 1282 # Create the debug files 1283 self.log = False 1284 if os.path.exists(self.debug_output): 1285 os.remove(self.debug_output) 1286 try: 1287 super(Cmd,self).onecmd('history %s' % self.debug_output.replace(' ', '\ ')) 1288 except Exception, error: 1289 logger.error(error) 1290 1291 debug_file = open(self.debug_output, 'a') 1292 traceback.print_exc(file=debug_file) 1293 if hasattr(error, 'filename'): 1294 debug_file.write("Related File: %s\n" % error.filename) 1295 # Create a nice error output 1296 if self.history and line == self.history[-1]: 1297 error_text = 'Command \"%s\" interrupted with error:\n' % line 1298 elif self.history: 1299 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1300 error_text += '\"%s\" with error:\n' % self.history[-1] 1301 else: 1302 error_text = '' 1303 error_text += '%s : %s\n' % (error.__class__.__name__, 1304 str(error).replace('\n','\n\t')) 1305 error_text += self.error_debug % {'debug':self.debug_output} 1306 logger_stderr.critical(error_text) 1307 1308 1309 # Add options status to the debug file 1310 try: 1311 self.do_display('options', debug_file) 1312 except Exception, error: 1313 debug_file.write('Fail to write options with error %s' % error) 1314 1315 #add the cards: 1316 for card in ['proc_card_mg5.dat','param_card.dat', 'run_card.dat']: 1317 try: 1318 ff = open(pjoin(self.me_dir, 'Cards', card)) 1319 debug_file.write(ff.read()) 1320 ff.close() 1321 except Exception: 1322 pass 1323 1324 if hasattr(self, 'options') and 'crash_on_error' in self.options: 1325 if self.options['crash_on_error'] is True: 1326 logger.info('stop computation due to crash_on_error=True') 1327 sys.exit(str(error)) 1328 elif self.options['crash_on_error'] == 'never': 1329 return False 1330 1331 #stop the execution if on a non interactive mode 1332 if self.use_rawinput == False or self.inputfile: 1333 return True 1334 elif self.mother: 1335 if self.mother.use_rawinput is False: 1336 return True 1337 1338 elif self.mother.mother: 1339 if self.mother.mother.use_rawinput is False: 1340 return True 1341 1342 return False
1343 1344 1345
1346 - def nice_user_error(self, error, line):
1347 if self.child: 1348 return self.child.nice_user_error(error, line) 1349 1350 # Make sure that we are at the initial position 1351 os.chdir(self.__initpos) 1352 if not self.history or line == self.history[-1]: 1353 error_text = 'Command \"%s\" interrupted with error:\n' % line 1354 else: 1355 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1356 error_text += '\"%s\" with error:\n' % self.history[-1] 1357 error_text += '%s : %s' % (error.__class__.__name__, 1358 str(error).replace('\n','\n\t')) 1359 logger_stderr.error(error_text) 1360 1361 if hasattr(self, 'options') and 'crash_on_error' in self.options: 1362 if self.options['crash_on_error'] is True: 1363 logger.info('stop computation due to crash_on_error=True') 1364 sys.exit(str(error)) 1365 elif self.options['crash_on_error'] == 'never': 1366 self.history.pop() 1367 return False 1368 1369 #stop the execution if on a non interactive mode 1370 if self.use_rawinput == False or self.inputfile: 1371 return True 1372 elif self.mother: 1373 if self.mother.use_rawinput is False: 1374 return True 1375 elif self.mother.mother: 1376 if self.mother.mother.use_rawinput is False: 1377 return True 1378 1379 # Remove failed command from history 1380 self.history.pop() 1381 return False
1382
1383 - def nice_config_error(self, error, line):
1384 if self.child: 1385 return self.child.nice_user_error(error, line) 1386 1387 # Make sure that we are at the initial position 1388 os.chdir(self.__initpos) 1389 if not self.history or line == self.history[-1]: 1390 error_text = 'Error detected in \"%s\"\n' % line 1391 else: 1392 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 1393 error_text += 'write debug file %s \n' % self.debug_output 1394 self.log = False 1395 super(Cmd,self).onecmd('history %s' % self.debug_output) 1396 debug_file = open(self.debug_output, 'a') 1397 traceback.print_exc(file=debug_file) 1398 error_text += self.config_debug % {'debug' :self.debug_output} 1399 error_text += '%s : %s' % (error.__class__.__name__, 1400 str(error).replace('\n','\n\t')) 1401 logger_stderr.error(error_text) 1402 1403 # Add options status to the debug file 1404 try: 1405 self.do_display('options', debug_file) 1406 except Exception, error: 1407 debug_file.write('Fail to write options with error %s' % error) 1408 1409 if hasattr(self, 'options') and 'crash_on_error' in self.options: 1410 if self.options['crash_on_error'] is True: 1411 logger.info('stop computation due to crash_on_error=True') 1412 sys.exit(str(error)) 1413 elif self.options['crash_on_error'] == 'never': 1414 if self.history: 1415 self.history.pop() 1416 return False 1417 1418 1419 1420 #stop the execution if on a non interactive mode 1421 if self.use_rawinput == False or self.inputfile: 1422 return True 1423 elif self.mother: 1424 if self.mother.use_rawinput is False: 1425 return True 1426 elif self.mother.mother: 1427 if self.mother.mother.use_rawinput is False: 1428 return True 1429 1430 # Remove failed command from history 1431 if self.history: 1432 self.history.pop() 1433 return False
1434
1435 - def onecmd_orig(self, line, **opt):
1436 """Interpret the argument as though it had been typed in response 1437 to the prompt. 1438 1439 The return value is a flag indicating whether interpretation of 1440 commands by the interpreter should stop. 1441 1442 This allow to pass extra argument for internal call. 1443 """ 1444 if '~/' in line and os.environ.has_key('HOME'): 1445 line = line.replace('~/', '%s/' % os.environ['HOME']) 1446 if '#' in line: 1447 line = line.split('#')[0] 1448 1449 line = os.path.expandvars(line) 1450 cmd, arg, line = self.parseline(line) 1451 if not line: 1452 return self.emptyline() 1453 if cmd is None: 1454 return self.default(line) 1455 self.lastcmd = line 1456 if cmd == '': 1457 return self.default(line) 1458 else: 1459 try: 1460 func = getattr(self, 'do_' + cmd) 1461 except AttributeError: 1462 return self.default(line) 1463 return func(arg, **opt)
1464
1465 - def error_handling(self, error, line):
1466 1467 me_dir = '' 1468 if hasattr(self, 'me_dir'): 1469 me_dir = os.path.basename(me_dir) + ' ' 1470 1471 misc.EasterEgg('error') 1472 stop=False 1473 try: 1474 raise 1475 except self.InvalidCmd as error: 1476 if __debug__: 1477 stop = self.nice_error_handling(error, line) 1478 self.history.pop() 1479 else: 1480 stop = self.nice_user_error(error, line) 1481 1482 if self.allow_notification_center: 1483 misc.apple_notify('Run %sfailed' % me_dir, 1484 'Invalid Command: %s' % error.__class__.__name__) 1485 1486 except self.ConfigurationError as error: 1487 stop = self.nice_config_error(error, line) 1488 if self.allow_notification_center: 1489 misc.apple_notify('Run %sfailed' % me_dir, 1490 'Configuration error') 1491 except Exception as error: 1492 stop = self.nice_error_handling(error, line) 1493 if self.mother: 1494 self.do_quit('') 1495 if self.allow_notification_center: 1496 misc.apple_notify('Run %sfailed' % me_dir, 1497 'Exception: %s' % error.__class__.__name__) 1498 except KeyboardInterrupt as error: 1499 self.stop_on_keyboard_stop() 1500 if __debug__: 1501 self.nice_config_error(error, line) 1502 logger.error(self.keyboard_stop_msg) 1503 1504 if stop: 1505 self.do_quit('all') 1506 return stop
1507 1508 1509
1510 - def onecmd(self, line, **opt):
1511 """catch all error and stop properly command accordingly""" 1512 1513 try: 1514 return self.onecmd_orig(line, **opt) 1515 except BaseException, error: 1516 return self.error_handling(error, line)
1517 1518
1519 - def stop_on_keyboard_stop(self):
1520 """action to perform to close nicely on a keyboard interupt""" 1521 pass # dummy function
1522
1523 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 1524 precmd=False, postcmd=True, 1525 child=True, **opt):
1526 """for third party call, call the line with pre and postfix treatment 1527 without global error handling """ 1528 1529 1530 if printcmd and not line.startswith('#'): 1531 logger.info(line) 1532 if self.child and child: 1533 current_interface = self.child 1534 else: 1535 current_interface = self 1536 if precmd: 1537 line = current_interface.precmd(line) 1538 if errorhandling or \ 1539 (hasattr(self, 'options') and 'crash_on_error' in self.options and 1540 self.options['crash_on_error']=='never'): 1541 stop = current_interface.onecmd(line, **opt) 1542 else: 1543 stop = Cmd.onecmd_orig(current_interface, line, **opt) 1544 if postcmd: 1545 stop = current_interface.postcmd(stop, line) 1546 return stop
1547
1548 - def run_cmd(self, line):
1549 """for third party call, call the line with pre and postfix treatment 1550 with global error handling""" 1551 1552 return self.exec_cmd(line, errorhandling=True, precmd=True)
1553
1554 - def emptyline(self):
1555 """If empty line, do nothing. Default is repeat previous command.""" 1556 pass
1557
1558 - def default(self, line, log=True):
1559 """Default action if line is not recognized""" 1560 1561 # Faulty command 1562 if log: 1563 logger.warning("Command \"%s\" not recognized, please try again" % \ 1564 line.split()[0]) 1565 if line.strip() in ['q', '.q', 'stop']: 1566 logger.info("If you want to quit mg5 please type \"exit\".") 1567 1568 if self.history and self.history[-1] == line: 1569 self.history.pop()
1570 1571 # Write the list of command line use in this session
1572 - def do_history(self, line):
1573 """write in a file the suite of command that was used""" 1574 1575 args = self.split_arg(line) 1576 # Check arguments validity 1577 self.check_history(args) 1578 1579 if len(args) == 0: 1580 logger.info('\n'.join(self.history)) 1581 return 1582 elif args[0] == 'clean': 1583 self.history = [] 1584 logger.info('History is cleaned') 1585 return 1586 elif args[0] == '.': 1587 output_file = os.path.join(self._export_dir, 'Cards', \ 1588 'proc_card_mg5.dat') 1589 output_file = open(output_file, 'w') 1590 else: 1591 output_file = open(args[0], 'w') 1592 1593 # Create the command file 1594 text = self.get_history_header() 1595 text += ('\n'.join(self.history) + '\n') 1596 1597 #write this information in a file 1598 output_file.write(text) 1599 output_file.close() 1600 1601 if self.log: 1602 logger.info("History written to " + output_file.name)
1603
1604 - def compile(self, *args, **opts):
1605 """ """ 1606 1607 return misc.compile(nb_core=self.options['nb_core'], *args, **opts)
1608
1609 - def avoid_history_duplicate(self, line, no_break=[]):
1610 """remove all line in history (but the last) starting with line. 1611 up to the point when a line didn't start by something in no_break. 1612 (reading in reverse order)""" 1613 1614 new_history = [] 1615 for i in range(1, len(self.history)+1): 1616 cur_line = self.history[-i] 1617 if i == 1: 1618 new_history.append(cur_line) 1619 elif not any((cur_line.startswith(text) for text in no_break)): 1620 to_add = self.history[:-i+1] 1621 to_add.reverse() 1622 new_history += to_add 1623 break 1624 elif cur_line.startswith(line): 1625 continue 1626 else: 1627 new_history.append(cur_line) 1628 1629 new_history.reverse() 1630 self.history[:] = new_history
1631 1632
1633 - def import_command_file(self, filepath):
1634 # remove this call from history 1635 if self.history: 1636 self.history.pop() 1637 1638 1639 #avoid that command of other file interfere with this one. 1640 previous_store_line = self.get_stored_line() 1641 1642 # Read the lines of the file and execute them 1643 if isinstance(filepath, str): 1644 commandline = open(filepath).readlines() 1645 else: 1646 commandline = filepath 1647 oldinputfile = self.inputfile 1648 oldraw = self.use_rawinput 1649 self.inputfile = (l for l in commandline) # make a generator 1650 self.use_rawinput = False 1651 # Note using "for line in open(filepath)" is not safe since the file 1652 # filepath can be overwritten during the run (leading to weird results) 1653 # Note also that we need a generator and not a list. 1654 for line in self.inputfile: 1655 1656 #remove pointless spaces and \n 1657 line = line.replace('\n', '').strip() 1658 # execute the line 1659 if line: 1660 self.exec_cmd(line, precmd=True) 1661 stored = self.get_stored_line() 1662 while stored: 1663 line = stored 1664 self.exec_cmd(line, precmd=True) 1665 stored = self.get_stored_line() 1666 1667 # If a child was open close it 1668 if self.child: 1669 self.child.exec_cmd('quit') 1670 self.inputfile = oldinputfile 1671 self.use_rawinput = oldraw 1672 1673 # restore original store line 1674 cmd = self 1675 while hasattr(cmd, 'mother') and cmd.mother: 1676 cmd = cmd.mother 1677 cmd.stored_line = previous_store_line 1678 return
1679
1680 - def get_history_header(self):
1681 """Default history header""" 1682 1683 return self.history_header
1684
1685 - def postloop(self):
1686 """ """ 1687 1688 if self.use_rawinput and self.completekey: 1689 try: 1690 import readline 1691 readline.set_completer(self.old_completer) 1692 del self.old_completer 1693 except ImportError: 1694 pass 1695 except AttributeError: 1696 pass 1697 1698 args = self.split_arg(self.lastcmd) 1699 if args and args[0] in ['quit','exit']: 1700 if 'all' in args: 1701 return True 1702 if len(args) >1 and args[1].isdigit(): 1703 if args[1] not in ['0', '1']: 1704 return True 1705 1706 return False
1707 1708 #=============================================================================== 1709 # Ask a question with a maximum amount of time to answer 1710 #=============================================================================== 1711 @staticmethod
1712 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 1713 fct_timeout=None):
1714 """ a question with a maximal time to answer take default otherwise""" 1715 1716 def handle_alarm(signum, frame): 1717 raise TimeOutError
1718 1719 signal.signal(signal.SIGALRM, handle_alarm) 1720 1721 if fct is None: 1722 fct = raw_input 1723 1724 if timeout: 1725 signal.alarm(timeout) 1726 question += '[%ss to answer] ' % (timeout) 1727 try: 1728 result = fct(question) 1729 except TimeOutError: 1730 if noerror: 1731 logger.info('\nuse %s' % default) 1732 if fct_timeout: 1733 fct_timeout(True) 1734 return default 1735 else: 1736 signal.alarm(0) 1737 raise 1738 finally: 1739 signal.alarm(0) 1740 if fct_timeout: 1741 fct_timeout(False) 1742 return result
1743 1744 1745 1746 1747 1748 1749 # Quit
1750 - def do_quit(self, line):
1751 """Not in help: exit the mainloop() """ 1752 1753 if self.child: 1754 self.child.exec_cmd('quit ' + line, printcmd=False) 1755 return 1756 elif self.mother: 1757 self.mother.child = None 1758 if line == 'all': 1759 self.mother.do_quit('all') 1760 pass 1761 elif line: 1762 level = int(line) - 1 1763 if level: 1764 self.mother.lastcmd = 'quit %s' % level 1765 elif self.inputfile: 1766 for line in self.inputfile: 1767 logger.warning('command not executed: %s' % line.replace('\n','')) 1768 1769 return True
1770 1771 # Aliases 1772 do_EOF = do_quit 1773 do_exit = do_quit 1774
1775 - def do_help(self, line):
1776 """Not in help: propose some usefull possible action """ 1777 1778 # if they are an argument use the default help 1779 if line: 1780 return super(Cmd, self).do_help(line) 1781 1782 1783 names = self.get_names() 1784 cmds = {} 1785 names.sort() 1786 # There can be duplicates if routines overridden 1787 prevname = '' 1788 for name in names: 1789 if name[:3] == 'do_': 1790 if name == prevname: 1791 continue 1792 prevname = name 1793 cmdname=name[3:] 1794 try: 1795 doc = getattr(self.cmd, name).__doc__ 1796 except Exception: 1797 doc = None 1798 if not doc: 1799 doc = getattr(self, name).__doc__ 1800 if not doc: 1801 tag = "Documented commands" 1802 elif ':' in doc: 1803 tag = doc.split(':',1)[0] 1804 else: 1805 tag = "Documented commands" 1806 if tag in cmds: 1807 cmds[tag].append(cmdname) 1808 else: 1809 cmds[tag] = [cmdname] 1810 1811 self.stdout.write("%s\n"%str(self.doc_leader)) 1812 for tag in self.helporder: 1813 if tag not in cmds: 1814 continue 1815 header = "%s (type help <topic>):" % tag 1816 self.print_topics(header, cmds[tag], 15,80) 1817 for name, item in cmds.items(): 1818 if name in self.helporder: 1819 continue 1820 if name == "Not in help": 1821 continue 1822 header = "%s (type help <topic>):" % name 1823 self.print_topics(header, item, 15,80) 1824 1825 1826 ## Add contextual help 1827 if len(self.history) == 0: 1828 last_action_2 = last_action = 'start' 1829 else: 1830 last_action_2 = last_action = 'none' 1831 1832 pos = 0 1833 authorize = self.next_possibility.keys() 1834 while last_action_2 not in authorize and last_action not in authorize: 1835 pos += 1 1836 if pos > len(self.history): 1837 last_action_2 = last_action = 'start' 1838 break 1839 1840 args = self.history[-1 * pos].split() 1841 last_action = args[0] 1842 if len(args)>1: 1843 last_action_2 = '%s %s' % (last_action, args[1]) 1844 else: 1845 last_action_2 = 'none' 1846 1847 logger.info('Contextual Help') 1848 logger.info('===============') 1849 if last_action_2 in authorize: 1850 options = self.next_possibility[last_action_2] 1851 elif last_action in authorize: 1852 options = self.next_possibility[last_action] 1853 else: 1854 return 1855 text = 'The following command(s) may be useful in order to continue.\n' 1856 for option in options: 1857 text+='\t %s \n' % option 1858 logger.info(text)
1859
1860 - def do_display(self, line, output=sys.stdout):
1861 """Advanced commands: basic display""" 1862 1863 args = self.split_arg(line) 1864 #check the validity of the arguments 1865 1866 if len(args) == 0: 1867 self.help_display() 1868 raise self.InvalidCmd, 'display require at least one argument' 1869 1870 if args[0] == "options": 1871 outstr = "Value of current Options:\n" 1872 for key, value in self.options.items(): 1873 outstr += '%25s \t:\t%s\n' %(key,value) 1874 output.write(outstr) 1875 1876 elif args[0] == "variable": 1877 outstr = "Value of Internal Variable:\n" 1878 try: 1879 var = eval(args[1]) 1880 except Exception: 1881 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1882 else: 1883 outstr += 'GLOBAL:\n' 1884 outstr += misc.nice_representation(var, nb_space=4) 1885 1886 try: 1887 var = eval('self.%s' % args[1]) 1888 except Exception: 1889 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1890 else: 1891 outstr += 'LOCAL:\n' 1892 outstr += misc.nice_representation(var, nb_space=4) 1893 split = args[1].split('.') 1894 for i, name in enumerate(split): 1895 try: 1896 __import__('.'.join(split[:i+1])) 1897 exec('%s=sys.modules[\'%s\']' % (split[i], '.'.join(split[:i+1]))) 1898 except ImportError: 1899 try: 1900 var = eval(args[1]) 1901 except Exception, error: 1902 outstr += 'EXTERNAL:\nVariable %s is not a external variable\n' % args[1] 1903 break 1904 else: 1905 outstr += 'EXTERNAL:\n' 1906 outstr += misc.nice_representation(var, nb_space=4) 1907 else: 1908 var = eval(args[1]) 1909 outstr += 'EXTERNAL:\n' 1910 outstr += misc.nice_representation(var, nb_space=4) 1911 1912 pydoc.pager(outstr)
1913 1914
1915 - def do_save(self, line, check=True):
1916 """Save the configuration file""" 1917 1918 args = self.split_arg(line) 1919 # Check argument validity 1920 if check: 1921 Cmd.check_save(self, args) 1922 1923 # find base file for the configuration 1924 if 'HOME' in os.environ and os.environ['HOME'] and \ 1925 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1926 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1927 if hasattr(self, 'me_dir'): 1928 basedir = self.me_dir 1929 elif not MADEVENT: 1930 basedir = MG5DIR 1931 else: 1932 basedir = os.getcwd() 1933 elif MADEVENT: 1934 # launch via ./bin/madevent 1935 for config_file in ['me5_configuration.txt', 'amcatnlo_configuration.txt']: 1936 if os.path.exists(pjoin(self.me_dir, 'Cards', config_file)): 1937 base = pjoin(self.me_dir, 'Cards', config_file) 1938 basedir = self.me_dir 1939 else: 1940 if hasattr(self, 'me_dir'): 1941 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1942 if len(args) == 0 and os.path.exists(base): 1943 self.write_configuration(base, base, self.me_dir) 1944 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1945 basedir = MG5DIR 1946 1947 if len(args) == 0: 1948 args.append(base) 1949 self.write_configuration(args[0], base, basedir, self.options)
1950
1951 - def write_configuration(self, filepath, basefile, basedir, to_keep):
1952 """Write the configuration file""" 1953 # We use the default configuration file as a template. 1954 # to ensure that all configuration information are written we 1955 # keep track of all key that we need to write. 1956 1957 logger.info('save configuration file to %s' % filepath) 1958 to_write = to_keep.keys() 1959 text = "" 1960 has_mg5_path = False 1961 # Use local configuration => Need to update the path 1962 for line in file(basefile): 1963 if '=' in line: 1964 data, value = line.split('=',1) 1965 else: 1966 text += line 1967 continue 1968 data = data.strip() 1969 if data.startswith('#'): 1970 key = data[1:].strip() 1971 else: 1972 key = data 1973 if '#' in value: 1974 value, comment = value.split('#',1) 1975 else: 1976 comment = '' 1977 if key in to_keep: 1978 value = str(to_keep[key]) 1979 else: 1980 text += line 1981 continue 1982 if key == 'mg5_path': 1983 has_mg5_path = True 1984 try: 1985 to_write.remove(key) 1986 except Exception: 1987 pass 1988 if '_path' in key: 1989 # special case need to update path 1990 # check if absolute path 1991 if not os.path.isabs(value): 1992 value = os.path.realpath(os.path.join(basedir, value)) 1993 text += '%s = %s # %s \n' % (key, value, comment) 1994 for key in to_write: 1995 if key in to_keep: 1996 text += '%s = %s \n' % (key, to_keep[key]) 1997 1998 if not MADEVENT and not has_mg5_path: 1999 text += """\n# MG5 MAIN DIRECTORY\n""" 2000 text += "mg5_path = %s\n" % MG5DIR 2001 2002 writer = open(filepath,'w') 2003 writer.write(text) 2004 writer.close()
2005
2006 2007 2008 2009 -class CmdShell(Cmd):
2010 """CMD command with shell activate""" 2011 2012 # Access to shell
2013 - def do_shell(self, line):
2014 "Run a shell command" 2015 2016 if line.strip() is '': 2017 self.help_shell() 2018 else: 2019 logging.info("running shell command: " + line) 2020 subprocess.call(line, shell=True)
2021
2022 - def complete_shell(self, text, line, begidx, endidx):
2023 """ add path for shell """ 2024 2025 # Filename if directory is given 2026 # 2027 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 2028 if not text: 2029 text = '' 2030 output = self.path_completion(text, 2031 base_dir=\ 2032 self.split_arg(line[0:begidx])[-1]) 2033 else: 2034 output = self.path_completion(text) 2035 return output
2036
2037 - def help_shell(self):
2038 """help for the shell""" 2039 logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE') 2040 logger.info("syntax: shell CMD (or ! CMD)",'$MG:BOLD')
2041
2042 2043 2044 -class NotValidInput(Exception): pass
2045 #===============================================================================
2046 # Question with auto-completion 2047 #=============================================================================== 2048 -class SmartQuestion(BasicCmd):
2049 """ a class for answering a question with the path autocompletion""" 2050 2051 allowpath = False
2052 - def preloop(self):
2053 """Initializing before starting the main loop""" 2054 self.prompt = '>' 2055 self.value = None 2056 BasicCmd.preloop(self)
2057 2058 @property
2059 - def answer(self):
2060 return self.value
2061
2062 - def __init__(self, question, allow_arg=[], default=None, 2063 mother_interface=None, *arg, **opt):
2064 2065 self.question = question 2066 self.wrong_answer = 0 # forbids infinite loop 2067 self.allow_arg = [str(a) for a in allow_arg] 2068 self.history_header = '' 2069 self.default_value = str(default) 2070 self.mother_interface = mother_interface 2071 2072 if 'case' in opt: 2073 self.casesensitive = opt['case'] 2074 del opt['case'] 2075 elif 'casesensitive' in opt: 2076 self.casesensitive = opt['casesensitive'] 2077 del opt['casesensitive'] 2078 else: 2079 self.casesensistive = True 2080 super(SmartQuestion, self).__init__(*arg, **opt)
2081
2082 - def __call__(self, question, reprint_opt=True, **opts):
2083 2084 self.question = question 2085 for key,value in opts: 2086 setattr(self, key, value) 2087 if reprint_opt: 2088 print question 2089 logger_tuto.info("Need help here? type 'help'", '$MG:BOLD') 2090 logger_plugin.info("Need help here? type 'help'" , '$MG:BOLD') 2091 return self.cmdloop()
2092 2093
2094 - def completenames(self, text, line, *ignored):
2095 prev_timer = signal.alarm(0) # avoid timer if any 2096 if prev_timer: 2097 nb_back = len(line) 2098 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2099 self.stdout.write(line) 2100 self.stdout.flush() 2101 try: 2102 out = {} 2103 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2104 out[' Recognized command'] = super(SmartQuestion, self).completenames(text,line, *ignored) 2105 2106 return self.deal_multiple_categories(out) 2107 except Exception, error: 2108 print error
2109 2110 completedefault = completenames 2111
2112 - def get_names(self):
2113 # This method used to pull in base class attributes 2114 # at a time dir() didn't do it yet. 2115 return dir(self)
2116
2117 - def onecmd(self, line, **opt):
2118 """catch all error and stop properly command accordingly 2119 Interpret the argument as though it had been typed in response 2120 to the prompt. 2121 2122 The return value is a flag indicating whether interpretation of 2123 commands by the interpreter should stop. 2124 2125 This allow to pass extra argument for internal call. 2126 """ 2127 try: 2128 if '~/' in line and os.environ.has_key('HOME'): 2129 line = line.replace('~/', '%s/' % os.environ['HOME']) 2130 line = os.path.expandvars(line) 2131 cmd, arg, line = self.parseline(line) 2132 if not line: 2133 return self.emptyline() 2134 if cmd is None: 2135 return self.default(line) 2136 self.lastcmd = line 2137 if cmd == '': 2138 return self.default(line) 2139 else: 2140 try: 2141 func = getattr(self, 'do_' + cmd) 2142 except AttributeError: 2143 return self.default(line) 2144 return func(arg, **opt) 2145 except Exception as error: 2146 logger.warning(error) 2147 if __debug__: 2148 raise
2149
2150 - def reask(self, reprint_opt=True):
2151 pat = re.compile('\[(\d*)s to answer\]') 2152 prev_timer = signal.alarm(0) # avoid timer if any 2153 2154 if prev_timer: 2155 if pat.search(self.question): 2156 timeout = int(pat.search(self.question).groups()[0]) 2157 signal.alarm(timeout) 2158 if reprint_opt: 2159 if not prev_timer: 2160 self.question = pat.sub('',self.question) 2161 print self.question.encode('utf8') 2162 2163 if self.mother_interface: 2164 answer = self.mother_interface.check_answer_in_input_file(self, 'EOF', 2165 path=self.allowpath) 2166 if answer: 2167 stop = self.default(answer) 2168 self.postcmd(stop, answer) 2169 return False 2170 2171 return False
2172
2173 - def do_help(self, line):
2174 2175 text=line 2176 out ={} 2177 out['Options'] = Cmd.list_completion(text, self.allow_arg) 2178 out['command'] = BasicCmd.completenames(self, text) 2179 2180 if not text: 2181 if out['Options']: 2182 logger.info( "Here is the list of all valid options:", '$MG:BOLD') 2183 logger.info( " "+ "\n ".join(out['Options'])) 2184 if out['command']: 2185 logger.info( "Here is the list of command available:", '$MG:BOLD') 2186 logger.info( " "+ "\n ".join(out['command'])) 2187 else: 2188 if out['Options']: 2189 logger.info( "Here is the list of all valid options starting with \'%s\'" % text, '$MG:BOLD') 2190 logger.info( " "+ "\n ".join(out['Options'])) 2191 if out['command']: 2192 logger.info( "Here is the list of command available starting with \'%s\':" % text, '$MG:BOLD') 2193 logger.info( " "+ "\n ".join(out['command'])) 2194 elif not out['Options']: 2195 logger.info( "No possibility starting with \'%s\'" % text, '$MG:BOLD') 2196 logger.info( "You can type help XXX, to see all command starting with XXX", '$MG:BOLD')
2197 - def complete_help(self, text, line, begidx, endidx):
2198 """ """ 2199 return self.completenames(text, line)
2200
2201 - def default(self, line):
2202 """Default action if line is not recognized""" 2203 2204 if line.strip() == '' and self.default_value is not None: 2205 self.value = self.default_value 2206 else: 2207 self.value = line
2208
2209 - def emptyline(self):
2210 """If empty line, return default""" 2211 2212 if self.default_value is not None: 2213 self.value = self.default_value
2214 2215
2216 - def postcmd(self, stop, line):
2217 2218 try: 2219 if self.value in self.allow_arg: 2220 return True 2221 elif str(self.value) == 'EOF': 2222 self.value = self.default_value 2223 return True 2224 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2225 return self.reask() 2226 elif self.value in ['repeat', 'reask']: 2227 return self.reask() 2228 elif len(self.allow_arg)==0: 2229 return True 2230 elif ' ' in line.strip() and '=' in self.value: 2231 line,n = re.subn(r'\s*=\s*', '=', line) 2232 if n: 2233 self.default(line) 2234 return self.postcmd(stop, line) 2235 if not self.casesensitive: 2236 for ans in self.allow_arg: 2237 if ans.lower() == self.value.lower(): 2238 self.value = ans 2239 return True 2240 break 2241 else: 2242 raise Exception 2243 2244 2245 else: 2246 raise Exception 2247 except Exception,error: 2248 if self.wrong_answer < 100: 2249 self.wrong_answer += 1 2250 logger.warning("""%s not valid argument. Valid argument are in (%s).""" \ 2251 % (self.value,','.join(self.allow_arg))) 2252 logger.warning('please retry') 2253 return False 2254 else: 2255 self.value = self.default_value 2256 return True
2257
2258 - def cmdloop(self, intro=None):
2259 super(SmartQuestion,self).cmdloop(intro) 2260 return self.answer
2261
2262 # a function helper 2263 -def smart_input(input_text, allow_arg=[], default=None):
2264 print input_text 2265 obj = SmartQuestion(allow_arg=allow_arg, default=default) 2266 return obj.cmdloop()
2267
2268 #=============================================================================== 2269 # Question in order to return a path with auto-completion 2270 #=============================================================================== 2271 -class OneLinePathCompletion(SmartQuestion):
2272 """ a class for answering a question with the path autocompletion""" 2273 2274 completion_prefix='' 2275 allowpath=True 2276
2277 - def completenames(self, text, line, begidx, endidx, formatting=True):
2278 prev_timer = signal.alarm(0) # avoid timer if any 2279 if prev_timer: 2280 nb_back = len(line) 2281 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2282 self.stdout.write(line) 2283 self.stdout.flush() 2284 2285 try: 2286 out = {} 2287 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2288 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 2289 out[' Recognized command'] = BasicCmd.completenames(self, text, line, begidx, endidx) 2290 2291 return self.deal_multiple_categories(out, formatting) 2292 except Exception, error: 2293 print error
2294
2295 - def precmd(self, *args):
2296 """ """ 2297 2298 signal.alarm(0) 2299 return SmartQuestion.precmd(self, *args)
2300
2301 - def completedefault(self,text, line, begidx, endidx):
2302 prev_timer = signal.alarm(0) # avoid timer if any 2303 if prev_timer: 2304 nb_back = len(line) 2305 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2306 self.stdout.write(line) 2307 self.stdout.flush() 2308 try: 2309 args = Cmd.split_arg(line[0:begidx]) 2310 except Exception, error: 2311 print error 2312 2313 # Directory continuation 2314 if args[-1].endswith(os.path.sep): 2315 2316 return Cmd.path_completion(text, 2317 os.path.join('.',*[a for a in args \ 2318 if a.endswith(os.path.sep)]), 2319 begidx, endidx) 2320 return self.completenames(text, line, begidx, endidx)
2321 2322
2323 - def postcmd(self, stop, line):
2324 try: 2325 if self.value in self.allow_arg: 2326 return True 2327 elif self.value and os.path.isfile(self.value): 2328 return os.path.relpath(self.value) 2329 elif self.value and str(self.value) == 'EOF': 2330 self.value = self.default_value 2331 return True 2332 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2333 # go to retry 2334 reprint_opt = True 2335 elif self.value == 'repeat': 2336 reprint_opt = True 2337 else: 2338 raise Exception 2339 except Exception, error: 2340 print """not valid argument. Valid argument are file path or value in (%s).""" \ 2341 % ','.join(self.allow_arg) 2342 print 'please retry' 2343 reprint_opt = False 2344 2345 if line != 'EOF': 2346 return self.reask(reprint_opt)
2347
2348 2349 # a function helper 2350 -def raw_path_input(input_text, allow_arg=[], default=None):
2351 print input_text 2352 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 2353 return obj.cmdloop()
2354
2355 2356 2357 -class ControlSwitch(SmartQuestion):
2358 """A class for asking a question on which program to run. 2359 This is the abstract class 2360 2361 Behavior for each switch can be customize via: 2362 set_default_XXXX() -> set default value 2363 This is super-seeded by self.default_switch if that attribute is defined (and has a key for XXXX) 2364 get_allowed_XXXX() -> return list of possible value 2365 check_value_XXXX(value) -> return True/False if the user can set such value 2366 switch_off_XXXXX() -> set it off (called for special mode) 2367 color_for_XXXX(value) -> return the representation on the screen for value 2368 get_cardcmd_for_XXXX(value)> return the command to run to customize the cards to 2369 match the status 2370 print_options_XXXX() -> return the text to disply below "other options" 2371 default is other possible value (ordered correctly) 2372 2373 consistency_XX_YY(val_XX, val_YY) 2374 -> XX is the new key set by the user to a new value val_XX 2375 -> YY is another key set by the user. 2376 -> return value should be None or "replace_YY" 2377 2378 consistency_XX(val_XX): 2379 check the consistency of the other switch given the new status of this one. 2380 return a dict {key:replaced_value} or {} if nothing to do 2381 2382 user typing "NAME" will result to a call to self.ans_NAME(None) 2383 user typing "NAME=XX" will result to a call to self.ans_NAME('XX') 2384 2385 Note on case sensitivity: 2386 ------------------------- 2387 the XXX is displayed with the case in self.to_control 2388 but ALL functions should use the lower case version. 2389 for key associated to get_allowed_keys(), 2390 if (user) value not in that list. 2391 -> try to find the first entry matching up to the case 2392 for ans_XXX, set the value to lower case, but if case_XXX is set to True 2393 """ 2394 2395 case_sensitive = False 2396 quit_on = ['0','done', 'EOF','','auto'] 2397
2398 - def __init__(self, to_control, motherinstance, *args, **opts):
2399 """to_control is a list of ('KEY': 'Choose the shower/hadronization program') 2400 """ 2401 2402 self.to_control = to_control 2403 self.mother_interface = motherinstance 2404 self.inconsistent_keys = {} #flag parameter which are currently not consistent 2405 # and the value by witch they will be replaced if the 2406 # inconsistency remains. 2407 self.inconsistent_details = {} # flag to list 2408 self.last_changed = [] # keep the order in which the flag have been modified 2409 # to choose the resolution order of conflict 2410 #initialise the main return value 2411 self.switch = {} 2412 for key, _ in to_control: 2413 self.switch[key.lower()] = 'temporary' 2414 2415 self.set_default_switch() 2416 question = self.create_question() 2417 2418 #check all default for auto-completion 2419 allowed_args = [ `i`+';' for i in range(1, 1+len(self.to_control))] 2420 for key in self.switch: 2421 allowed_args += ['%s=%s;' % (key,s) for s in self.get_allowed(key)] 2422 # adding special mode 2423 allowed_args += [key[4:]+';' for key in dir(self) if key.startswith('ans_')] 2424 if 'allow_arg' in opts: 2425 allowed_args += opts['allow_arg'] 2426 del opts['allow_arg'] 2427 2428 allowed_args +=["0", "done"] 2429 SmartQuestion.__init__(self, question, allowed_args, *args, **opts) 2430 self.options = self.mother_interface.options
2431
2432 - def special_check_answer_in_input_file(self, line, default):
2433 """this is called after the standard check if the asnwer were not valid 2434 in particular all input in the auto-completion have been already validated. 2435 (this include all those with ans_xx and the XXXX=YYY for YYY in self.get_allowed(XXXX) 2436 We just check here the XXXX = YYYY for YYYY not in self.get_allowed(XXXX) 2437 but for which self.check_value(XXXX,YYYY) returns True. 2438 We actually allowed XXXX = YYY even if check_value is False to allow case 2439 where some module are missing 2440 """ 2441 2442 if '=' not in line: 2443 if line.strip().startswith('set'): 2444 self.mother_interface.store_line(line) 2445 return str(default) 2446 return None 2447 key, value = line.split('=',1) 2448 if key.lower() in self.switch: 2449 return line 2450 if key in [str(i+1) for i in range(len(self.to_control))]: 2451 self.value='reask' 2452 return line 2453 if hasattr(self, 'ans_%s' % key.lower()): 2454 self.value='reask' 2455 return line 2456 2457 return None
2458 2459 2460 2461 2462
2463 - def set_default_switch(self):
2464 2465 for key,_ in self.to_control: 2466 key = key.lower() 2467 if hasattr(self, 'default_switch') and key in self.default_switch: 2468 self.switch[key] = self.default_switch[key] 2469 continue 2470 if hasattr(self, 'set_default_%s' % key): 2471 getattr(self, 'set_default_%s' % key)() 2472 else: 2473 self.default_switch_for(key)
2474
2475 - def default_switch_for(self, key):
2476 """use this if they are no dedicated function for such key""" 2477 2478 if hasattr(self, 'get_allowed_%s' % key): 2479 return getattr(self, 'get_allowed_%s' % key)()[0] 2480 else: 2481 self.switch[key] = 'OFF'
2482
2483 - def set_all_off(self):
2484 """set all valid parameter to OFF --call before special keyword-- 2485 """ 2486 2487 for key in self.switch: 2488 if hasattr(self, 'switch_off_%s' % key): 2489 getattr(self, 'switch_off_%s' % key)() 2490 elif self.check_value(key, self.switch[key]): 2491 self.switch[key] = 'OFF' 2492 self.inconsistent_details = {} 2493 self.inconsistent_keys = {}
2494 2495
2496 - def check_value(self, key, value):
2497 """return True/False if the value is a correct value to be set by the USER. 2498 other value than those can be set by the system --like-- Not available. 2499 This does not check the full consistency of the switch 2500 """ 2501 2502 if hasattr(self, 'check_value_%s' % key): 2503 return getattr(self, 'check_value_%s' % key)(value) 2504 elif value in self.get_allowed(key): 2505 return True 2506 else: 2507 return False
2508 2509
2510 - def get_cardcmd(self):
2511 """ return the list of command that need to be run to have a consistent 2512 set of cards with the switch value choosen """ 2513 2514 switch = self.answer 2515 cmd= [] 2516 for key in self.switch: 2517 if hasattr(self, 'get_cardcmd_for_%s' % key): 2518 cmd += getattr(self, 'get_cardcmd_for_%s' % key)(switch[key]) 2519 return cmd
2520 2521
2522 - def get_allowed(self, key):
2523 """return the list of possible value for key""" 2524 2525 if hasattr(self, 'get_allowed_%s' % key): 2526 return getattr(self, 'get_allowed_%s' % key)() 2527 else: 2528 return ['ON', 'OFF']
2529
2530 - def default(self, line, raise_error=False):
2531 """Default action if line is not recognized""" 2532 2533 line=line.strip().replace('@', '__at__') 2534 if ';' in line: 2535 for l in line.split(';'): 2536 if l: 2537 out = self.default(l) 2538 return out 2539 2540 if '=' in line: 2541 base, value = line.split('=',1) 2542 base = base.strip() 2543 value = value.strip() 2544 # allow 1=OFF 2545 if base.isdigit() : 2546 try: 2547 base = self.to_control[int(base)-1][0] 2548 except: 2549 pass 2550 elif ' ' in line: 2551 base, value = line.split(' ', 1) 2552 elif hasattr(self, 'ans_%s' % line.lower()): 2553 base, value = line.lower(), None 2554 elif line.isdigit() and line in [`i` for i in range(1, len(self.to_control)+1)]: 2555 # go from one valid option to the next in the get_allowed for that option 2556 base = self.to_control[int(line)-1][0].lower() 2557 return self.default(base) # just recall this function with the associate name 2558 elif line.lower() in self.switch: 2559 # go from one valid option to the next in the get_allowed for that option 2560 base = line.lower() 2561 try: 2562 cur = self.get_allowed(base).index(self.switch[base]) 2563 except: 2564 if self.get_allowed(base): 2565 value = self.get_allowed(base)[0] 2566 else: 2567 logger.warning('Can not switch "%s" to another value via number', base) 2568 self.value='reask' 2569 return 2570 else: 2571 try: 2572 value = self.get_allowed(base)[cur+1] 2573 except IndexError: 2574 value = self.get_allowed(base)[0] 2575 if value == "OFF" and cur == 0: 2576 logger.warning("Invalid action: %s" % self.print_options(base)) 2577 elif cur == 0: 2578 logger.warning("Can not change value for this parameter") 2579 2580 2581 elif line in ['', 'done', 'EOF', 'eof','0']: 2582 super(ControlSwitch, self).default(line) 2583 return self.answer 2584 elif line in 'auto': 2585 self.switch['dynamical'] = True 2586 return super(ControlSwitch, self).default(line) 2587 elif raise_error: 2588 raise NotValidInput('unknow command: %s' % line) 2589 else: 2590 logger.warning('unknow command: %s' % line) 2591 self.value = 'reask' 2592 return 2593 2594 self.value = 'reask' 2595 base = base.lower() 2596 if hasattr(self, 'ans_%s' % base): 2597 if value and not self.is_case_sensitive(base): 2598 value = value.lower() 2599 getattr(self, 'ans_%s' % base)(value) 2600 elif base in self.switch: 2601 self.set_switch(base, value) 2602 elif raise_error: 2603 raise NotValidInput('Not valid command: %s' % line) 2604 else: 2605 logger.warning('Not valid command: %s' % line)
2606
2607 - def is_case_sensitive(self, key):
2608 """check if a key is case sensitive""" 2609 2610 case = self.case_sensitive 2611 if hasattr(self, 'case_%s' % key): 2612 case = getattr(self, 'case_%s' % key) 2613 return case
2614
2615 - def onecmd(self, line, **opt):
2616 """ensure to rewrite the function if a call is done directly""" 2617 out = super(ControlSwitch, self).onecmd(line, **opt) 2618 self.create_question() 2619 return out
2620 2621 @property
2622 - def answer(self):
2623 2624 #avoid key to Not Avail in the output 2625 for key,_ in self.to_control: 2626 if not self.check_value(key, self.switch[key]): 2627 self.switch[key] = 'OFF' 2628 2629 if not self.inconsistent_keys: 2630 return self.switch 2631 else: 2632 out = dict(self.switch) 2633 out.update(self.inconsistent_keys) 2634 return out
2635
2636 - def postcmd(self, stop, line):
2637 2638 # for diamond class arch where both branch defines the postcmd 2639 # set it up to be in coop mode 2640 try: 2641 out = super(ControlSwitch,self).postcmd(stop, line) 2642 except AttributeError: 2643 pass 2644 2645 line = line.strip() 2646 if ';' in line: 2647 line= [l for l in line.split(';') if l][-1] 2648 if line in self.quit_on: 2649 return True 2650 self.create_question() 2651 return self.reask(True)
2652 2653
2654 - def set_switch(self, key, value, user=True):
2655 """change a switch to a given value""" 2656 2657 assert key in self.switch 2658 2659 if hasattr(self, 'ans_%s' % key): 2660 if not self.is_case_sensitive(key): 2661 value = value.lower() 2662 return getattr(self, 'ans_%s' % key)(value) 2663 2664 if not self.is_case_sensitive(key) and value not in self.get_allowed(key): 2665 lower = [t.lower() for t in self.get_allowed(key)] 2666 try: 2667 ind = lower.index(value.lower()) 2668 except ValueError: 2669 pass # keep the current case, in case check_value accepts it anyway. 2670 else: 2671 value = self.get_allowed(key)[ind] 2672 2673 check = self.check_value(key, value) 2674 if not check: 2675 logger.warning('"%s" not valid option for "%s"', value, key) 2676 return 2677 if isinstance(check, str): 2678 value = check 2679 2680 self.switch[key] = value 2681 2682 if user: 2683 self.check_consistency(key, value)
2684
2685 - def remove_inconsistency(self, keys=[]):
2686 2687 if not keys: 2688 self.inconsistent_keys = {} 2689 self.inconsistent_details = {} 2690 elif isinstance(keys, list): 2691 for key in keys: 2692 if key in self.inconsistent_keys: 2693 del self.inconsistent_keys[keys] 2694 del self.inconsistent_details[keys] 2695 else: 2696 if keys in self.inconsistent_keys: 2697 del self.inconsistent_keys[keys] 2698 del self.inconsistent_details[keys]
2699
2700 - def check_consistency(self, key, value):
2701 """check the consistency of the new flag with the old ones""" 2702 2703 2704 if key in self.last_changed: 2705 self.last_changed.remove(key) 2706 self.last_changed.append(key) 2707 2708 # this is used to update self.consistency_keys which contains: 2709 # {key: replacement value with solved conflict} 2710 # it is based on self.consistency_details which is a dict 2711 # key: {'orig_value': 2712 # 'changed_key': 2713 # 'new_changed_key_val': 2714 # 'replacement': 2715 # which keeps track of all conflict and of their origin. 2716 2717 2718 # rules is a dict: {keys:None} if the value for that key is consistent. 2719 # {keys:value_to_replace} if that key is inconsistent 2720 if hasattr(self, 'consistency_%s' % key): 2721 rules = dict([(key2, None) for key2 in self.switch]) 2722 rules.update(getattr(self, 'consistency_%s' % key)(value, self.switch)) 2723 else: 2724 rules = {} 2725 for key2,value2 in self.switch.items(): 2726 if hasattr(self, 'consistency_%s_%s' % (key,key2)): 2727 rules[key2] = getattr(self, 'consistency_%s_%s' % (key,key2))(value, value2) 2728 # check that the suggested value is allowed. 2729 # can happen that it is not if some program are not installed 2730 if rules[key2] is not None and not self.check_value(key2, rules[key2]): 2731 if rules[key2] != 'OFF': 2732 logger.debug('consistency_%s_%s returns invalid output. Assume no conflict') 2733 rules[key2] = None 2734 else: 2735 rules[key2] = None 2736 2737 # 2738 2739 #update the self.inconsisten_details adding new conflict 2740 # start by removing the inconsistency for the newly set parameter 2741 self.remove_inconsistency(key) 2742 # then add the new ones 2743 for key2 in self.switch: 2744 if rules[key2]: 2745 info = {'orig_value': self.switch[key2], 2746 'changed_key': key, 2747 'new_changed_key_val': value, 2748 'replacement': rules[key2]} 2749 if key2 in self.inconsistent_details: 2750 self.inconsistent_details[key2].append(info) 2751 else: 2752 self.inconsistent_details[key2] = [info] 2753 2754 if not self.inconsistent_details: 2755 return 2756 2757 # review the status of all conflict 2758 for key2 in dict(self.inconsistent_details): 2759 for conflict in list(self.inconsistent_details[key2]): 2760 keep_conflict = True 2761 # check that we are still at the current value 2762 if conflict['orig_value'] != self.switch[key2]: 2763 keep_conflict = False 2764 # check if the reason of the conflict still in place 2765 if self.switch[conflict['changed_key']] != conflict['new_changed_key_val']: 2766 keep_conflict = False 2767 if not keep_conflict: 2768 self.inconsistent_details[key2].remove(conflict) 2769 if not self.inconsistent_details[key2]: 2770 del self.inconsistent_details[key2] 2771 2772 2773 # create the valid set of replacement for this current conflict 2774 # start by current status to avoid to keep irrelevant conflict 2775 tmp_switch = dict(self.switch) 2776 2777 # build the order in which we have to check the various conflict reported 2778 to_check = [(c['changed_key'], c['new_changed_key_val']) \ 2779 for k in self.inconsistent_details.values() for c in k 2780 if c['changed_key'] != key] 2781 2782 to_check.sort(lambda x, y: -1 if self.last_changed.index(x[0])>self.last_changed.index(y[0]) else 1) 2783 2784 # validate tmp_switch. 2785 to_check = [(key, value)] + to_check 2786 2787 i = 0 2788 while len(to_check) and i < 50: 2789 #misc.sprint(i, to_check, tmp_switch) 2790 # check in a iterative way the consistency of the tmp_switch parameter 2791 i +=1 2792 key2, value2 = to_check.pop(0) 2793 if hasattr(self, 'consistency_%s' % key2): 2794 rules2 = dict([(key2, None) for key2 in self.switch]) 2795 rules2.update(getattr(self, 'consistency_%s' % key2)(value, tmp_switch)) 2796 else: 2797 rules = {} 2798 for key3,value3 in self.switch.items(): 2799 if hasattr(self, 'consistency_%s_%s' % (key2,key3)): 2800 rules[key3] = getattr(self, 'consistency_%s_%s' % (key2,key3))(value2, value3) 2801 else: 2802 rules[key3] = None 2803 2804 for key, replacement in rules.items(): 2805 if replacement: 2806 tmp_switch[key] = replacement 2807 to_check.append((key, replacement)) 2808 # avoid situation like 2809 # to_check = [('fixed_order', 'ON'), ('fixed_order', 'OFF')] 2810 # always keep the last one 2811 pos = {} 2812 for i, (key,value) in enumerate(to_check): 2813 pos[key] = i 2814 to_check_new = [] 2815 for i, (key,value) in enumerate(to_check): 2816 if pos[key] == i: 2817 to_check_new.append((key,value)) 2818 to_check = to_check_new 2819 if i>=50: 2820 logger.critical('Failed to find a consistent set of switch values.') 2821 2822 # Now tmp_switch is to a fully consistent setup for sure. 2823 # fill self.inconsistent_key 2824 self.inconsistent_keys = {} 2825 for key2, value2 in tmp_switch.items(): 2826 if value2 != self.switch[key2]: 2827 # check that not available module stays on that switch 2828 if value2 == 'OFF' and not self.check_value(key2, 'OFF'): 2829 continue 2830 self.inconsistent_keys[key2] = value2
2831 2832 # 2833 # Helper routine for putting questions with correct color 2834 # 2835 green = '\x1b[32m%s\x1b[0m' 2836 yellow = '\x1b[33m%s\x1b[0m' 2837 red = '\x1b[31m%s\x1b[0m' 2838 bold = '\x1b[01m%s\x1b[0m'
2839 - def color_for_value(self, key, switch_value, consistency=True):
2840 2841 if consistency and key in self.inconsistent_keys: 2842 return self.color_for_value(key, self.inconsistent_keys[key], consistency=False) +\ 2843 u' \u21d0 '+ self.yellow % switch_value 2844 2845 if self.check_value(key, switch_value): 2846 if hasattr(self, 'color_for_%s' % key): 2847 return getattr(self, 'color_for_%s' % key)(switch_value) 2848 if switch_value in ['OFF']: 2849 # inconsistent key are the list of key which are inconsistent with the last change 2850 return self.red % switch_value 2851 else: 2852 return self.green % switch_value 2853 else: 2854 if ' ' in switch_value: 2855 return self.bold % switch_value 2856 else: 2857 return self.red % switch_value
2858
2859 - def print_options(self,key, keep_default=False):
2860 2861 if hasattr(self, 'print_options_%s' % key) and not keep_default: 2862 return getattr(self, 'print_options_%s' % key)() 2863 2864 #re-order the options in order to have those in cycling order 2865 try: 2866 ind = self.get_allowed(key).index(self.switch[key]) 2867 except Exception, err: 2868 options = self.get_allowed(key) 2869 else: 2870 options = self.get_allowed(key)[ind:]+ self.get_allowed(key)[:ind] 2871 2872 info = '|'.join([v for v in options if v != self.switch[key]]) 2873 if info == '': 2874 info = 'Please install module' 2875 return info
2876
2877 - def do_help(self, line, list_command=False):
2878 """dedicated help for the control switch""" 2879 2880 if line: 2881 return self.print_help_for_switch(line) 2882 2883 # here for simple "help" 2884 logger.info(" ") 2885 logger.info(" In order to change a switch you can:") 2886 logger.info(" - type 'NAME = VALUE' to set the switch NAME to a given value.") 2887 logger.info(" - type 'ID = VALUE' to set the switch correspond to the line ID to a given value.") 2888 logger.info(" - type 'ID' where ID is the value of the line to pass from one value to the next.") 2889 logger.info(" - type 'NAME' to set the switch NAME to the next value.") 2890 logger.info("") 2891 logger.info(" You can type 'help NAME' for more help on a given switch") 2892 logger.info("") 2893 logger.info(" Special keyword:", '$MG:BOLD') 2894 logger.info(" %s" % '\t'.join([p[4:] for p in dir(self) if p.startswith('ans_')]) ) 2895 logger.info(" type 'help XXX' for more information") 2896 if list_command: 2897 super(ControlSwitch, self).do_help(line)
2898 2899
2900 - def print_help_for_switch(self, line):
2901 """ """ 2902 2903 arg = line.split()[0] 2904 2905 if hasattr(self, 'help_%s' % arg): 2906 return getattr(self, 'help_%s' % arg)('') 2907 2908 if hasattr(self, 'ans_%s' % arg): 2909 return getattr(self, 'help_%s' % arg).__doc__ 2910 2911 if arg in self.switch: 2912 logger.info(" information for switch %s: ", arg, '$MG:BOLD') 2913 logger.info(" allowed value:") 2914 logger.info(" %s", '\t'.join(self.get_allowed(arg))) 2915 if hasattr(self, 'help_text_%s' % arg): 2916 logger.info("") 2917 for line in getattr(self, 'help_text_%s' % arg): 2918 logger.info(line)
2919 2920 2921 2922
2923 - def question_formatting(self, nb_col = 80, 2924 ldescription=0, 2925 lswitch=0, 2926 lname=0, 2927 ladd_info=0, 2928 lpotential_switch=0, 2929 lnb_key=0, 2930 key=None):
2931 """should return four lines: 2932 1. The upper band (typically /========\ 2933 2. The lower band (typically \========/ 2934 3. The line without conflict | %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s | 2935 4. The line with conflict | %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s | 2936 # Be carefull to include the size of the color flag for the switch 2937 green/red/yellow are adding 9 in length 2938 2939 line should be like '| %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s |' 2940 2941 the total lenght of the line (for defining the upper/lower line) 2942 available key : nb 2943 descrip 2944 name 2945 switch # formatted with color + conflict handling 2946 conflict_switch # color formatted value from self.inconsistent_keys 2947 switch_nc # self.switch without color formatting 2948 conflict_switch_nc # self.inconsistent_keys without color formatting 2949 add_info 2950 """ 2951 2952 if key: 2953 # key is only provided for conflict 2954 len_switch = len(self.switch[key]) 2955 if key in self.inconsistent_keys: 2956 len_cswitch = len(self.inconsistent_keys[key]) 2957 else: 2958 len_cswitch = 0 2959 else: 2960 len_switch = 0 2961 len_cswitch = 0 2962 2963 list_length = [] 2964 # | 1. KEY = VALUE | 2965 list_length.append(lnb_key + lname + lswitch + 9) 2966 #1. DESCRIP KEY = VALUE 2967 list_length.append(lnb_key + ldescription+ lname + lswitch + 6) 2968 #1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT 2969 list_length.append(list_length[-1] - lswitch + max(lswitch,lpotential_switch)) 2970 #| 1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT | 2971 list_length.append(list_length[-1] +4) 2972 # 1. DESCRIP KEY = VALUE_MAXSIZE 2973 list_length.append(lnb_key + ldescription+ lname + max((2*lpotential_switch+3),lswitch) + 6) 2974 #| 1. DESCRIP KEY = VALUE_MAXSIZE | 2975 list_length.append(list_length[-1] +4) 2976 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 2977 list_length.append(list_length[-1] +3+ max(15,6+ladd_info)) 2978 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 2979 list_length.append(list_length[-2] +13+ max(15,10+ladd_info)) 2980 2981 selected = [0] + [i+1 for i,s in enumerate(list_length) if s < nb_col] 2982 selected = selected[-1] 2983 2984 # upper and lower band 2985 if selected !=0: 2986 size = list_length[selected-1] 2987 else: 2988 size = nb_col 2989 2990 2991 # default for upper/lower: 2992 upper = "/%s\\" % ("=" * (size-2)) 2993 lower = "\\%s/" % ("=" * (size-2)) 2994 2995 if selected==0: 2996 f1= '%(nb){0}d \x1b[1m%(name){1}s\x1b[0m=%(switch)-{2}s'.format(lnb_key, 2997 lname,lswitch) 2998 f2= f1 2999 # | 1. KEY = VALUE | 3000 elif selected == 1: 3001 upper = "/%s\\" % ("=" * (nb_col-2)) 3002 lower = "\\%s/" % ("=" * (nb_col-2)) 3003 to_add = nb_col -size 3004 f1 = '| %(nb){0}d. \x1b[1m%(name){1}s\x1b[0m = %(switch)-{2}s |'.format(lnb_key, 3005 lname,lswitch+9+to_add) 3006 3007 f = u'| %(nb){0}d. \x1b[1m%(name){1}s\x1b[0m = %(conflict_switch)-{2}s \u21d0 %(strike_switch)-{3}s |' 3008 f2 =f.format(lnb_key, lname, len_cswitch+9, lswitch-len_cswitch+len_switch+to_add-1) 3009 #1. DESCRIP KEY = VALUE 3010 elif selected == 2: 3011 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 3012 f1 = f.format(lnb_key, ldescription,lname,lswitch) 3013 f2 = f1 3014 #1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT 3015 elif selected == 3: 3016 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 3017 f1 = f.format(lnb_key, ldescription,lname,max(lpotential_switch, lswitch)) 3018 l_conflict_line = size-lpotential_switch+len_switch+len_cswitch+3+1 3019 if l_conflict_line <= nb_col: 3020 f = u'%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s\u21d0 %(strike_switch)-{4}s' 3021 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3022 elif l_conflict_line -4 <= nb_col: 3023 f = u'%(nb){0}d.%(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m=%(conflict_switch)-{3}s\u21d0 %(strike_switch)-{4}s' 3024 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3025 else: 3026 ldescription -= (l_conflict_line - nb_col) 3027 f = u'%(nb){0}d. %(descrip)-{1}.{1}s. \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s\u21d0 %(strike_switch)-{4}s' 3028 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3029 #| 1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT | 3030 elif selected == 4: 3031 upper = "/%s\\" % ("=" * (nb_col-2)) 3032 lower = "\\%s/" % ("=" * (nb_col-2)) 3033 to_add = nb_col -size 3034 f='| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s |' 3035 f1 = f.format(lnb_key,ldescription,lname,max(lpotential_switch, lswitch)+9+to_add) 3036 l_conflict_line = size-lpotential_switch+len_switch+len_cswitch+3+1 3037 if l_conflict_line <= nb_col: 3038 f=u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s|' 3039 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3040 elif l_conflict_line -1 <= nb_col: 3041 f=u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3042 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3043 elif l_conflict_line -3 <= nb_col: 3044 f=u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m=%(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3045 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3046 3047 else: 3048 ldescription -= (l_conflict_line - nb_col) 3049 f=u'| %(nb){0}d. %(descrip)-{1}.{1}s. \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3050 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3051 3052 # 1. DESCRIP KEY = VALUE_MAXSIZE 3053 elif selected == 5: 3054 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 3055 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)) 3056 f = u'%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3057 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9, max(2*lpotential_switch+3, lswitch)-lpotential_switch+len_switch) 3058 #| 1. DESCRIP KEY = VALUE_MAXSIZE | 3059 elif selected == 6: 3060 f= '| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s |' 3061 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)+9) 3062 f= u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s|' 3063 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9,max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch) 3064 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 3065 elif selected == 7: 3066 ladd_info = max(15,6+ladd_info) 3067 upper = "/{0:=^%s}|{1:=^%s}|{2:=^%s}\\" % (lnb_key+ldescription+4, 3068 lname+max(2*lpotential_switch+3, lswitch)+5, 3069 ladd_info) 3070 upper = upper.format(' Description ', ' values ', ' other options ') 3071 3072 f='| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s | %(add_info)-{4}s |' 3073 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)+9, ladd_info-4) 3074 f= u'| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s| %(add_info)-{5}s |' 3075 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9, 3076 max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch, ladd_info-4) 3077 elif selected == 8: 3078 ladd_info = max(15,10+ladd_info) 3079 upper = "/{0:=^%s}|{1:=^%s}|{2:=^%s}\\" % (lnb_key+ldescription+4+5, 3080 lname+max(3+2*lpotential_switch,lswitch)+10, 3081 ladd_info) 3082 upper = upper.format(' Description ', ' values ', ' other options ') 3083 lower = "\\%s/" % ("=" * (size-2)) 3084 3085 f='| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s | %(add_info)-{4}s|' 3086 f1 = f.format(lnb_key,ldescription+5,5+lname,max(2*lpotential_switch+3,lswitch)+9, ladd_info-5) 3087 f=u'| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s| %(add_info)-{5}s|' 3088 f2 = f.format(lnb_key,ldescription+5,5+lname, 3089 lpotential_switch+9, 3090 max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch, ladd_info-5) 3091 3092 3093 return upper, lower, f1, f2
3094
3095 - def create_question(self, help_text=True):
3096 """ create the question with correct formatting""" 3097 3098 # geth the number of line and column of the shell to adapt the printing 3099 # accordingly 3100 try: 3101 nb_rows, nb_col = os.popen('stty size', 'r').read().split() 3102 nb_rows, nb_col = int(nb_rows), int(nb_col) 3103 except Exception,error: 3104 nb_rows, nb_col = 20, 80 3105 3106 #compute information on the length of element to display 3107 max_len_description = 0 3108 max_len_switch = 0 3109 max_len_name = 0 3110 max_len_add_info = 0 3111 max_len_potential_switch = 0 3112 max_nb_key = 1 + int(math.log10(len(self.to_control))) 3113 3114 for key, descrip in self.to_control: 3115 if len(descrip) > max_len_description: max_len_description = len(descrip) 3116 if len(key) > max_len_name: max_len_name = len(key) 3117 if key in self.inconsistent_keys: 3118 to_display = '%s < %s' % (self.switch[key], self.inconsistent_keys[key]) 3119 else: 3120 to_display = self.switch[key] 3121 if len(to_display) > max_len_switch: max_len_switch=len(to_display) 3122 3123 info = self.print_options(key) 3124 if len(info)> max_len_add_info: max_len_add_info = len(info) 3125 3126 if self.get_allowed(key): 3127 max_k = max(len(k) for k in self.get_allowed(key)) 3128 else: 3129 max_k = 0 3130 if max_k > max_len_potential_switch: max_len_potential_switch = max_k 3131 3132 upper_line, lower_line, f1, f2 = self.question_formatting(nb_col, max_len_description, max_len_switch, 3133 max_len_name, max_len_add_info, 3134 max_len_potential_switch, max_nb_key) 3135 3136 text = \ 3137 ["The following switches determine which programs are run:", 3138 upper_line 3139 ] 3140 3141 3142 3143 for i,(key, descrip) in enumerate(self.to_control): 3144 3145 3146 3147 data_to_format = {'nb': i+1, 3148 'descrip': descrip, 3149 'name': key, 3150 'switch': self.color_for_value(key,self.switch[key]), 3151 'add_info': self.print_options(key), 3152 'switch_nc': self.switch[key], 3153 'strike_switch': u'\u0336'.join(' %s ' %self.switch[key].upper()) + u'\u0336', 3154 } 3155 if key in self.inconsistent_keys: 3156 # redefine the formatting here, due to the need to know the conflict size 3157 _,_,_, f2 = self.question_formatting(nb_col, max_len_description, max_len_switch, 3158 max_len_name, max_len_add_info, 3159 max_len_potential_switch, max_nb_key, 3160 key=key) 3161 3162 data_to_format['conflict_switch_nc'] = self.inconsistent_keys[key] 3163 data_to_format['conflict_switch'] = self.color_for_value(key,self.inconsistent_keys[key], consistency=False) 3164 text.append(f2 % data_to_format) 3165 else: 3166 text.append(f1 % data_to_format) 3167 3168 3169 text.append(lower_line) 3170 3171 # find a good example of switch to set for the lower part of the description 3172 example = None 3173 for key in self.switch: 3174 if len(self.get_allowed(key)) > 1: 3175 for val in self.get_allowed(key): 3176 if val != self.switch[key]: 3177 example = (key, val) 3178 break 3179 else: 3180 continue 3181 break 3182 3183 if not example: 3184 example = ('KEY', 'VALUE') 3185 3186 if help_text: 3187 text += \ 3188 ["Either type the switch number (1 to %s) to change its setting," % len(self.to_control), 3189 "Set any switch explicitly (e.g. type '%s=%s' at the prompt)" % example, 3190 "Type 'help' for the list of all valid option", 3191 "Type '0', 'auto', 'done' or just press enter when you are done."] 3192 3193 # check on the number of row: 3194 if len(text) > nb_rows: 3195 # too many lines. Remove some 3196 to_remove = [ -2, #Type 'help' for the list of all valid option 3197 -5, # \====/ 3198 -4, #Either type the switch number (1 to %s) to change its setting, 3199 -3, # Set any switch explicitly 3200 -1, # Type '0', 'auto', 'done' or just press enter when you are done. 3201 ] 3202 to_remove = to_remove[:min(len(to_remove), len(text)-nb_rows)] 3203 text = [t for i,t in enumerate(text) if i-len(text) not in to_remove] 3204 3205 self.question = "\n".join(text) 3206 return self.question
3207
3208 3209 #=============================================================================== 3210 # 3211 #=============================================================================== 3212 -class CmdFile(file):
3213 """ a class for command input file -in order to debug cmd \n problem""" 3214
3215 - def __init__(self, name, opt='rU'):
3216 3217 file.__init__(self, name, opt) 3218 self.text = file.read(self) 3219 self.close() 3220 self.lines = self.text.split('\n')
3221
3222 - def readline(self, *arg, **opt):
3223 """readline method treating correctly a line whithout \n at the end 3224 (add it) 3225 """ 3226 if self.lines: 3227 line = self.lines.pop(0) 3228 else: 3229 return '' 3230 3231 if line.endswith('\n'): 3232 return line 3233 else: 3234 return line + '\n'
3235
3236 - def __next__(self):
3237 return self.lines.__next__()
3238
3239 - def __iter__(self):
3240 return self.lines.__iter__()
3241