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

Source Code for Module madgraph.interface.extended_cmd

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