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