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