Package madgraph :: Package interface :: Module extended_cmd
[hide private]
[frames] | no frames]

Source Code for Module madgraph.interface.extended_cmd

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