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 1325 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1326 self.options['crash_on_error']: 1327 logger.info('stop computation due to crash_on_error=True') 1328 sys.exit(str(error)) 1329 1330 #stop the execution if on a non interactive mode 1331 if self.use_rawinput == False or self.inputfile: 1332 return True 1333 elif self.mother: 1334 if self.mother.use_rawinput is False: 1335 return True 1336 1337 elif self.mother.mother: 1338 if self.mother.mother.use_rawinput is False: 1339 return True 1340 1341 return False
1342 1343 1344
1345 - def nice_user_error(self, error, line):
1346 if self.child: 1347 return self.child.nice_user_error(error, line) 1348 # Make sure that we are at the initial position 1349 os.chdir(self.__initpos) 1350 if not self.history or line == self.history[-1]: 1351 error_text = 'Command \"%s\" interrupted with error:\n' % line 1352 else: 1353 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 1354 error_text += '\"%s\" with error:\n' % self.history[-1] 1355 error_text += '%s : %s' % (error.__class__.__name__, 1356 str(error).replace('\n','\n\t')) 1357 logger_stderr.error(error_text) 1358 1359 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1360 self.options['crash_on_error']: 1361 logger.info('stop computation due to crash_on_error=True') 1362 sys.exit(str(error)) 1363 1364 #stop the execution if on a non interactive mode 1365 if self.use_rawinput == False or self.inputfile: 1366 return True 1367 elif self.mother: 1368 if self.mother.use_rawinput is False: 1369 return True 1370 elif self.mother.mother: 1371 if self.mother.mother.use_rawinput is False: 1372 return True 1373 1374 # Remove failed command from history 1375 self.history.pop() 1376 return False
1377
1378 - def nice_config_error(self, error, line):
1379 if self.child: 1380 return self.child.nice_user_error(error, line) 1381 # Make sure that we are at the initial position 1382 os.chdir(self.__initpos) 1383 if not self.history or line == self.history[-1]: 1384 error_text = 'Error detected in \"%s\"\n' % line 1385 else: 1386 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 1387 error_text += 'write debug file %s \n' % self.debug_output 1388 self.log = False 1389 super(Cmd,self).onecmd('history %s' % self.debug_output) 1390 debug_file = open(self.debug_output, 'a') 1391 traceback.print_exc(file=debug_file) 1392 error_text += self.config_debug % {'debug' :self.debug_output} 1393 error_text += '%s : %s' % (error.__class__.__name__, 1394 str(error).replace('\n','\n\t')) 1395 logger_stderr.error(error_text) 1396 1397 # Add options status to the debug file 1398 try: 1399 self.do_display('options', debug_file) 1400 except Exception, error: 1401 debug_file.write('Fail to write options with error %s' % error) 1402 if hasattr(self, 'options') and 'crash_on_error' in self.options and \ 1403 self.options['crash_on_error']: 1404 logger.info('stop computation due to crash_on_error=True') 1405 sys.exit(str(error)) 1406 1407 #stop the execution if on a non interactive mode 1408 if self.use_rawinput == False or self.inputfile: 1409 return True 1410 elif self.mother: 1411 if self.mother.use_rawinput is False: 1412 return True 1413 elif self.mother.mother: 1414 if self.mother.mother.use_rawinput is False: 1415 return True 1416 1417 # Remove failed command from history 1418 if self.history: 1419 self.history.pop() 1420 return False
1421
1422 - def onecmd_orig(self, line, **opt):
1423 """Interpret the argument as though it had been typed in response 1424 to the prompt. 1425 1426 The return value is a flag indicating whether interpretation of 1427 commands by the interpreter should stop. 1428 1429 This allow to pass extra argument for internal call. 1430 """ 1431 if '~/' in line and os.environ.has_key('HOME'): 1432 line = line.replace('~/', '%s/' % os.environ['HOME']) 1433 if '#' in line: 1434 line = line.split('#')[0] 1435 1436 line = os.path.expandvars(line) 1437 cmd, arg, line = self.parseline(line) 1438 if not line: 1439 return self.emptyline() 1440 if cmd is None: 1441 return self.default(line) 1442 self.lastcmd = line 1443 if cmd == '': 1444 return self.default(line) 1445 else: 1446 try: 1447 func = getattr(self, 'do_' + cmd) 1448 except AttributeError: 1449 return self.default(line) 1450 return func(arg, **opt)
1451
1452 - def error_handling(self, error, line):
1453 1454 me_dir = '' 1455 if hasattr(self, 'me_dir'): 1456 me_dir = os.path.basename(me_dir) + ' ' 1457 1458 misc.EasterEgg('error') 1459 stop=False 1460 try: 1461 raise 1462 except self.InvalidCmd as error: 1463 if __debug__: 1464 stop = self.nice_error_handling(error, line) 1465 self.history.pop() 1466 else: 1467 stop = self.nice_user_error(error, line) 1468 1469 if self.allow_notification_center: 1470 misc.apple_notify('Run %sfailed' % me_dir, 1471 'Invalid Command: %s' % error.__class__.__name__) 1472 1473 except self.ConfigurationError as error: 1474 stop = self.nice_config_error(error, line) 1475 if self.allow_notification_center: 1476 misc.apple_notify('Run %sfailed' % me_dir, 1477 'Configuration error') 1478 except Exception as error: 1479 stop = self.nice_error_handling(error, line) 1480 if self.mother: 1481 self.do_quit('') 1482 if self.allow_notification_center: 1483 misc.apple_notify('Run %sfailed' % me_dir, 1484 'Exception: %s' % error.__class__.__name__) 1485 except KeyboardInterrupt as error: 1486 self.stop_on_keyboard_stop() 1487 if __debug__: 1488 self.nice_config_error(error, line) 1489 logger.error(self.keyboard_stop_msg) 1490 1491 1492 if stop: 1493 self.do_quit('all')
1494 1495 1496
1497 - def onecmd(self, line, **opt):
1498 """catch all error and stop properly command accordingly""" 1499 1500 try: 1501 return self.onecmd_orig(line, **opt) 1502 except BaseException, error: 1503 self.error_handling(error, line)
1504 1505
1506 - def stop_on_keyboard_stop(self):
1507 """action to perform to close nicely on a keyboard interupt""" 1508 pass # dummy function
1509
1510 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 1511 precmd=False, postcmd=True, 1512 child=True, **opt):
1513 """for third party call, call the line with pre and postfix treatment 1514 without global error handling """ 1515 1516 1517 if printcmd and not line.startswith('#'): 1518 logger.info(line) 1519 if self.child and child: 1520 current_interface = self.child 1521 else: 1522 current_interface = self 1523 if precmd: 1524 line = current_interface.precmd(line) 1525 if errorhandling: 1526 stop = current_interface.onecmd(line, **opt) 1527 else: 1528 stop = Cmd.onecmd_orig(current_interface, line, **opt) 1529 if postcmd: 1530 stop = current_interface.postcmd(stop, line) 1531 return stop
1532
1533 - def run_cmd(self, line):
1534 """for third party call, call the line with pre and postfix treatment 1535 with global error handling""" 1536 1537 return self.exec_cmd(line, errorhandling=True, precmd=True)
1538
1539 - def emptyline(self):
1540 """If empty line, do nothing. Default is repeat previous command.""" 1541 pass
1542
1543 - def default(self, line, log=True):
1544 """Default action if line is not recognized""" 1545 1546 # Faulty command 1547 if log: 1548 logger.warning("Command \"%s\" not recognized, please try again" % \ 1549 line.split()[0]) 1550 if line.strip() in ['q', '.q', 'stop']: 1551 logger.info("If you want to quit mg5 please type \"exit\".") 1552 1553 if self.history and self.history[-1] == line: 1554 self.history.pop()
1555 1556 # Write the list of command line use in this session
1557 - def do_history(self, line):
1558 """write in a file the suite of command that was used""" 1559 1560 args = self.split_arg(line) 1561 # Check arguments validity 1562 self.check_history(args) 1563 1564 if len(args) == 0: 1565 logger.info('\n'.join(self.history)) 1566 return 1567 elif args[0] == 'clean': 1568 self.history = [] 1569 logger.info('History is cleaned') 1570 return 1571 elif args[0] == '.': 1572 output_file = os.path.join(self._export_dir, 'Cards', \ 1573 'proc_card_mg5.dat') 1574 output_file = open(output_file, 'w') 1575 else: 1576 output_file = open(args[0], 'w') 1577 1578 # Create the command file 1579 text = self.get_history_header() 1580 text += ('\n'.join(self.history) + '\n') 1581 1582 #write this information in a file 1583 output_file.write(text) 1584 output_file.close() 1585 1586 if self.log: 1587 logger.info("History written to " + output_file.name)
1588
1589 - def compile(self, *args, **opts):
1590 """ """ 1591 1592 return misc.compile(nb_core=self.options['nb_core'], *args, **opts)
1593
1594 - def avoid_history_duplicate(self, line, no_break=[]):
1595 """remove all line in history (but the last) starting with line. 1596 up to the point when a line didn't start by something in no_break. 1597 (reading in reverse order)""" 1598 1599 new_history = [] 1600 for i in range(1, len(self.history)+1): 1601 cur_line = self.history[-i] 1602 if i == 1: 1603 new_history.append(cur_line) 1604 elif not any((cur_line.startswith(text) for text in no_break)): 1605 to_add = self.history[:-i+1] 1606 to_add.reverse() 1607 new_history += to_add 1608 break 1609 elif cur_line.startswith(line): 1610 continue 1611 else: 1612 new_history.append(cur_line) 1613 1614 new_history.reverse() 1615 self.history[:] = new_history
1616 1617
1618 - def import_command_file(self, filepath):
1619 # remove this call from history 1620 if self.history: 1621 self.history.pop() 1622 1623 #avoid that command of other file interfere with this one. 1624 previous_store_line = self.get_stored_line() 1625 1626 # Read the lines of the file and execute them 1627 if isinstance(filepath, str): 1628 commandline = open(filepath).readlines() 1629 else: 1630 commandline = filepath 1631 oldinputfile = self.inputfile 1632 oldraw = self.use_rawinput 1633 self.inputfile = (l for l in commandline) # make a generator 1634 self.use_rawinput = False 1635 # Note using "for line in open(filepath)" is not safe since the file 1636 # filepath can be overwritten during the run (leading to weird results) 1637 # Note also that we need a generator and not a list. 1638 for line in self.inputfile: 1639 #remove pointless spaces and \n 1640 line = line.replace('\n', '').strip() 1641 # execute the line 1642 if line: 1643 self.exec_cmd(line, precmd=True) 1644 stored = self.get_stored_line() 1645 while stored: 1646 line = stored 1647 self.exec_cmd(line, precmd=True) 1648 stored = self.get_stored_line() 1649 1650 # If a child was open close it 1651 if self.child: 1652 self.child.exec_cmd('quit') 1653 self.inputfile = oldinputfile 1654 self.use_rawinput = oldraw 1655 1656 # restore original store line 1657 cmd = self 1658 while hasattr(cmd, 'mother') and cmd.mother: 1659 cmd = cmd.mother 1660 cmd.stored_line = previous_store_line 1661 return
1662
1663 - def get_history_header(self):
1664 """Default history header""" 1665 1666 return self.history_header
1667
1668 - def postloop(self):
1669 """ """ 1670 1671 if self.use_rawinput and self.completekey: 1672 try: 1673 import readline 1674 readline.set_completer(self.old_completer) 1675 del self.old_completer 1676 except ImportError: 1677 pass 1678 except AttributeError: 1679 pass 1680 1681 args = self.split_arg(self.lastcmd) 1682 if args and args[0] in ['quit','exit']: 1683 if 'all' in args: 1684 return True 1685 if len(args) >1 and args[1].isdigit(): 1686 if args[1] not in ['0', '1']: 1687 return True 1688 1689 return False
1690 1691 #=============================================================================== 1692 # Ask a question with a maximum amount of time to answer 1693 #=============================================================================== 1694 @staticmethod
1695 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 1696 fct_timeout=None):
1697 """ a question with a maximal time to answer take default otherwise""" 1698 1699 def handle_alarm(signum, frame): 1700 raise TimeOutError
1701 1702 signal.signal(signal.SIGALRM, handle_alarm) 1703 1704 if fct is None: 1705 fct = raw_input 1706 1707 if timeout: 1708 signal.alarm(timeout) 1709 question += '[%ss to answer] ' % (timeout) 1710 try: 1711 result = fct(question) 1712 except TimeOutError: 1713 if noerror: 1714 logger.info('\nuse %s' % default) 1715 if fct_timeout: 1716 fct_timeout(True) 1717 return default 1718 else: 1719 signal.alarm(0) 1720 raise 1721 finally: 1722 signal.alarm(0) 1723 if fct_timeout: 1724 fct_timeout(False) 1725 return result
1726 1727 1728 1729 1730 1731 1732 # Quit
1733 - def do_quit(self, line):
1734 """Not in help: exit the mainloop() """ 1735 1736 if self.child: 1737 self.child.exec_cmd('quit ' + line, printcmd=False) 1738 return 1739 elif self.mother: 1740 self.mother.child = None 1741 if line == 'all': 1742 self.mother.do_quit('all') 1743 pass 1744 elif line: 1745 level = int(line) - 1 1746 if level: 1747 self.mother.lastcmd = 'quit %s' % level 1748 elif self.inputfile: 1749 for line in self.inputfile: 1750 logger.warning('command not executed: %s' % line.replace('\n','')) 1751 1752 return True
1753 1754 # Aliases 1755 do_EOF = do_quit 1756 do_exit = do_quit 1757
1758 - def do_help(self, line):
1759 """Not in help: propose some usefull possible action """ 1760 1761 # if they are an argument use the default help 1762 if line: 1763 return super(Cmd, self).do_help(line) 1764 1765 1766 names = self.get_names() 1767 cmds = {} 1768 names.sort() 1769 # There can be duplicates if routines overridden 1770 prevname = '' 1771 for name in names: 1772 if name[:3] == 'do_': 1773 if name == prevname: 1774 continue 1775 prevname = name 1776 cmdname=name[3:] 1777 try: 1778 doc = getattr(self.cmd, name).__doc__ 1779 except Exception: 1780 doc = None 1781 if not doc: 1782 doc = getattr(self, name).__doc__ 1783 if not doc: 1784 tag = "Documented commands" 1785 elif ':' in doc: 1786 tag = doc.split(':',1)[0] 1787 else: 1788 tag = "Documented commands" 1789 if tag in cmds: 1790 cmds[tag].append(cmdname) 1791 else: 1792 cmds[tag] = [cmdname] 1793 1794 self.stdout.write("%s\n"%str(self.doc_leader)) 1795 for tag in self.helporder: 1796 if tag not in cmds: 1797 continue 1798 header = "%s (type help <topic>):" % tag 1799 self.print_topics(header, cmds[tag], 15,80) 1800 for name, item in cmds.items(): 1801 if name in self.helporder: 1802 continue 1803 if name == "Not in help": 1804 continue 1805 header = "%s (type help <topic>):" % name 1806 self.print_topics(header, item, 15,80) 1807 1808 1809 ## Add contextual help 1810 if len(self.history) == 0: 1811 last_action_2 = last_action = 'start' 1812 else: 1813 last_action_2 = last_action = 'none' 1814 1815 pos = 0 1816 authorize = self.next_possibility.keys() 1817 while last_action_2 not in authorize and last_action not in authorize: 1818 pos += 1 1819 if pos > len(self.history): 1820 last_action_2 = last_action = 'start' 1821 break 1822 1823 args = self.history[-1 * pos].split() 1824 last_action = args[0] 1825 if len(args)>1: 1826 last_action_2 = '%s %s' % (last_action, args[1]) 1827 else: 1828 last_action_2 = 'none' 1829 1830 logger.info('Contextual Help') 1831 logger.info('===============') 1832 if last_action_2 in authorize: 1833 options = self.next_possibility[last_action_2] 1834 elif last_action in authorize: 1835 options = self.next_possibility[last_action] 1836 else: 1837 return 1838 text = 'The following command(s) may be useful in order to continue.\n' 1839 for option in options: 1840 text+='\t %s \n' % option 1841 logger.info(text)
1842
1843 - def do_display(self, line, output=sys.stdout):
1844 """Advanced commands: basic display""" 1845 1846 args = self.split_arg(line) 1847 #check the validity of the arguments 1848 1849 if len(args) == 0: 1850 self.help_display() 1851 raise self.InvalidCmd, 'display require at least one argument' 1852 1853 if args[0] == "options": 1854 outstr = "Value of current Options:\n" 1855 for key, value in self.options.items(): 1856 outstr += '%25s \t:\t%s\n' %(key,value) 1857 output.write(outstr) 1858 1859 elif args[0] == "variable": 1860 outstr = "Value of Internal Variable:\n" 1861 try: 1862 var = eval(args[1]) 1863 except Exception: 1864 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1865 else: 1866 outstr += 'GLOBAL:\n' 1867 outstr += misc.nice_representation(var, nb_space=4) 1868 1869 try: 1870 var = eval('self.%s' % args[1]) 1871 except Exception: 1872 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1873 else: 1874 outstr += 'LOCAL:\n' 1875 outstr += misc.nice_representation(var, nb_space=4) 1876 split = args[1].split('.') 1877 for i, name in enumerate(split): 1878 try: 1879 __import__('.'.join(split[:i+1])) 1880 exec('%s=sys.modules[\'%s\']' % (split[i], '.'.join(split[:i+1]))) 1881 except ImportError: 1882 try: 1883 var = eval(args[1]) 1884 except Exception, error: 1885 outstr += 'EXTERNAL:\nVariable %s is not a external variable\n' % args[1] 1886 break 1887 else: 1888 outstr += 'EXTERNAL:\n' 1889 outstr += misc.nice_representation(var, nb_space=4) 1890 else: 1891 var = eval(args[1]) 1892 outstr += 'EXTERNAL:\n' 1893 outstr += misc.nice_representation(var, nb_space=4) 1894 1895 pydoc.pager(outstr)
1896 1897
1898 - def do_save(self, line, check=True):
1899 """Save the configuration file""" 1900 1901 args = self.split_arg(line) 1902 # Check argument validity 1903 if check: 1904 Cmd.check_save(self, args) 1905 1906 # find base file for the configuration 1907 if 'HOME' in os.environ and os.environ['HOME'] and \ 1908 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1909 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1910 if hasattr(self, 'me_dir'): 1911 basedir = self.me_dir 1912 elif not MADEVENT: 1913 basedir = MG5DIR 1914 else: 1915 basedir = os.getcwd() 1916 elif MADEVENT: 1917 # launch via ./bin/madevent 1918 for config_file in ['me5_configuration.txt', 'amcatnlo_configuration.txt']: 1919 if os.path.exists(pjoin(self.me_dir, 'Cards', config_file)): 1920 base = pjoin(self.me_dir, 'Cards', config_file) 1921 basedir = self.me_dir 1922 else: 1923 if hasattr(self, 'me_dir'): 1924 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1925 if len(args) == 0 and os.path.exists(base): 1926 self.write_configuration(base, base, self.me_dir) 1927 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1928 basedir = MG5DIR 1929 1930 if len(args) == 0: 1931 args.append(base) 1932 self.write_configuration(args[0], base, basedir, self.options)
1933
1934 - def write_configuration(self, filepath, basefile, basedir, to_keep):
1935 """Write the configuration file""" 1936 # We use the default configuration file as a template. 1937 # to ensure that all configuration information are written we 1938 # keep track of all key that we need to write. 1939 1940 logger.info('save configuration file to %s' % filepath) 1941 to_write = to_keep.keys() 1942 text = "" 1943 has_mg5_path = False 1944 # Use local configuration => Need to update the path 1945 for line in file(basefile): 1946 if '=' in line: 1947 data, value = line.split('=',1) 1948 else: 1949 text += line 1950 continue 1951 data = data.strip() 1952 if data.startswith('#'): 1953 key = data[1:].strip() 1954 else: 1955 key = data 1956 if '#' in value: 1957 value, comment = value.split('#',1) 1958 else: 1959 comment = '' 1960 if key in to_keep: 1961 value = str(to_keep[key]) 1962 else: 1963 text += line 1964 continue 1965 if key == 'mg5_path': 1966 has_mg5_path = True 1967 try: 1968 to_write.remove(key) 1969 except Exception: 1970 pass 1971 if '_path' in key: 1972 # special case need to update path 1973 # check if absolute path 1974 if not os.path.isabs(value): 1975 value = os.path.realpath(os.path.join(basedir, value)) 1976 text += '%s = %s # %s \n' % (key, value, comment) 1977 for key in to_write: 1978 if key in to_keep: 1979 text += '%s = %s \n' % (key, to_keep[key]) 1980 1981 if not MADEVENT and not has_mg5_path: 1982 text += """\n# MG5 MAIN DIRECTORY\n""" 1983 text += "mg5_path = %s\n" % MG5DIR 1984 1985 writer = open(filepath,'w') 1986 writer.write(text) 1987 writer.close()
1988
1989 1990 1991 1992 -class CmdShell(Cmd):
1993 """CMD command with shell activate""" 1994 1995 # Access to shell
1996 - def do_shell(self, line):
1997 "Run a shell command" 1998 1999 if line.strip() is '': 2000 self.help_shell() 2001 else: 2002 logging.info("running shell command: " + line) 2003 subprocess.call(line, shell=True)
2004
2005 - def complete_shell(self, text, line, begidx, endidx):
2006 """ add path for shell """ 2007 2008 # Filename if directory is given 2009 # 2010 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 2011 if not text: 2012 text = '' 2013 output = self.path_completion(text, 2014 base_dir=\ 2015 self.split_arg(line[0:begidx])[-1]) 2016 else: 2017 output = self.path_completion(text) 2018 return output
2019
2020 - def help_shell(self):
2021 """help for the shell""" 2022 logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE') 2023 logger.info("syntax: shell CMD (or ! CMD)",'$MG:BOLD')
2024
2025 2026 2027 -class NotValidInput(Exception): pass
2028 #===============================================================================
2029 # Question with auto-completion 2030 #=============================================================================== 2031 -class SmartQuestion(BasicCmd):
2032 """ a class for answering a question with the path autocompletion""" 2033 2034 allowpath = False
2035 - def preloop(self):
2036 """Initializing before starting the main loop""" 2037 self.prompt = '>' 2038 self.value = None 2039 BasicCmd.preloop(self)
2040 2041 @property
2042 - def answer(self):
2043 return self.value
2044
2045 - def __init__(self, question, allow_arg=[], default=None, 2046 mother_interface=None, *arg, **opt):
2047 2048 self.question = question 2049 self.wrong_answer = 0 # forbids infinite loop 2050 self.allow_arg = [str(a) for a in allow_arg] 2051 self.history_header = '' 2052 self.default_value = str(default) 2053 self.mother_interface = mother_interface 2054 2055 if 'case' in opt: 2056 self.casesensitive = opt['case'] 2057 del opt['case'] 2058 elif 'casesensitive' in opt: 2059 self.casesensitive = opt['casesensitive'] 2060 del opt['casesensitive'] 2061 else: 2062 self.casesensistive = True 2063 super(SmartQuestion, self).__init__(*arg, **opt)
2064
2065 - def __call__(self, question, reprint_opt=True, **opts):
2066 2067 self.question = question 2068 for key,value in opts: 2069 setattr(self, key, value) 2070 if reprint_opt: 2071 print question 2072 logger_tuto.info("Need help here? type 'help'", '$MG:BOLD') 2073 logger_plugin.info("Need help here? type 'help'" , '$MG:BOLD') 2074 return self.cmdloop()
2075 2076
2077 - def completenames(self, text, line, *ignored):
2078 prev_timer = signal.alarm(0) # avoid timer if any 2079 if prev_timer: 2080 nb_back = len(line) 2081 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2082 self.stdout.write(line) 2083 self.stdout.flush() 2084 try: 2085 out = {} 2086 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2087 out[' Recognized command'] = super(SmartQuestion, self).completenames(text,line, *ignored) 2088 2089 return self.deal_multiple_categories(out) 2090 except Exception, error: 2091 print error
2092 2093 completedefault = completenames 2094
2095 - def get_names(self):
2096 # This method used to pull in base class attributes 2097 # at a time dir() didn't do it yet. 2098 return dir(self)
2099
2100 - def onecmd(self, line, **opt):
2101 """catch all error and stop properly command accordingly 2102 Interpret the argument as though it had been typed in response 2103 to the prompt. 2104 2105 The return value is a flag indicating whether interpretation of 2106 commands by the interpreter should stop. 2107 2108 This allow to pass extra argument for internal call. 2109 """ 2110 try: 2111 if '~/' in line and os.environ.has_key('HOME'): 2112 line = line.replace('~/', '%s/' % os.environ['HOME']) 2113 line = os.path.expandvars(line) 2114 cmd, arg, line = self.parseline(line) 2115 if not line: 2116 return self.emptyline() 2117 if cmd is None: 2118 return self.default(line) 2119 self.lastcmd = line 2120 if cmd == '': 2121 return self.default(line) 2122 else: 2123 try: 2124 func = getattr(self, 'do_' + cmd) 2125 except AttributeError: 2126 return self.default(line) 2127 return func(arg, **opt) 2128 except Exception as error: 2129 logger.warning(error) 2130 if __debug__: 2131 raise
2132
2133 - def reask(self, reprint_opt=True):
2134 pat = re.compile('\[(\d*)s to answer\]') 2135 prev_timer = signal.alarm(0) # avoid timer if any 2136 2137 if prev_timer: 2138 if pat.search(self.question): 2139 timeout = int(pat.search(self.question).groups()[0]) 2140 signal.alarm(timeout) 2141 if reprint_opt: 2142 if not prev_timer: 2143 self.question = pat.sub('',self.question) 2144 print self.question.encode('utf8') 2145 2146 if self.mother_interface: 2147 answer = self.mother_interface.check_answer_in_input_file(self, 'EOF', 2148 path=self.allowpath) 2149 if answer: 2150 stop = self.default(answer) 2151 self.postcmd(stop, answer) 2152 return False 2153 2154 return False
2155
2156 - def do_help(self, line):
2157 2158 text=line 2159 out ={} 2160 out['Options'] = Cmd.list_completion(text, self.allow_arg) 2161 out['command'] = BasicCmd.completenames(self, text) 2162 2163 if not text: 2164 if out['Options']: 2165 logger.info( "Here is the list of all valid options:", '$MG:BOLD') 2166 logger.info( " "+ "\n ".join(out['Options'])) 2167 if out['command']: 2168 logger.info( "Here is the list of command available:", '$MG:BOLD') 2169 logger.info( " "+ "\n ".join(out['command'])) 2170 else: 2171 if out['Options']: 2172 logger.info( "Here is the list of all valid options starting with \'%s\'" % text, '$MG:BOLD') 2173 logger.info( " "+ "\n ".join(out['Options'])) 2174 if out['command']: 2175 logger.info( "Here is the list of command available starting with \'%s\':" % text, '$MG:BOLD') 2176 logger.info( " "+ "\n ".join(out['command'])) 2177 elif not out['Options']: 2178 logger.info( "No possibility starting with \'%s\'" % text, '$MG:BOLD') 2179 logger.info( "You can type help XXX, to see all command starting with XXX", '$MG:BOLD')
2180 - def complete_help(self, text, line, begidx, endidx):
2181 """ """ 2182 return self.completenames(text, line)
2183
2184 - def default(self, line):
2185 """Default action if line is not recognized""" 2186 2187 if line.strip() == '' and self.default_value is not None: 2188 self.value = self.default_value 2189 else: 2190 self.value = line
2191
2192 - def emptyline(self):
2193 """If empty line, return default""" 2194 2195 if self.default_value is not None: 2196 self.value = self.default_value
2197 2198
2199 - def postcmd(self, stop, line):
2200 2201 try: 2202 if self.value in self.allow_arg: 2203 return True 2204 elif str(self.value) == 'EOF': 2205 self.value = self.default_value 2206 return True 2207 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2208 return self.reask() 2209 elif self.value in ['repeat', 'reask']: 2210 return self.reask() 2211 elif len(self.allow_arg)==0: 2212 return True 2213 elif ' ' in line.strip() and '=' in self.value: 2214 line,n = re.subn(r'\s*=\s*', '=', line) 2215 if n: 2216 self.default(line) 2217 return self.postcmd(stop, line) 2218 if not self.casesensitive: 2219 for ans in self.allow_arg: 2220 if ans.lower() == self.value.lower(): 2221 self.value = ans 2222 return True 2223 break 2224 else: 2225 raise Exception 2226 2227 2228 else: 2229 raise Exception 2230 except Exception,error: 2231 if self.wrong_answer < 100: 2232 self.wrong_answer += 1 2233 logger.warning("""%s not valid argument. Valid argument are in (%s).""" \ 2234 % (self.value,','.join(self.allow_arg))) 2235 logger.warning('please retry') 2236 return False 2237 else: 2238 self.value = self.default_value 2239 return True
2240
2241 - def cmdloop(self, intro=None):
2242 super(SmartQuestion,self).cmdloop(intro) 2243 return self.answer
2244
2245 # a function helper 2246 -def smart_input(input_text, allow_arg=[], default=None):
2247 print input_text 2248 obj = SmartQuestion(allow_arg=allow_arg, default=default) 2249 return obj.cmdloop()
2250
2251 #=============================================================================== 2252 # Question in order to return a path with auto-completion 2253 #=============================================================================== 2254 -class OneLinePathCompletion(SmartQuestion):
2255 """ a class for answering a question with the path autocompletion""" 2256 2257 completion_prefix='' 2258 allowpath=True 2259
2260 - def completenames(self, text, line, begidx, endidx, formatting=True):
2261 prev_timer = signal.alarm(0) # avoid timer if any 2262 if prev_timer: 2263 nb_back = len(line) 2264 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2265 self.stdout.write(line) 2266 self.stdout.flush() 2267 2268 try: 2269 out = {} 2270 out[' Options'] = Cmd.list_completion(text, self.allow_arg) 2271 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 2272 out[' Recognized command'] = BasicCmd.completenames(self, text, line, begidx, endidx) 2273 2274 return self.deal_multiple_categories(out, formatting) 2275 except Exception, error: 2276 print error
2277
2278 - def precmd(self, *args):
2279 """ """ 2280 2281 signal.alarm(0) 2282 return SmartQuestion.precmd(self, *args)
2283
2284 - def completedefault(self,text, line, begidx, endidx):
2285 prev_timer = signal.alarm(0) # avoid timer if any 2286 if prev_timer: 2287 nb_back = len(line) 2288 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 2289 self.stdout.write(line) 2290 self.stdout.flush() 2291 try: 2292 args = Cmd.split_arg(line[0:begidx]) 2293 except Exception, error: 2294 print error 2295 2296 # Directory continuation 2297 if args[-1].endswith(os.path.sep): 2298 2299 return Cmd.path_completion(text, 2300 os.path.join('.',*[a for a in args \ 2301 if a.endswith(os.path.sep)]), 2302 begidx, endidx) 2303 return self.completenames(text, line, begidx, endidx)
2304 2305
2306 - def postcmd(self, stop, line):
2307 try: 2308 if self.value in self.allow_arg: 2309 return True 2310 elif self.value and os.path.isfile(self.value): 2311 return os.path.relpath(self.value) 2312 elif self.value and str(self.value) == 'EOF': 2313 self.value = self.default_value 2314 return True 2315 elif line and hasattr(self, 'do_%s' % line.split()[0]): 2316 # go to retry 2317 reprint_opt = True 2318 elif self.value == 'repeat': 2319 reprint_opt = True 2320 else: 2321 raise Exception 2322 except Exception, error: 2323 print """not valid argument. Valid argument are file path or value in (%s).""" \ 2324 % ','.join(self.allow_arg) 2325 print 'please retry' 2326 reprint_opt = False 2327 2328 if line != 'EOF': 2329 return self.reask(reprint_opt)
2330
2331 2332 # a function helper 2333 -def raw_path_input(input_text, allow_arg=[], default=None):
2334 print input_text 2335 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 2336 return obj.cmdloop()
2337
2338 2339 2340 -class ControlSwitch(SmartQuestion):
2341 """A class for asking a question on which program to run. 2342 This is the abstract class 2343 2344 Behavior for each switch can be customize via: 2345 set_default_XXXX() -> set default value 2346 This is super-seeded by self.default_switch if that attribute is defined (and has a key for XXXX) 2347 get_allowed_XXXX() -> return list of possible value 2348 check_value_XXXX(value) -> return True/False if the user can set such value 2349 switch_off_XXXXX() -> set it off (called for special mode) 2350 color_for_XXXX(value) -> return the representation on the screen for value 2351 get_cardcmd_for_XXXX(value)> return the command to run to customize the cards to 2352 match the status 2353 print_options_XXXX() -> return the text to disply below "other options" 2354 default is other possible value (ordered correctly) 2355 2356 consistency_XX_YY(val_XX, val_YY) 2357 -> XX is the new key set by the user to a new value val_XX 2358 -> YY is another key set by the user. 2359 -> return value should be None or "replace_YY" 2360 2361 consistency_XX(val_XX): 2362 check the consistency of the other switch given the new status of this one. 2363 return a dict {key:replaced_value} or {} if nothing to do 2364 2365 user typing "NAME" will result to a call to self.ans_NAME(None) 2366 user typing "NAME=XX" will result to a call to self.ans_NAME('XX') 2367 2368 Note on case sensitivity: 2369 ------------------------- 2370 the XXX is displayed with the case in self.to_control 2371 but ALL functions should use the lower case version. 2372 for key associated to get_allowed_keys(), 2373 if (user) value not in that list. 2374 -> try to find the first entry matching up to the case 2375 for ans_XXX, set the value to lower case, but if case_XXX is set to True 2376 """ 2377 2378 case_sensitive = False 2379 quit_on = ['0','done', 'EOF','','auto'] 2380
2381 - def __init__(self, to_control, motherinstance, *args, **opts):
2382 """to_control is a list of ('KEY': 'Choose the shower/hadronization program') 2383 """ 2384 2385 self.to_control = to_control 2386 self.mother_interface = motherinstance 2387 self.inconsistent_keys = {} #flag parameter which are currently not consistent 2388 # and the value by witch they will be replaced if the 2389 # inconsistency remains. 2390 self.inconsistent_details = {} # flag to list 2391 self.last_changed = [] # keep the order in which the flag have been modified 2392 # to choose the resolution order of conflict 2393 #initialise the main return value 2394 self.switch = {} 2395 for key, _ in to_control: 2396 self.switch[key.lower()] = 'temporary' 2397 2398 self.set_default_switch() 2399 question = self.create_question() 2400 2401 #check all default for auto-completion 2402 allowed_args = [ `i`+';' for i in range(1, 1+len(self.to_control))] 2403 for key in self.switch: 2404 allowed_args += ['%s=%s;' % (key,s) for s in self.get_allowed(key)] 2405 # adding special mode 2406 allowed_args += [key[4:]+';' for key in dir(self) if key.startswith('ans_')] 2407 if 'allow_arg' in opts: 2408 allowed_args += opts['allow_arg'] 2409 del opts['allow_arg'] 2410 2411 allowed_args +=["0", "done"] 2412 SmartQuestion.__init__(self, question, allowed_args, *args, **opts) 2413 self.options = self.mother_interface.options
2414
2415 - def special_check_answer_in_input_file(self, line, default):
2416 """this is called after the standard check if the asnwer were not valid 2417 in particular all input in the auto-completion have been already validated. 2418 (this include all those with ans_xx and the XXXX=YYY for YYY in self.get_allowed(XXXX) 2419 We just check here the XXXX = YYYY for YYYY not in self.get_allowed(XXXX) 2420 but for which self.check_value(XXXX,YYYY) returns True. 2421 We actually allowed XXXX = YYY even if check_value is False to allow case 2422 where some module are missing 2423 """ 2424 2425 if '=' not in line: 2426 if line.strip().startswith('set'): 2427 self.mother_interface.store_line(line) 2428 return str(default) 2429 return None 2430 key, value = line.split('=',1) 2431 if key.lower() in self.switch: 2432 return line 2433 if key in [str(i+1) for i in range(len(self.to_control))]: 2434 self.value='reask' 2435 return line 2436 if hasattr(self, 'ans_%s' % key.lower()): 2437 self.value='reask' 2438 return line 2439 2440 return None
2441 2442 2443 2444 2445
2446 - def set_default_switch(self):
2447 2448 for key,_ in self.to_control: 2449 key = key.lower() 2450 if hasattr(self, 'default_switch') and key in self.default_switch: 2451 self.switch[key] = self.default_switch[key] 2452 continue 2453 if hasattr(self, 'set_default_%s' % key): 2454 getattr(self, 'set_default_%s' % key)() 2455 else: 2456 self.default_switch_for(key)
2457
2458 - def default_switch_for(self, key):
2459 """use this if they are no dedicated function for such key""" 2460 2461 if hasattr(self, 'get_allowed_%s' % key): 2462 return getattr(self, 'get_allowed_%s' % key)()[0] 2463 else: 2464 self.switch[key] = 'OFF'
2465
2466 - def set_all_off(self):
2467 """set all valid parameter to OFF --call before special keyword-- 2468 """ 2469 2470 for key in self.switch: 2471 if hasattr(self, 'switch_off_%s' % key): 2472 getattr(self, 'switch_off_%s' % key)() 2473 elif self.check_value(key, self.switch[key]): 2474 self.switch[key] = 'OFF' 2475 self.inconsistent_details = {} 2476 self.inconsistent_keys = {}
2477 2478
2479 - def check_value(self, key, value):
2480 """return True/False if the value is a correct value to be set by the USER. 2481 other value than those can be set by the system --like-- Not available. 2482 This does not check the full consistency of the switch 2483 """ 2484 2485 if hasattr(self, 'check_value_%s' % key): 2486 return getattr(self, 'check_value_%s' % key)(value) 2487 elif value in self.get_allowed(key): 2488 return True 2489 else: 2490 return False
2491 2492
2493 - def get_cardcmd(self):
2494 """ return the list of command that need to be run to have a consistent 2495 set of cards with the switch value choosen """ 2496 2497 switch = self.answer 2498 cmd= [] 2499 for key in self.switch: 2500 if hasattr(self, 'get_cardcmd_for_%s' % key): 2501 cmd += getattr(self, 'get_cardcmd_for_%s' % key)(switch[key]) 2502 return cmd
2503 2504
2505 - def get_allowed(self, key):
2506 """return the list of possible value for key""" 2507 2508 if hasattr(self, 'get_allowed_%s' % key): 2509 return getattr(self, 'get_allowed_%s' % key)() 2510 else: 2511 return ['ON', 'OFF']
2512
2513 - def default(self, line, raise_error=False):
2514 """Default action if line is not recognized""" 2515 2516 line=line.strip().replace('@', '__at__') 2517 if ';' in line: 2518 for l in line.split(';'): 2519 if l: 2520 out = self.default(l) 2521 return out 2522 2523 if '=' in line: 2524 base, value = line.split('=',1) 2525 base = base.strip() 2526 value = value.strip() 2527 # allow 1=OFF 2528 if base.isdigit() : 2529 try: 2530 base = self.to_control[int(base)-1][0] 2531 except: 2532 pass 2533 elif ' ' in line: 2534 base, value = line.split(' ', 1) 2535 elif hasattr(self, 'ans_%s' % line.lower()): 2536 base, value = line.lower(), None 2537 elif line.isdigit() and line in [`i` for i in range(1, len(self.to_control)+1)]: 2538 # go from one valid option to the next in the get_allowed for that option 2539 base = self.to_control[int(line)-1][0].lower() 2540 return self.default(base) # just recall this function with the associate name 2541 elif line.lower() in self.switch: 2542 # go from one valid option to the next in the get_allowed for that option 2543 base = line.lower() 2544 try: 2545 cur = self.get_allowed(base).index(self.switch[base]) 2546 except: 2547 if self.get_allowed(base): 2548 value = self.get_allowed(base)[0] 2549 else: 2550 logger.warning('Can not switch "%s" to another value via number', base) 2551 self.value='reask' 2552 return 2553 else: 2554 try: 2555 value = self.get_allowed(base)[cur+1] 2556 except IndexError: 2557 value = self.get_allowed(base)[0] 2558 if value == "OFF" and cur == 0: 2559 logger.warning("Invalid action: %s" % self.print_options(base)) 2560 elif cur == 0: 2561 logger.warning("Can not change value for this parameter") 2562 2563 2564 elif line in ['', 'done', 'EOF', 'eof','0']: 2565 super(ControlSwitch, self).default(line) 2566 return self.answer 2567 elif line in 'auto': 2568 self.switch['dynamical'] = True 2569 return super(ControlSwitch, self).default(line) 2570 elif raise_error: 2571 raise NotValidInput('unknow command: %s' % line) 2572 else: 2573 logger.warning('unknow command: %s' % line) 2574 self.value = 'reask' 2575 return 2576 2577 self.value = 'reask' 2578 base = base.lower() 2579 if hasattr(self, 'ans_%s' % base): 2580 if value and not self.is_case_sensitive(base): 2581 value = value.lower() 2582 getattr(self, 'ans_%s' % base)(value) 2583 elif base in self.switch: 2584 self.set_switch(base, value) 2585 elif raise_error: 2586 raise NotValidInput('Not valid command: %s' % line) 2587 else: 2588 logger.warning('Not valid command: %s' % line)
2589
2590 - def is_case_sensitive(self, key):
2591 """check if a key is case sensitive""" 2592 2593 case = self.case_sensitive 2594 if hasattr(self, 'case_%s' % key): 2595 case = getattr(self, 'case_%s' % key) 2596 return case
2597
2598 - def onecmd(self, line, **opt):
2599 """ensure to rewrite the function if a call is done directly""" 2600 out = super(ControlSwitch, self).onecmd(line, **opt) 2601 self.create_question() 2602 return out
2603 2604 @property
2605 - def answer(self):
2606 2607 #avoid key to Not Avail in the output 2608 for key,_ in self.to_control: 2609 if not self.check_value(key, self.switch[key]): 2610 self.switch[key] = 'OFF' 2611 2612 if not self.inconsistent_keys: 2613 return self.switch 2614 else: 2615 out = dict(self.switch) 2616 out.update(self.inconsistent_keys) 2617 return out
2618
2619 - def postcmd(self, stop, line):
2620 2621 # for diamond class arch where both branch defines the postcmd 2622 # set it up to be in coop mode 2623 try: 2624 out = super(ControlSwitch,self).postcmd(stop, line) 2625 except AttributeError: 2626 pass 2627 2628 line = line.strip() 2629 if ';' in line: 2630 line= [l for l in line.split(';') if l][-1] 2631 if line in self.quit_on: 2632 return True 2633 self.create_question() 2634 return self.reask(True)
2635 2636
2637 - def set_switch(self, key, value, user=True):
2638 """change a switch to a given value""" 2639 2640 assert key in self.switch 2641 2642 if hasattr(self, 'ans_%s' % key): 2643 if not self.is_case_sensitive(key): 2644 value = value.lower() 2645 return getattr(self, 'ans_%s' % key)(value) 2646 2647 if not self.is_case_sensitive(key) and value not in self.get_allowed(key): 2648 lower = [t.lower() for t in self.get_allowed(key)] 2649 try: 2650 ind = lower.index(value.lower()) 2651 except ValueError: 2652 pass # keep the current case, in case check_value accepts it anyway. 2653 else: 2654 value = self.get_allowed(key)[ind] 2655 2656 check = self.check_value(key, value) 2657 if not check: 2658 logger.warning('"%s" not valid option for "%s"', value, key) 2659 return 2660 if isinstance(check, str): 2661 value = check 2662 2663 self.switch[key] = value 2664 2665 if user: 2666 self.check_consistency(key, value)
2667
2668 - def remove_inconsistency(self, keys=[]):
2669 2670 if not keys: 2671 self.inconsistent_keys = {} 2672 self.inconsistent_details = {} 2673 elif isinstance(keys, list): 2674 for key in keys: 2675 if key in self.inconsistent_keys: 2676 del self.inconsistent_keys[keys] 2677 del self.inconsistent_details[keys] 2678 else: 2679 if keys in self.inconsistent_keys: 2680 del self.inconsistent_keys[keys] 2681 del self.inconsistent_details[keys]
2682
2683 - def check_consistency(self, key, value):
2684 """check the consistency of the new flag with the old ones""" 2685 2686 2687 if key in self.last_changed: 2688 self.last_changed.remove(key) 2689 self.last_changed.append(key) 2690 2691 # this is used to update self.consistency_keys which contains: 2692 # {key: replacement value with solved conflict} 2693 # it is based on self.consistency_details which is a dict 2694 # key: {'orig_value': 2695 # 'changed_key': 2696 # 'new_changed_key_val': 2697 # 'replacement': 2698 # which keeps track of all conflict and of their origin. 2699 2700 2701 # rules is a dict: {keys:None} if the value for that key is consistent. 2702 # {keys:value_to_replace} if that key is inconsistent 2703 if hasattr(self, 'consistency_%s' % key): 2704 rules = dict([(key2, None) for key2 in self.switch]) 2705 rules.update(getattr(self, 'consistency_%s' % key)(value, self.switch)) 2706 else: 2707 rules = {} 2708 for key2,value2 in self.switch.items(): 2709 if hasattr(self, 'consistency_%s_%s' % (key,key2)): 2710 rules[key2] = getattr(self, 'consistency_%s_%s' % (key,key2))(value, value2) 2711 # check that the suggested value is allowed. 2712 # can happen that it is not if some program are not installed 2713 if rules[key2] is not None and not self.check_value(key2, rules[key2]): 2714 if rules[key2] != 'OFF': 2715 logger.debug('consistency_%s_%s returns invalid output. Assume no conflict') 2716 rules[key2] = None 2717 else: 2718 rules[key2] = None 2719 2720 # 2721 2722 #update the self.inconsisten_details adding new conflict 2723 # start by removing the inconsistency for the newly set parameter 2724 self.remove_inconsistency(key) 2725 # then add the new ones 2726 for key2 in self.switch: 2727 if rules[key2]: 2728 info = {'orig_value': self.switch[key2], 2729 'changed_key': key, 2730 'new_changed_key_val': value, 2731 'replacement': rules[key2]} 2732 if key2 in self.inconsistent_details: 2733 self.inconsistent_details[key2].append(info) 2734 else: 2735 self.inconsistent_details[key2] = [info] 2736 2737 if not self.inconsistent_details: 2738 return 2739 2740 # review the status of all conflict 2741 for key2 in dict(self.inconsistent_details): 2742 for conflict in list(self.inconsistent_details[key2]): 2743 keep_conflict = True 2744 # check that we are still at the current value 2745 if conflict['orig_value'] != self.switch[key2]: 2746 keep_conflict = False 2747 # check if the reason of the conflict still in place 2748 if self.switch[conflict['changed_key']] != conflict['new_changed_key_val']: 2749 keep_conflict = False 2750 if not keep_conflict: 2751 self.inconsistent_details[key2].remove(conflict) 2752 if not self.inconsistent_details[key2]: 2753 del self.inconsistent_details[key2] 2754 2755 2756 # create the valid set of replacement for this current conflict 2757 # start by current status to avoid to keep irrelevant conflict 2758 tmp_switch = dict(self.switch) 2759 2760 # build the order in which we have to check the various conflict reported 2761 to_check = [(c['changed_key'], c['new_changed_key_val']) \ 2762 for k in self.inconsistent_details.values() for c in k 2763 if c['changed_key'] != key] 2764 2765 to_check.sort(lambda x, y: -1 if self.last_changed.index(x[0])>self.last_changed.index(y[0]) else 1) 2766 2767 # validate tmp_switch. 2768 to_check = [(key, value)] + to_check 2769 2770 i = 0 2771 while len(to_check) and i < 50: 2772 #misc.sprint(i, to_check, tmp_switch) 2773 # check in a iterative way the consistency of the tmp_switch parameter 2774 i +=1 2775 key2, value2 = to_check.pop(0) 2776 if hasattr(self, 'consistency_%s' % key2): 2777 rules2 = dict([(key2, None) for key2 in self.switch]) 2778 rules2.update(getattr(self, 'consistency_%s' % key2)(value, tmp_switch)) 2779 else: 2780 rules = {} 2781 for key3,value3 in self.switch.items(): 2782 if hasattr(self, 'consistency_%s_%s' % (key2,key3)): 2783 rules[key3] = getattr(self, 'consistency_%s_%s' % (key2,key3))(value2, value3) 2784 else: 2785 rules[key3] = None 2786 2787 for key, replacement in rules.items(): 2788 if replacement: 2789 tmp_switch[key] = replacement 2790 to_check.append((key, replacement)) 2791 # avoid situation like 2792 # to_check = [('fixed_order', 'ON'), ('fixed_order', 'OFF')] 2793 # always keep the last one 2794 pos = {} 2795 for i, (key,value) in enumerate(to_check): 2796 pos[key] = i 2797 to_check_new = [] 2798 for i, (key,value) in enumerate(to_check): 2799 if pos[key] == i: 2800 to_check_new.append((key,value)) 2801 to_check = to_check_new 2802 if i>=50: 2803 logger.critical('Failed to find a consistent set of switch values.') 2804 2805 # Now tmp_switch is to a fully consistent setup for sure. 2806 # fill self.inconsistent_key 2807 self.inconsistent_keys = {} 2808 for key2, value2 in tmp_switch.items(): 2809 if value2 != self.switch[key2]: 2810 # check that not available module stays on that switch 2811 if value2 == 'OFF' and not self.check_value(key2, 'OFF'): 2812 continue 2813 self.inconsistent_keys[key2] = value2
2814 2815 # 2816 # Helper routine for putting questions with correct color 2817 # 2818 green = '\x1b[32m%s\x1b[0m' 2819 yellow = '\x1b[33m%s\x1b[0m' 2820 red = '\x1b[31m%s\x1b[0m' 2821 bold = '\x1b[01m%s\x1b[0m'
2822 - def color_for_value(self, key, switch_value, consistency=True):
2823 2824 if consistency and key in self.inconsistent_keys: 2825 return self.color_for_value(key, self.inconsistent_keys[key], consistency=False) +\ 2826 u' \u21d0 '+ self.yellow % switch_value 2827 2828 if self.check_value(key, switch_value): 2829 if hasattr(self, 'color_for_%s' % key): 2830 return getattr(self, 'color_for_%s' % key)(switch_value) 2831 if switch_value in ['OFF']: 2832 # inconsistent key are the list of key which are inconsistent with the last change 2833 return self.red % switch_value 2834 else: 2835 return self.green % switch_value 2836 else: 2837 if ' ' in switch_value: 2838 return self.bold % switch_value 2839 else: 2840 return self.red % switch_value
2841
2842 - def print_options(self,key, keep_default=False):
2843 2844 if hasattr(self, 'print_options_%s' % key) and not keep_default: 2845 return getattr(self, 'print_options_%s' % key)() 2846 2847 #re-order the options in order to have those in cycling order 2848 try: 2849 ind = self.get_allowed(key).index(self.switch[key]) 2850 except Exception, err: 2851 options = self.get_allowed(key) 2852 else: 2853 options = self.get_allowed(key)[ind:]+ self.get_allowed(key)[:ind] 2854 2855 info = '|'.join([v for v in options if v != self.switch[key]]) 2856 if info == '': 2857 info = 'Please install module' 2858 return info
2859
2860 - def do_help(self, line, list_command=False):
2861 """dedicated help for the control switch""" 2862 2863 if line: 2864 return self.print_help_for_switch(line) 2865 2866 # here for simple "help" 2867 logger.info(" ") 2868 logger.info(" In order to change a switch you can:") 2869 logger.info(" - type 'NAME = VALUE' to set the switch NAME to a given value.") 2870 logger.info(" - type 'ID = VALUE' to set the switch correspond to the line ID to a given value.") 2871 logger.info(" - type 'ID' where ID is the value of the line to pass from one value to the next.") 2872 logger.info(" - type 'NAME' to set the switch NAME to the next value.") 2873 logger.info("") 2874 logger.info(" You can type 'help NAME' for more help on a given switch") 2875 logger.info("") 2876 logger.info(" Special keyword:", '$MG:BOLD') 2877 logger.info(" %s" % '\t'.join([p[4:] for p in dir(self) if p.startswith('ans_')]) ) 2878 logger.info(" type 'help XXX' for more information") 2879 if list_command: 2880 super(ControlSwitch, self).do_help(line)
2881 2882
2883 - def print_help_for_switch(self, line):
2884 """ """ 2885 2886 arg = line.split()[0] 2887 2888 if hasattr(self, 'help_%s' % arg): 2889 return getattr(self, 'help_%s' % arg)('') 2890 2891 if hasattr(self, 'ans_%s' % arg): 2892 return getattr(self, 'help_%s' % arg).__doc__ 2893 2894 if arg in self.switch: 2895 logger.info(" information for switch %s: ", arg, '$MG:BOLD') 2896 logger.info(" allowed value:") 2897 logger.info(" %s", '\t'.join(self.get_allowed(arg))) 2898 if hasattr(self, 'help_text_%s' % arg): 2899 logger.info("") 2900 for line in getattr(self, 'help_text_%s' % arg): 2901 logger.info(line)
2902 2903 2904 2905
2906 - def question_formatting(self, nb_col = 80, 2907 ldescription=0, 2908 lswitch=0, 2909 lname=0, 2910 ladd_info=0, 2911 lpotential_switch=0, 2912 lnb_key=0, 2913 key=None):
2914 """should return four lines: 2915 1. The upper band (typically /========\ 2916 2. The lower band (typically \========/ 2917 3. The line without conflict | %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s | 2918 4. The line with conflict | %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s | 2919 # Be carefull to include the size of the color flag for the switch 2920 green/red/yellow are adding 9 in length 2921 2922 line should be like '| %(nb)2d. %(descrip)-20s %(name)5s = %(switch)-10s |' 2923 2924 the total lenght of the line (for defining the upper/lower line) 2925 available key : nb 2926 descrip 2927 name 2928 switch # formatted with color + conflict handling 2929 conflict_switch # color formatted value from self.inconsistent_keys 2930 switch_nc # self.switch without color formatting 2931 conflict_switch_nc # self.inconsistent_keys without color formatting 2932 add_info 2933 """ 2934 2935 if key: 2936 # key is only provided for conflict 2937 len_switch = len(self.switch[key]) 2938 if key in self.inconsistent_keys: 2939 len_cswitch = len(self.inconsistent_keys[key]) 2940 else: 2941 len_cswitch = 0 2942 else: 2943 len_switch = 0 2944 len_cswitch = 0 2945 2946 list_length = [] 2947 # | 1. KEY = VALUE | 2948 list_length.append(lnb_key + lname + lswitch + 9) 2949 #1. DESCRIP KEY = VALUE 2950 list_length.append(lnb_key + ldescription+ lname + lswitch + 6) 2951 #1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT 2952 list_length.append(list_length[-1] - lswitch + max(lswitch,lpotential_switch)) 2953 #| 1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT | 2954 list_length.append(list_length[-1] +4) 2955 # 1. DESCRIP KEY = VALUE_MAXSIZE 2956 list_length.append(lnb_key + ldescription+ lname + max((2*lpotential_switch+3),lswitch) + 6) 2957 #| 1. DESCRIP KEY = VALUE_MAXSIZE | 2958 list_length.append(list_length[-1] +4) 2959 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 2960 list_length.append(list_length[-1] +3+ max(15,6+ladd_info)) 2961 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 2962 list_length.append(list_length[-2] +13+ max(15,10+ladd_info)) 2963 2964 selected = [0] + [i+1 for i,s in enumerate(list_length) if s < nb_col] 2965 selected = selected[-1] 2966 2967 # upper and lower band 2968 if selected !=0: 2969 size = list_length[selected-1] 2970 else: 2971 size = nb_col 2972 2973 2974 # default for upper/lower: 2975 upper = "/%s\\" % ("=" * (size-2)) 2976 lower = "\\%s/" % ("=" * (size-2)) 2977 2978 if selected==0: 2979 f1= '%(nb){0}d \x1b[1m%(name){1}s\x1b[0m=%(switch)-{2}s'.format(lnb_key, 2980 lname,lswitch) 2981 f2= f1 2982 # | 1. KEY = VALUE | 2983 elif selected == 1: 2984 upper = "/%s\\" % ("=" * (nb_col-2)) 2985 lower = "\\%s/" % ("=" * (nb_col-2)) 2986 to_add = nb_col -size 2987 f1 = '| %(nb){0}d. \x1b[1m%(name){1}s\x1b[0m = %(switch)-{2}s |'.format(lnb_key, 2988 lname,lswitch+9+to_add) 2989 2990 f = u'| %(nb){0}d. \x1b[1m%(name){1}s\x1b[0m = %(conflict_switch)-{2}s \u21d0 %(strike_switch)-{3}s |' 2991 f2 =f.format(lnb_key, lname, len_cswitch+9, lswitch-len_cswitch+len_switch+to_add-1) 2992 #1. DESCRIP KEY = VALUE 2993 elif selected == 2: 2994 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 2995 f1 = f.format(lnb_key, ldescription,lname,lswitch) 2996 f2 = f1 2997 #1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT 2998 elif selected == 3: 2999 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 3000 f1 = f.format(lnb_key, ldescription,lname,max(lpotential_switch, lswitch)) 3001 l_conflict_line = size-lpotential_switch+len_switch+len_cswitch+3+1 3002 if l_conflict_line <= nb_col: 3003 f = u'%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s\u21d0 %(strike_switch)-{4}s' 3004 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3005 elif l_conflict_line -4 <= nb_col: 3006 f = u'%(nb){0}d.%(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m=%(conflict_switch)-{3}s\u21d0 %(strike_switch)-{4}s' 3007 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3008 else: 3009 ldescription -= (l_conflict_line - nb_col) 3010 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' 3011 f2 =f.format(lnb_key, ldescription,lname,len_cswitch+9, lpotential_switch) 3012 #| 1. DESCRIP KEY = VALUE_SIZE_NOCONFLICT | 3013 elif selected == 4: 3014 upper = "/%s\\" % ("=" * (nb_col-2)) 3015 lower = "\\%s/" % ("=" * (nb_col-2)) 3016 to_add = nb_col -size 3017 f='| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s |' 3018 f1 = f.format(lnb_key,ldescription,lname,max(lpotential_switch, lswitch)+9+to_add) 3019 l_conflict_line = size-lpotential_switch+len_switch+len_cswitch+3+1 3020 if l_conflict_line <= nb_col: 3021 f=u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s|' 3022 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3023 elif l_conflict_line -1 <= nb_col: 3024 f=u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3025 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3026 elif l_conflict_line -3 <= nb_col: 3027 f=u'| %(nb){0}d. %(descrip)-{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, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3029 3030 else: 3031 ldescription -= (l_conflict_line - nb_col) 3032 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' 3033 f2 = f.format(lnb_key,ldescription,lname, len_cswitch+9, max(lswitch,lpotential_switch)-len_cswitch+len_switch+to_add-3+3) 3034 3035 # 1. DESCRIP KEY = VALUE_MAXSIZE 3036 elif selected == 5: 3037 f = '%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s' 3038 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)) 3039 f = u'%(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s' 3040 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9, max(2*lpotential_switch+3, lswitch)-lpotential_switch+len_switch) 3041 #| 1. DESCRIP KEY = VALUE_MAXSIZE | 3042 elif selected == 6: 3043 f= '| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s |' 3044 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)+9) 3045 f= u'| %(nb){0}d. %(descrip)-{1}s \x1b[1m%(name){2}s\x1b[0m = %(conflict_switch)-{3}s \u21d0 %(strike_switch)-{4}s|' 3046 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9,max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch) 3047 #| 1. DESCRIP | KEY = VALUE_MAXSIZE | INFO | 3048 elif selected == 7: 3049 ladd_info = max(15,6+ladd_info) 3050 upper = "/{0:=^%s}|{1:=^%s}|{2:=^%s}\\" % (lnb_key+ldescription+4, 3051 lname+max(2*lpotential_switch+3, lswitch)+5, 3052 ladd_info) 3053 upper = upper.format(' Description ', ' values ', ' other options ') 3054 3055 f='| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s | %(add_info)-{4}s |' 3056 f1 = f.format(lnb_key,ldescription,lname,max(2*lpotential_switch+3,lswitch)+9, ladd_info-4) 3057 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 |' 3058 f2 = f.format(lnb_key,ldescription,lname,lpotential_switch+9, 3059 max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch, ladd_info-4) 3060 elif selected == 8: 3061 ladd_info = max(15,10+ladd_info) 3062 upper = "/{0:=^%s}|{1:=^%s}|{2:=^%s}\\" % (lnb_key+ldescription+4+5, 3063 lname+max(3+2*lpotential_switch,lswitch)+10, 3064 ladd_info) 3065 upper = upper.format(' Description ', ' values ', ' other options ') 3066 lower = "\\%s/" % ("=" * (size-2)) 3067 3068 f='| %(nb){0}d. %(descrip)-{1}s | \x1b[1m%(name){2}s\x1b[0m = %(switch)-{3}s | %(add_info)-{4}s|' 3069 f1 = f.format(lnb_key,ldescription+5,5+lname,max(2*lpotential_switch+3,lswitch)+9, ladd_info-5) 3070 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|' 3071 f2 = f.format(lnb_key,ldescription+5,5+lname, 3072 lpotential_switch+9, 3073 max(2*lpotential_switch+3,lswitch)-lpotential_switch+len_switch, ladd_info-5) 3074 3075 3076 return upper, lower, f1, f2
3077
3078 - def create_question(self, help_text=True):
3079 """ create the question with correct formatting""" 3080 3081 # geth the number of line and column of the shell to adapt the printing 3082 # accordingly 3083 try: 3084 nb_rows, nb_col = os.popen('stty size', 'r').read().split() 3085 nb_rows, nb_col = int(nb_rows), int(nb_col) 3086 except Exception,error: 3087 nb_rows, nb_col = 20, 80 3088 3089 #compute information on the length of element to display 3090 max_len_description = 0 3091 max_len_switch = 0 3092 max_len_name = 0 3093 max_len_add_info = 0 3094 max_len_potential_switch = 0 3095 max_nb_key = 1 + int(math.log10(len(self.to_control))) 3096 3097 for key, descrip in self.to_control: 3098 if len(descrip) > max_len_description: max_len_description = len(descrip) 3099 if len(key) > max_len_name: max_len_name = len(key) 3100 if key in self.inconsistent_keys: 3101 to_display = '%s < %s' % (self.switch[key], self.inconsistent_keys[key]) 3102 else: 3103 to_display = self.switch[key] 3104 if len(to_display) > max_len_switch: max_len_switch=len(to_display) 3105 3106 info = self.print_options(key) 3107 if len(info)> max_len_add_info: max_len_add_info = len(info) 3108 3109 if self.get_allowed(key): 3110 max_k = max(len(k) for k in self.get_allowed(key)) 3111 else: 3112 max_k = 0 3113 if max_k > max_len_potential_switch: max_len_potential_switch = max_k 3114 3115 upper_line, lower_line, f1, f2 = self.question_formatting(nb_col, max_len_description, max_len_switch, 3116 max_len_name, max_len_add_info, 3117 max_len_potential_switch, max_nb_key) 3118 3119 text = \ 3120 ["The following switches determine which programs are run:", 3121 upper_line 3122 ] 3123 3124 3125 3126 for i,(key, descrip) in enumerate(self.to_control): 3127 3128 3129 3130 data_to_format = {'nb': i+1, 3131 'descrip': descrip, 3132 'name': key, 3133 'switch': self.color_for_value(key,self.switch[key]), 3134 'add_info': self.print_options(key), 3135 'switch_nc': self.switch[key], 3136 'strike_switch': u'\u0336'.join(' %s ' %self.switch[key].upper()) + u'\u0336', 3137 } 3138 if key in self.inconsistent_keys: 3139 # redefine the formatting here, due to the need to know the conflict size 3140 _,_,_, f2 = self.question_formatting(nb_col, max_len_description, max_len_switch, 3141 max_len_name, max_len_add_info, 3142 max_len_potential_switch, max_nb_key, 3143 key=key) 3144 3145 data_to_format['conflict_switch_nc'] = self.inconsistent_keys[key] 3146 data_to_format['conflict_switch'] = self.color_for_value(key,self.inconsistent_keys[key], consistency=False) 3147 text.append(f2 % data_to_format) 3148 else: 3149 text.append(f1 % data_to_format) 3150 3151 3152 text.append(lower_line) 3153 3154 # find a good example of switch to set for the lower part of the description 3155 example = None 3156 for key in self.switch: 3157 if len(self.get_allowed(key)) > 1: 3158 for val in self.get_allowed(key): 3159 if val != self.switch[key]: 3160 example = (key, val) 3161 break 3162 else: 3163 continue 3164 break 3165 3166 if not example: 3167 example = ('KEY', 'VALUE') 3168 3169 if help_text: 3170 text += \ 3171 ["Either type the switch number (1 to %s) to change its setting," % len(self.to_control), 3172 "Set any switch explicitly (e.g. type '%s=%s' at the prompt)" % example, 3173 "Type 'help' for the list of all valid option", 3174 "Type '0', 'auto', 'done' or just press enter when you are done."] 3175 3176 # check on the number of row: 3177 if len(text) > nb_rows: 3178 # too many lines. Remove some 3179 to_remove = [ -2, #Type 'help' for the list of all valid option 3180 -5, # \====/ 3181 -4, #Either type the switch number (1 to %s) to change its setting, 3182 -3, # Set any switch explicitly 3183 -1, # Type '0', 'auto', 'done' or just press enter when you are done. 3184 ] 3185 to_remove = to_remove[:min(len(to_remove), len(text)-nb_rows)] 3186 text = [t for i,t in enumerate(text) if i-len(text) not in to_remove] 3187 3188 self.question = "\n".join(text) 3189 return self.question
3190
3191 3192 #=============================================================================== 3193 # 3194 #=============================================================================== 3195 -class CmdFile(file):
3196 """ a class for command input file -in order to debug cmd \n problem""" 3197
3198 - def __init__(self, name, opt='rU'):
3199 3200 file.__init__(self, name, opt) 3201 self.text = file.read(self) 3202 self.close() 3203 self.lines = self.text.split('\n')
3204
3205 - def readline(self, *arg, **opt):
3206 """readline method treating correctly a line whithout \n at the end 3207 (add it) 3208 """ 3209 if self.lines: 3210 line = self.lines.pop(0) 3211 else: 3212 return '' 3213 3214 if line.endswith('\n'): 3215 return line 3216 else: 3217 return line + '\n'
3218
3219 - def __next__(self):
3220 return self.lines.__next__()
3221
3222 - def __iter__(self):
3223 return self.lines.__iter__()
3224