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