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