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