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 MadGraph Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph 5 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 MadGraph license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, please visit: http://madgraph.phys.ucl.ac.be 
  13  # 
  14  ################################################################################ 
  15  """  A file containing different extension of the cmd basic python library""" 
  16   
  17   
  18  import cmd 
  19  import logging 
  20  import os 
  21  import pydoc 
  22  import signal 
  23  import subprocess 
  24  import sys 
  25  import traceback 
  26  try: 
  27      import readline 
  28      GNU_SPLITTING = ('GNU' in readline.__doc__) 
  29  except: 
  30      readline = None 
  31      GNU_SPLITTING = True 
  32   
  33   
  34  logger = logging.getLogger('cmdprint') # for stdout 
  35  logger_stderr = logging.getLogger('fatalerror') # for stderr 
  36   
  37  try: 
  38      import madgraph.various.misc as misc 
  39      from madgraph import MG5DIR 
  40      MADEVENT = False 
  41  except Exception, error: 
  42      if __debug__: 
  43          print error 
  44      import internal.misc as misc 
  45      MADEVENT = True 
  46   
  47  pjoin = os.path.join 
48 49 -class TimeOutError(Exception):
50 """Class for run-time error"""
51
52 -def debug(debug_only=True):
53 54 def deco_debug(f): 55 56 if debug_only and not __debug__: 57 return f 58 59 def deco_f(*args, **opt): 60 try: 61 return f(*args, **opt) 62 except Exception, error: 63 print error 64 print traceback.print_exc(file=sys.stdout) 65 return
66 return deco_f 67 return deco_debug 68
69 70 #=============================================================================== 71 # CmdExtended 72 #=============================================================================== 73 -class BasicCmd(cmd.Cmd):
74 """Simple extension for the readline""" 75
76 - def preloop(self):
77 if readline and not 'libedit' in readline.__doc__: 78 readline.set_completion_display_matches_hook(self.print_suggestions)
79
80 - def deal_multiple_categories(self, dico):
81 """convert the multiple category in a formatted list understand by our 82 specific readline parser""" 83 84 if 'libedit' in readline.__doc__: 85 # No parser in this case, just send all the valid options 86 out = [] 87 for name, opt in dico.items(): 88 out += opt 89 return out 90 91 # That's the real work 92 out = [] 93 valid=0 94 # if the key starts with number order the key with that number. 95 for name, opt in dico.items(): 96 if not opt: 97 continue 98 name = name.replace(' ', '_') 99 valid += 1 100 out.append(opt[0].rstrip()+'@@'+name+'@@') 101 # Remove duplicate 102 d = {} 103 for x in opt: 104 d[x] = 1 105 opt = list(d.keys()) 106 opt.sort() 107 out += opt 108 109 110 if valid == 1: 111 out = out[1:] 112 return out
113 114 @debug()
115 - def print_suggestions(self, substitution, matches, longest_match_length) :
116 """print auto-completions by category""" 117 longest_match_length += len(self.completion_prefix) 118 try: 119 if len(matches) == 1: 120 self.stdout.write(matches[0]+' ') 121 return 122 self.stdout.write('\n') 123 l2 = [a[-2:] for a in matches] 124 if '@@' in l2: 125 nb_column = self.getTerminalSize()//(longest_match_length+1) 126 pos=0 127 for val in self.completion_matches: 128 if val.endswith('@@'): 129 category = val.rsplit('@@',2)[1] 130 category = category.replace('_',' ') 131 self.stdout.write('\n %s:\n%s\n' % (category, '=' * (len(category)+2))) 132 start = 0 133 pos = 0 134 continue 135 elif pos and pos % nb_column ==0: 136 self.stdout.write('\n') 137 self.stdout.write(self.completion_prefix + val + \ 138 ' ' * (longest_match_length +1 -len(val))) 139 pos +=1 140 self.stdout.write('\n') 141 else: 142 # nb column 143 nb_column = self.getTerminalSize()//(longest_match_length+1) 144 for i,val in enumerate(matches): 145 if i and i%nb_column ==0: 146 self.stdout.write('\n') 147 self.stdout.write(self.completion_prefix + val + \ 148 ' ' * (longest_match_length +1 -len(val))) 149 self.stdout.write('\n') 150 151 self.stdout.write(self.prompt+readline.get_line_buffer()) 152 self.stdout.flush() 153 except Exception, error: 154 if __debug__: 155 print error
156
157 - def getTerminalSize(self):
158 def ioctl_GWINSZ(fd): 159 try: 160 import fcntl, termios, struct, os 161 cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 162 '1234')) 163 except: 164 return None 165 return cr
166 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) 167 if not cr: 168 try: 169 fd = os.open(os.ctermid(), os.O_RDONLY) 170 cr = ioctl_GWINSZ(fd) 171 os.close(fd) 172 except: 173 pass 174 if not cr: 175 try: 176 cr = (env['LINES'], env['COLUMNS']) 177 except: 178 cr = (25, 80) 179 return int(cr[1])
180
181 - def complete(self, text, state):
182 """Return the next possible completion for 'text'. 183 If a command has not been entered, then complete against command list. 184 Otherwise try to call complete_<command> to get list of completions. 185 """ 186 187 if state == 0: 188 import readline 189 origline = readline.get_line_buffer() 190 line = origline.lstrip() 191 stripped = len(origline) - len(line) 192 begidx = readline.get_begidx() - stripped 193 endidx = readline.get_endidx() - stripped 194 195 if ';' in line: 196 begin, line = line.rsplit(';',1) 197 begidx = begidx - len(begin) - 1 198 endidx = endidx - len(begin) - 1 199 if line[:begidx] == ' ' * begidx: 200 begidx=0 201 202 if begidx>0: 203 cmd, args, foo = self.parseline(line) 204 if cmd == '': 205 compfunc = self.completedefault 206 else: 207 try: 208 compfunc = getattr(self, 'complete_' + cmd) 209 except AttributeError: 210 compfunc = self.completedefault 211 else: 212 compfunc = self.completenames 213 214 # correct wrong splittion with '\ ' 215 if line and begidx > 2 and line[begidx-2:begidx] == '\ ': 216 Ntext = line.split(os.path.sep)[-1] 217 self.completion_prefix = Ntext.rsplit('\ ', 1)[0] + '\ ' 218 to_rm = len(self.completion_prefix) - 1 219 Nbegidx = len(line.rsplit(os.path.sep, 1)[0]) + 1 220 data = compfunc(Ntext.replace('\ ', ' '), line, Nbegidx, endidx) 221 self.completion_matches = [p[to_rm:] for p in data 222 if len(p)>to_rm] 223 # correct wrong splitting with '-' 224 elif line and line[begidx-1] == '-': 225 try: 226 Ntext = line.split()[-1] 227 self.completion_prefix = Ntext.rsplit('-',1)[0] +'-' 228 to_rm = len(self.completion_prefix) 229 Nbegidx = len(line.rsplit(None, 1)[0]) 230 data = compfunc(Ntext, line, Nbegidx, endidx) 231 self.completion_matches = [p[to_rm:] for p in data 232 if len(p)>to_rm] 233 except Exception, error: 234 print error 235 else: 236 self.completion_prefix = '' 237 self.completion_matches = compfunc(text, line, begidx, endidx) 238 #print self.completion_matches 239 240 self.completion_matches = [ (l[-1] in [' ','@','=',os.path.sep] 241 and l or (l+' ')) for l in self.completion_matches if l] 242 243 try: 244 return self.completion_matches[state] 245 except IndexError, error: 246 #if __debug__: 247 # print '\n Completion ERROR:' 248 # print error 249 # print '\n' 250 return None
251
252 -class CheckCmd(object):
253 """Extension of the cmd object for only the check command""" 254
255 - def check_history(self, args):
256 """check the validity of line""" 257 258 if len(args) > 1: 259 self.help_history() 260 raise self.InvalidCmd('\"history\" command takes at most one argument') 261 262 if not len(args): 263 return 264 265 if args[0] =='.': 266 if not self._export_dir: 267 raise self.InvalidCmd("No default directory is defined for \'.\' option") 268 elif args[0] != 'clean': 269 dirpath = os.path.dirname(args[0]) 270 if dirpath and not os.path.exists(dirpath) or \ 271 os.path.isdir(args[0]): 272 raise self.InvalidCmd("invalid path %s " % dirpath)
273
274 - def check_save(self, args):
275 """check that the line is compatible with save options""" 276 277 if len(args) > 2: 278 self.help_save() 279 raise self.InvalidCmd, 'too many arguments for save command.' 280 281 if len(args) == 2: 282 if args[0] != 'options': 283 self.help_save() 284 raise self.InvalidCmd, '\'%s\' is not recognized as first argument.' % \ 285 args[0] 286 else: 287 args.pop(0)
288
289 -class HelpCmd(object):
290 """Extension of the cmd object for only the help command""" 291
292 - def help_quit(self):
293 logger.info("syntax: quit") 294 logger.info("-- terminates the application")
295 296 help_EOF = help_quit 297
298 - def help_history(self):
299 logger.info("syntax: history [FILEPATH|clean|.] ") 300 logger.info(" If FILEPATH is \'.\' and \'output\' is done,") 301 logger.info(" Cards/proc_card_mg5.dat will be used.") 302 logger.info(" If FILEPATH is omitted, the history will be output to stdout.") 303 logger.info(" \"clean\" will remove all entries from the history.")
304
305 - def help_help(self):
306 logger.info("syntax: help") 307 logger.info("-- access to the in-line help" )
308
309 - def help_save(self):
310 """help text for save""" 311 logger.info("syntax: save [options] [FILEPATH]") 312 logger.info("-- save options configuration to filepath.")
313
314 - def help_display(self):
315 """help for display command""" 316 logger.info("syntax: display " + "|".join(self._display_opts)) 317 logger.info("-- display a the status of various internal state variables")
318
319 -class CompleteCmd(object):
320 """Extension of the cmd object for only the complete command""" 321
322 - def complete_display(self,text, line, begidx, endidx):
323 args = self.split_arg(line[0:begidx]) 324 # Format 325 if len(args) == 1: 326 return self.list_completion(text, self._display_opts)
327
328 - def complete_history(self, text, line, begidx, endidx):
329 "Complete the history command" 330 331 args = self.split_arg(line[0:begidx]) 332 333 # Directory continuation 334 if args[-1].endswith(os.path.sep): 335 return self.path_completion(text, 336 os.path.join('.',*[a for a in args \ 337 if a.endswith(os.path.sep)])) 338 339 if len(args) == 1: 340 return self.path_completion(text)
341
342 - def complete_save(self, text, line, begidx, endidx):
343 "Complete the save command" 344 345 args = self.split_arg(line[0:begidx]) 346 347 # Format 348 if len(args) == 1: 349 return self.list_completion(text, ['options']) 350 351 # Directory continuation 352 if args[-1].endswith(os.path.sep): 353 return self.path_completion(text, 354 pjoin('.',*[a for a in args if a.endswith(os.path.sep)]), 355 only_dirs = True) 356 357 # Filename if directory is not given 358 if len(args) == 2: 359 return self.path_completion(text)
360
361 -class Cmd(CheckCmd, HelpCmd, CompleteCmd, BasicCmd):
362 """Extension of the cmd.Cmd command line. 363 This extensions supports line breaking, history, comments, 364 internal call to cmdline, path completion,... 365 this class should be MG5 independent""" 366 367 #suggested list of command 368 next_possibility = {} # command : [list of suggested command] 369 history_header = "" 370 371 _display_opts = ['options','variable'] 372
373 - class InvalidCmd(Exception):
374 """expected error for wrong command""" 375 pass
376 377 ConfigurationError = InvalidCmd 378 379 debug_output = 'debug' 380 error_debug = """Please report this bug to developers\n 381 More information is found in '%s'.\n 382 Please attach this file to your report.""" 383 config_debug = error_debug 384 385 keyboard_stop_msg = """stopping all current operation 386 in order to quit the program please enter exit""" 387 388
389 - def __init__(self, *arg, **opt):
390 """Init history and line continuation""" 391 392 self.log = True 393 self.history = [] 394 self.save_line = '' # for line splitting 395 cmd.Cmd.__init__(self, *arg, **opt) 396 self.__initpos = os.path.abspath(os.getcwd()) 397 self.child = None # sub CMD interface call from this one 398 self.mother = None #This CMD interface was called from another one 399 self.inputfile = None # input file (in non interactive mode) 400 self.stored_line = '' # for be able to treat answer to question in input file
401 # answer which are not required. 402 403 404
405 - def precmd(self, line):
406 """ A suite of additional function needed for in the cmd 407 this implement history, line breaking, comment treatment,... 408 """ 409 410 if not line: 411 return line 412 line = line.lstrip() 413 414 # Update the history of this suite of command, 415 # except for useless commands (empty history and help calls) 416 if ';' in line: 417 lines = line.split(';') 418 else: 419 lines = [line] 420 for l in lines: 421 l = l.strip() 422 if not (l.startswith("history") or l.startswith('help') or \ 423 l.startswith('#*')): 424 self.history.append(l) 425 426 # Check if we are continuing a line: 427 if self.save_line: 428 line = self.save_line + line 429 self.save_line = '' 430 431 # Check if the line is complete 432 if line.endswith('\\'): 433 self.save_line = line[:-1] 434 return '' # do nothing 435 436 # Remove comment 437 if '#' in line: 438 line = line.split('#')[0] 439 440 # Deal with line splitting 441 if ';' in line and not (line.startswith('!') or line.startswith('shell')): 442 for subline in line.split(';'): 443 stop = self.onecmd(subline) 444 stop = self.postcmd(stop, subline) 445 return '' 446 447 # execute the line command 448 return line
449
450 - def nice_error_handling(self, error, line):
451 """ """ 452 # Make sure that we are at the initial position 453 if self.child: 454 return self.child.nice_error_handling(error, line) 455 456 os.chdir(self.__initpos) 457 # Create the debug files 458 self.log = False 459 if os.path.exists(self.debug_output): 460 os.remove(self.debug_output) 461 try: 462 cmd.Cmd.onecmd(self, 'history %s' % self.debug_output.replace(' ', '\ ')) 463 except Exception, error: 464 print error 465 466 debug_file = open(self.debug_output, 'a') 467 traceback.print_exc(file=debug_file) 468 # Create a nice error output 469 if self.history and line == self.history[-1]: 470 error_text = 'Command \"%s\" interrupted with error:\n' % line 471 elif self.history: 472 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 473 error_text += '\"%s\" with error:\n' % self.history[-1] 474 else: 475 error_text = '' 476 error_text += '%s : %s\n' % (error.__class__.__name__, 477 str(error).replace('\n','\n\t')) 478 error_text += self.error_debug % {'debug': self.debug_output} 479 logger_stderr.critical(error_text) 480 481 # Add options status to the debug file 482 try: 483 self.do_display('options', debug_file) 484 except Exception, error: 485 debug_file.write('Fail to write options with error %s' % error) 486 487 #stop the execution if on a non interactive mode 488 if self.use_rawinput == False: 489 return True 490 return False
491
492 - def nice_user_error(self, error, line):
493 if self.child: 494 return self.child.nice_user_error(error, line) 495 # Make sure that we are at the initial position 496 os.chdir(self.__initpos) 497 if line == self.history[-1]: 498 error_text = 'Command \"%s\" interrupted with error:\n' % line 499 else: 500 error_text = 'Command \"%s\" interrupted in sub-command:\n' %line 501 error_text += '\"%s\" with error:\n' % self.history[-1] 502 error_text += '%s : %s' % (error.__class__.__name__, 503 str(error).replace('\n','\n\t')) 504 logger_stderr.error(error_text) 505 #stop the execution if on a non interactive mode 506 if self.use_rawinput == False: 507 return True 508 # Remove failed command from history 509 self.history.pop() 510 return False
511
512 - def nice_config_error(self, error, line):
513 if self.child: 514 return self.child.nice_user_error(error, line) 515 # Make sure that we are at the initial position 516 os.chdir(self.__initpos) 517 if not self.history or line == self.history[-1]: 518 error_text = 'Error detected in \"%s\"\n' % line 519 else: 520 error_text = 'Error detected in sub-command %s\n' % self.history[-1] 521 error_text += 'write debug file %s \n' % self.debug_output 522 self.log = False 523 cmd.Cmd.onecmd(self, 'history %s' % self.debug_output) 524 debug_file = open(self.debug_output, 'a') 525 traceback.print_exc(file=debug_file) 526 error_text += self.config_debug % {'debug' :self.debug_output} 527 error_text += '%s : %s' % (error.__class__.__name__, 528 str(error).replace('\n','\n\t')) 529 logger_stderr.error(error_text) 530 531 # Add options status to the debug file 532 try: 533 self.do_display('options', debug_file) 534 except Exception, error: 535 debug_file.write('Fail to write options with error %s' % error) 536 #stop the execution if on a non interactive mode 537 if self.use_rawinput == False: 538 return True 539 # Remove failed command from history 540 if self.history: 541 self.history.pop() 542 return False
543 544
545 - def onecmd(self, line):
546 """catch all error and stop properly command accordingly""" 547 548 try: 549 return cmd.Cmd.onecmd(self, line) 550 except self.InvalidCmd as error: 551 if __debug__: 552 self.nice_error_handling(error, line) 553 else: 554 self.nice_user_error(error, line) 555 except self.ConfigurationError as error: 556 self.nice_config_error(error, line) 557 except Exception as error: 558 self.nice_error_handling(error, line) 559 if self.mother: 560 self.do_quit('') 561 except KeyboardInterrupt as error: 562 self.stop_on_keyboard_stop() 563 #self.nice_error_handling(error, line) 564 print self.keyboard_stop_msg
565
566 - def stop_on_keyboard_stop(self):
567 """action to perform to close nicely on a keyboard interupt""" 568 pass # dummy function
569
570 - def exec_cmd(self, line, errorhandling=False, printcmd=True, 571 precmd=False, postcmd=True):
572 """for third party call, call the line with pre and postfix treatment 573 without global error handling """ 574 575 if printcmd: 576 logger.info(line) 577 if self.child: 578 current_interface = self.child 579 else: 580 current_interface = self 581 582 if precmd: 583 line = current_interface.precmd(line) 584 if errorhandling: 585 stop = current_interface.onecmd(line) 586 else: 587 stop = cmd.Cmd.onecmd(current_interface, line) 588 if postcmd: 589 stop = current_interface.postcmd(stop, line) 590 return stop
591
592 - def run_cmd(self, line):
593 """for third party call, call the line with pre and postfix treatment 594 with global error handling""" 595 596 return self.exec_cmd(line, errorhandling=True, precmd=True)
597
598 - def emptyline(self):
599 """If empty line, do nothing. Default is repeat previous command.""" 600 pass
601
602 - def default(self, line):
603 """Default action if line is not recognized""" 604 605 # Faulty command 606 logger.warning("Command \"%s\" not recognized, please try again" % \ 607 line.split()[0])
608 609 610 @staticmethod
611 - def split_arg(line):
612 """Split a line of arguments""" 613 614 split = line.split() 615 out=[] 616 tmp='' 617 for data in split: 618 if data[-1] == '\\': 619 tmp += data[:-1]+' ' 620 elif tmp: 621 tmp += data 622 tmp = os.path.expanduser(os.path.expandvars(tmp)) 623 out.append(tmp) 624 else: 625 out.append(data) 626 return out
627
628 - def correct_splitting(line):
629 """if the line finish with a '-' the code splits in a weird way 630 on GNU_SPLITTING""" 631 632 line = line.lstrip() 633 if line[-1] in [' ','\t']: 634 return '', line, len(line),len(enidx) 635 return text, line, begidx, endidx
636 637 638 639 640 # Write the list of command line use in this session
641 - def do_history(self, line):
642 """write in a file the suite of command that was used""" 643 644 args = self.split_arg(line) 645 # Check arguments validity 646 self.check_history(args) 647 648 if len(args) == 0: 649 print '\n'.join(self.history) 650 return 651 elif args[0] == 'clean': 652 self.history = [] 653 logger.info('History is cleaned') 654 return 655 elif args[0] == '.': 656 output_file = os.path.join(self._export_dir, 'Cards', \ 657 'proc_card_mg5.dat') 658 output_file = open(output_file, 'w') 659 else: 660 output_file = open(args[0], 'w') 661 662 # Create the command file 663 text = self.get_history_header() 664 text += ('\n'.join(self.history) + '\n') 665 666 #write this information in a file 667 output_file.write(text) 668 output_file.close() 669 670 if self.log: 671 logger.info("History written to " + output_file.name)
672
673 - def clean_history(self, to_keep=['set','add','load'], 674 remove_bef_lb1=None, 675 to_remove=['open','display','launch'], 676 keep_last=False):
677 """Remove all commands in arguments from history""" 678 679 680 nline = -1 681 last = 0 682 while nline > -len(self.history): 683 684 if remove_bef_lb1 and self.history[nline].startswith(remove_bef_lb1): 685 if last: 686 last = 2 687 self.history.pop(nline) 688 continue 689 else: 690 last=1 691 if nline == -1 and keep_last: 692 nline -=1 693 continue 694 if any([self.history[nline].startswith(arg) for arg in to_remove]): 695 self.history.pop(nline) 696 continue 697 if last == 2: 698 if not any([self.history[nline].startswith(arg) for arg in to_keep]): 699 self.history.pop(nline) 700 continue 701 else: 702 nline -= 1 703 else: 704 nline -= 1
705
706 - def import_command_file(self, filepath):
707 # remove this call from history 708 if self.history: 709 self.history.pop() 710 711 # Read the lines of the file and execute them 712 self.inputfile = open(filepath) 713 for line in self.inputfile: 714 #remove pointless spaces and \n 715 line = line.replace('\n', '').strip() 716 # execute the line 717 if line: 718 self.exec_cmd(line, precmd=True) 719 if self.stored_line: # created by intermediate question 720 self.exec_cmd(self.stored_line, precmd=True) 721 self.stored_line = '' 722 # If a child was open close it 723 if self.child: 724 self.child.exec_cmd('quit') 725 726 return
727
728 - def get_history_header(self):
729 """Default history header""" 730 731 return self.history_header
732
733 - def define_child_cmd_interface(self, obj_instance, interface=True):
734 """Define a sub cmd_interface""" 735 736 # We are in a file reading mode. So we need to redirect the cmd 737 self.child = obj_instance 738 self.child.mother = self 739 740 if self.use_rawinput and interface: 741 # We are in interactive mode -> simply call the child 742 obj_instance.cmdloop() 743 stop = obj_instance.postloop() 744 return stop 745 if self.inputfile: 746 # we are in non interactive mode -> so pass the line information 747 obj_instance.inputfile = self.inputfile 748 749 if not interface: 750 return self.child
751
752 - def postloop(self):
753 """ """ 754 755 args = self.split_arg(self.lastcmd) 756 if args and args[0] in ['quit','exit']: 757 if 'all' in args: 758 return True 759 if len(args) >1 and args[1].isdigit(): 760 if args[1] not in ['0', '1']: 761 return True 762 return False
763 764 #=============================================================================== 765 # Ask a question with a maximum amount of time to answer 766 #=============================================================================== 767 @staticmethod
768 - def timed_input(question, default, timeout=None, noerror=True, fct=None, 769 fct_timeout=None):
770 """ a question with a maximal time to answer take default otherwise""" 771 772 def handle_alarm(signum, frame): 773 raise TimeOutError
774 775 signal.signal(signal.SIGALRM, handle_alarm) 776 777 if fct is None: 778 fct = raw_input 779 780 if timeout: 781 signal.alarm(timeout) 782 question += '[%ss to answer] ' % (timeout) 783 try: 784 result = fct(question) 785 except TimeOutError: 786 if noerror: 787 print '\nuse %s' % default 788 if fct_timeout: 789 fct_timeout(True) 790 return default 791 else: 792 signal.alarm(0) 793 raise 794 finally: 795 signal.alarm(0) 796 if fct_timeout: 797 fct_timeout(False) 798 return result
799 800 801 #=============================================================================== 802 # Ask a question with nice options handling 803 #===============================================================================
804 - def ask(self, question, default, choices=[], path_msg=None, 805 timeout = True, fct_timeout=None):
806 """ ask a question with some pre-define possibility 807 path info is 808 """ 809 810 if path_msg: 811 path_msg = [path_msg] 812 else: 813 path_msg = [] 814 815 if timeout: 816 try: 817 timeout = self.options['timeout'] 818 except: 819 pass 820 821 # add choice info to the question 822 if choices + path_msg: 823 question += ' [' 824 question += "\033[%dm%s\033[0m, " % (4, default) 825 for data in choices[:9] + path_msg: 826 if default == data: 827 continue 828 else: 829 question += "%s, " % data 830 831 if len(choices) > 9: 832 question += '... , ' 833 question = question[:-2]+']' 834 835 if path_msg: 836 f = lambda q: raw_path_input(q, allow_arg=choices, default=default) 837 else: 838 f = lambda q: smart_input(q, allow_arg=choices, default=default) 839 840 answer = self.check_answer_in_input_file(choices, path_msg) 841 if answer is not None: 842 return answer 843 844 value = Cmd.timed_input(question, default, timeout=timeout, 845 fct=f, fct_timeout=fct_timeout) 846 847 return value
848
849 - def check_answer_in_input_file(self, options, path=False):
850 """Questions can have answer in output file (or not)""" 851 852 if self.check_stored_line(): 853 return None # already one line not correctly answer 854 855 if not self.inputfile: 856 return None# interactive mode 857 858 try: 859 line = self.inputfile.next() 860 except StopIteration: 861 return None 862 line = line.replace('\n','').strip() 863 if '#' in line: 864 line = line.split('#')[0] 865 if not line: 866 return self.check_answer_in_input_file(options, path) 867 868 if line in options: 869 return line 870 elif path and os.path.exists(line): 871 return line 872 else: 873 self.store_line(line) 874 return None
875
876 - def store_line(self, line):
877 """store a line of the input file which should be executed by the higher mother""" 878 879 if self.mother: 880 self.mother.store_line(line) 881 else: 882 self.stored_line = line
883
884 - def check_stored_line(self):
885 if self.mother: 886 return self.mother.check_stored_line() 887 else: 888 return self.stored_line
889 890 # Quit
891 - def do_quit(self, line):
892 """ exit the mainloop() """ 893 894 if self.child: 895 self.child.exec_cmd('quit ' + line, printcmd=False) 896 return 897 elif self.mother: 898 self.mother.child = None 899 if line == 'all': 900 pass 901 elif line: 902 level = int(line) - 1 903 if level: 904 self.mother.lastcmd = 'quit %s' % level 905 906 return True
907 908 # Aliases 909 do_EOF = do_quit 910 do_exit = do_quit 911 912 913 914 915
916 - def do_help(self, line):
917 """ propose some usefull possible action """ 918 919 # if they are an argument use the default help 920 if line: 921 return cmd.Cmd.do_help(self, line) 922 923 924 names = self.get_names() 925 cmds = {} 926 names.sort() 927 # There can be duplicates if routines overridden 928 prevname = '' 929 for name in names: 930 if name[:3] == 'do_': 931 if name == prevname: 932 continue 933 prevname = name 934 cmdname=name[3:] 935 try: 936 doc = getattr(self.cmd, name).__doc__ 937 except: 938 doc = None 939 if not doc: 940 doc = getattr(self, name).__doc__ 941 if not doc: 942 tag = "Documented commands" 943 elif ':' in doc: 944 tag = doc.split(':',1)[0] 945 else: 946 tag = "Documented commands" 947 if tag in cmds: 948 cmds[tag].append(cmdname) 949 else: 950 cmds[tag] = [cmdname] 951 952 953 self.stdout.write("%s\n"%str(self.doc_leader)) 954 tag = "Documented commands" 955 header = "%s (type help <topic>):" % tag 956 self.print_topics(header, cmds[tag], 15,80) 957 for name, item in cmds.items(): 958 if name == "Documented commands": 959 continue 960 if name == "Not in help": 961 continue 962 header = "%s (type help <topic>):" % name 963 self.print_topics(header, item, 15,80) 964 965 966 ## Add contextual help 967 if len(self.history) == 0: 968 last_action_2 = last_action = 'start' 969 else: 970 last_action_2 = last_action = 'none' 971 972 pos = 0 973 authorize = self.next_possibility.keys() 974 while last_action_2 not in authorize and last_action not in authorize: 975 pos += 1 976 if pos > len(self.history): 977 last_action_2 = last_action = 'start' 978 break 979 980 args = self.history[-1 * pos].split() 981 last_action = args[0] 982 if len(args)>1: 983 last_action_2 = '%s %s' % (last_action, args[1]) 984 else: 985 last_action_2 = 'none' 986 987 print 'Contextual Help' 988 print '===============' 989 if last_action_2 in authorize: 990 options = self.next_possibility[last_action_2] 991 elif last_action in authorize: 992 options = self.next_possibility[last_action] 993 994 text = 'The following command(s) may be useful in order to continue.\n' 995 for option in options: 996 text+='\t %s \n' % option 997 print text
998
999 - def do_display(self, line, output=sys.stdout):
1000 """Advanced commands: basic display""" 1001 1002 args = self.split_arg(line) 1003 #check the validity of the arguments 1004 1005 if len(args) == 0: 1006 self.help_display() 1007 raise self.InvalidCmd, 'display require at least one argument' 1008 1009 if args[0] == "options": 1010 outstr = "Value of current Options:\n" 1011 for key, value in self.options.items(): 1012 outstr += '%25s \t:\t%s\n' %(key,value) 1013 output.write(outstr) 1014 1015 elif args[0] == "variable": 1016 outstr = "Value of Internal Variable:\n" 1017 try: 1018 var = eval(args[1]) 1019 except: 1020 outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1] 1021 else: 1022 outstr += 'GLOBAL:\n' 1023 outstr += misc.nice_representation(var, nb_space=4) 1024 1025 try: 1026 var = eval('self.%s' % args[1]) 1027 except: 1028 outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1] 1029 else: 1030 outstr += 'LOCAL:\n' 1031 outstr += misc.nice_representation(var, nb_space=4) 1032 1033 pydoc.pager(outstr)
1034 1035
1036 - def do_save(self, line, check=True):
1037 """Save the configuration file""" 1038 1039 args = self.split_arg(line) 1040 # Check argument validity 1041 if check: 1042 Cmd.check_save(self, args) 1043 1044 # find base file for the configuration 1045 if'HOME' in os.environ and os.environ['HOME'] and \ 1046 os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')): 1047 base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt') 1048 if hasattr(self, 'me_dir'): 1049 basedir = self.me_dir 1050 elif not MADEVENT: 1051 basedir = MG5DIR 1052 else: 1053 basedir = os.getcwd() 1054 elif MADEVENT: 1055 # launch via ./bin/madevent 1056 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1057 basedir = self.me_dir 1058 else: 1059 if hasattr(self, 'me_dir'): 1060 base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt') 1061 if len(args) == 0 and os.path.exists(base): 1062 self.write_configuration(base, base, self.me_dir) 1063 base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt') 1064 basedir = MG5DIR 1065 1066 if len(args) == 0: 1067 args.append(base) 1068 self.write_configuration(args[0], base, basedir)
1069
1070 - def write_configuration(self, filepath, basefile, basedir):
1071 """Write the configuration file""" 1072 # We use the default configuration file as a template. 1073 # to ensure that all configuration information are written we 1074 # keep track of all key that we need to write. 1075 1076 logger.info('save configuration file to %s' % filepath) 1077 to_write = self.options.keys()[:] 1078 text = "" 1079 # Use local configuration => Need to update the path 1080 for line in file(basefile): 1081 if '#' in line: 1082 data, comment = line.split('#',1) 1083 else: 1084 data, comment = line, '' 1085 data = data.split('=') 1086 if len(data) !=2: 1087 text += line 1088 continue 1089 key = data[0].strip() 1090 if key in self.options: 1091 value = str(self.options[key]) 1092 else: 1093 value = data[1].strip() 1094 try: 1095 to_write.remove(key) 1096 except: 1097 pass 1098 if '_path' in key: 1099 # special case need to update path 1100 # check if absolute path 1101 if value.startswith('./'): 1102 value = os.path.realpath(os.path.join(basedir, value)) 1103 text += '%s = %s # %s \n' % (key, value, comment) 1104 for key in to_write: 1105 if key in self.options: 1106 text += '%s = %s \n' % (key,self.options[key]) 1107 else: 1108 text += '%s = %s \n' % (key,self.options[key]) 1109 writer = open(filepath,'w') 1110 writer.write(text) 1111 writer.close()
1112 1113 1114 1115 1116 @staticmethod
1117 - def list_completion(text, list, line=''):
1118 """Propose completions of text in list""" 1119 1120 if not text: 1121 completions = list 1122 else: 1123 completions = [ f 1124 for f in list 1125 if f.startswith(text) 1126 ] 1127 1128 return completions
1129 1130 1131 @staticmethod
1132 - def path_completion(text, base_dir = None, only_dirs = False, 1133 relative=True):
1134 """Propose completions of text to compose a valid path""" 1135 1136 if base_dir is None: 1137 base_dir = os.getcwd() 1138 1139 base_dir = os.path.expanduser(os.path.expandvars(base_dir)) 1140 1141 prefix, text = os.path.split(text) 1142 base_dir = os.path.join(base_dir, prefix) 1143 if prefix: 1144 prefix += os.path.sep 1145 1146 if only_dirs: 1147 completion = [prefix + f 1148 for f in os.listdir(base_dir) 1149 if f.startswith(text) and \ 1150 os.path.isdir(os.path.join(base_dir, f)) and \ 1151 (not f.startswith('.') or text.startswith('.')) 1152 ] 1153 else: 1154 completion = [ prefix + f 1155 for f in os.listdir(base_dir) 1156 if f.startswith(text) and \ 1157 os.path.isfile(os.path.join(base_dir, f)) and \ 1158 (not f.startswith('.') or text.startswith('.')) 1159 ] 1160 1161 completion = completion + \ 1162 [prefix + f + os.path.sep 1163 for f in os.listdir(base_dir) 1164 if f.startswith(text) and \ 1165 os.path.isdir(os.path.join(base_dir, f)) and \ 1166 (not f.startswith('.') or text.startswith('.')) 1167 ] 1168 1169 if relative: 1170 completion += [prefix + f for f in ['.'+os.path.sep, '..'+os.path.sep] if \ 1171 f.startswith(text) and not prefix.startswith('.')] 1172 1173 completion = [a.replace(' ','\ ') for a in completion] 1174 return completion
1175
1176 1177 1178 1179 -class CmdShell(Cmd):
1180 """CMD command with shell activate""" 1181 1182 # Access to shell
1183 - def do_shell(self, line):
1184 "Run a shell command" 1185 1186 if line.strip() is '': 1187 self.help_shell() 1188 else: 1189 logging.info("running shell command: " + line) 1190 subprocess.call(line, shell=True)
1191
1192 - def complete_shell(self, text, line, begidx, endidx):
1193 """ add path for shell """ 1194 1195 # Filename if directory is given 1196 # 1197 if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep: 1198 if not text: 1199 text = '' 1200 output = self.path_completion(text, 1201 base_dir=\ 1202 self.split_arg(line[0:begidx])[-1]) 1203 else: 1204 output = self.path_completion(text) 1205 return output
1206
1207 - def help_shell(self):
1208 """help for the shell""" 1209 1210 logger.info("syntax: shell CMD (or ! CMD)") 1211 logger.info("-- run the shell command CMD and catch output")
1212
1213 1214 1215 1216 #=============================================================================== 1217 # Question with auto-completion 1218 #=============================================================================== 1219 -class SmartQuestion(BasicCmd):
1220 """ a class for answering a question with the path autocompletion""" 1221
1222 - def preloop(self):
1223 """Initializing before starting the main loop""" 1224 self.prompt = '' 1225 self.value = None 1226 BasicCmd.preloop(self)
1227
1228 - def __init__(self, allow_arg=[], default=None, *arg, **opt):
1229 self.wrong_answer = 0 # forbids infinite loop 1230 self.allow_arg = [str(a) for a in allow_arg] 1231 self.history_header = '' 1232 self.default_value = str(default) 1233 cmd.Cmd.__init__(self, *arg, **opt)
1234
1235 - def completenames(self, text, line, *ignored):
1236 prev_timer = signal.alarm(0) # avoid timer if any 1237 if prev_timer: 1238 nb_back = len(line) 1239 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 1240 self.stdout.write(line) 1241 self.stdout.flush() 1242 try: 1243 return Cmd.list_completion(text, self.allow_arg) 1244 except Exception, error: 1245 print error
1246
1247 - def default(self, line):
1248 """Default action if line is not recognized""" 1249 1250 if line.strip() == '' and self.default_value is not None: 1251 self.value = self.default_value 1252 else: 1253 self.value = line
1254
1255 - def emptyline(self):
1256 """If empty line, return default""" 1257 1258 if self.default_value is not None: 1259 self.value = self.default_value
1260
1261 - def postcmd(self, stop, line):
1262 1263 try: 1264 if self.value in self.allow_arg: 1265 return True 1266 elif str(self.value) == 'EOF': 1267 self.value = self.default_value 1268 return True 1269 else: 1270 raise Exception 1271 except Exception: 1272 if self.wrong_answer < 100: 1273 self.wrong_answer += 1 1274 print """%s not valid argument. Valid argument are in (%s).""" \ 1275 % (self.value,','.join(self.allow_arg)) 1276 print 'please retry' 1277 return False 1278 else: 1279 self.value = self.default_value 1280 return True
1281
1282 - def cmdloop(self, intro=None):
1283 cmd.Cmd.cmdloop(self, intro) 1284 return self.value
1285
1286 # a function helper 1287 -def smart_input(input_text, allow_arg=[], default=None):
1288 print input_text 1289 obj = SmartQuestion(allow_arg=allow_arg, default=default) 1290 return obj.cmdloop()
1291
1292 #=============================================================================== 1293 # Question in order to return a path with auto-completion 1294 #=============================================================================== 1295 -class OneLinePathCompletion(SmartQuestion):
1296 """ a class for answering a question with the path autocompletion""" 1297 1298
1299 - def completenames(self, text, line, begidx, endidx):
1300 prev_timer = signal.alarm(0) # avoid timer if any 1301 if prev_timer: 1302 nb_back = len(line) 1303 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 1304 self.stdout.write(line) 1305 self.stdout.flush() 1306 try: 1307 out = {} 1308 out[' Options'] = SmartQuestion.completenames(self, text, line) 1309 out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False) 1310 1311 return self.deal_multiple_categories(out) 1312 except Exception, error: 1313 print error
1314
1315 - def completedefault(self,text, line, begidx, endidx):
1316 prev_timer = signal.alarm(0) # avoid timer if any 1317 if prev_timer: 1318 nb_back = len(line) 1319 self.stdout.write('\b'*nb_back + '[timer stopped]\n') 1320 self.stdout.write(line) 1321 self.stdout.flush() 1322 try: 1323 args = Cmd.split_arg(line[0:begidx]) 1324 except Exception, error: 1325 print error 1326 1327 # Directory continuation 1328 if args[-1].endswith(os.path.sep): 1329 1330 return Cmd.path_completion(text, 1331 os.path.join('.',*[a for a in args \ 1332 if a.endswith(os.path.sep)])) 1333 self.completenames(line+text)
1334 1335
1336 - def postcmd(self, stop, line):
1337 1338 try: 1339 if self.value in self.allow_arg: 1340 return True 1341 elif os.path.isfile(self.value): 1342 return os.path.relpath(self.value) 1343 else: 1344 raise Exception 1345 except Exception, error: 1346 print """not valid argument. Valid argument are file path or value in (%s).""" \ 1347 % ','.join(self.allow_arg) 1348 print 'please retry' 1349 return False
1350
1351 # a function helper 1352 -def raw_path_input(input_text, allow_arg=[], default=None):
1353 print input_text 1354 obj = OneLinePathCompletion(allow_arg=allow_arg, default=default ) 1355 return obj.cmdloop()
1356
1357 #=============================================================================== 1358 # 1359 #=============================================================================== 1360 -class CmdFile(file):
1361 """ a class for command input file -in order to debug cmd \n problem""" 1362
1363 - def __init__(self, name, opt='rU'):
1364 1365 file.__init__(self, name, opt) 1366 self.text = file.read(self) 1367 self.close() 1368 self.lines = self.text.split('\n')
1369
1370 - def readline(self, *arg, **opt):
1371 """readline method treating correctly a line whithout \n at the end 1372 (add it) 1373 """ 1374 if self.lines: 1375 line = self.lines.pop(0) 1376 else: 1377 return '' 1378 1379 if line.endswith('\n'): 1380 return line 1381 else: 1382 return line + '\n'
1383
1384 - def __next__(self):
1385 return self.lines.__next__()
1386
1387 - def __iter__(self):
1388 return self.lines.__iter__()
1389