1
################################################################################
3
# Copyright (c) 2011 The MadGraph Development team and Contributors
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.
9
# It is subject to the MadGraph license which should accompany this
12
# For more information, please visit: http://madgraph.phys.ucl.ac.be
14
################################################################################
15
""" A file containing different extension of the cmd basic python library"""
29
GNU_SPLITTING = ('GNU' in readline.__doc__)
35
logger = logging.getLogger('cmdprint') # for stdout
36
logger_stderr = logging.getLogger('fatalerror') # for stderr
39
import madgraph.various.misc as misc
40
from madgraph import MG5DIR
42
except ImportError, error:
44
import internal.misc as misc
51
class TimeOutError(Exception):
52
"""Class for run-time error"""
54
def debug(debug_only=True):
58
if debug_only and not __debug__:
61
def deco_f(*args, **opt):
63
return f(*args, **opt)
64
except Exception, error:
66
logger.error(traceback.print_exc(file=sys.stdout))
72
#===============================================================================
74
#===============================================================================
75
class BasicCmd(cmd.Cmd):
76
"""Simple extension for the readline"""
79
if readline and not 'libedit' in readline.__doc__:
80
readline.set_completion_display_matches_hook(self.print_suggestions)
82
def deal_multiple_categories(self, dico):
83
"""convert the multiple category in a formatted list understand by our
84
specific readline parser"""
86
if 'libedit' in readline.__doc__:
87
# No parser in this case, just send all the valid options
89
for name, opt in dico.items():
93
# That's the real work
96
# if the key starts with number order the key with that number.
97
for name, opt in dico.items():
100
name = name.replace(' ', '_')
102
out.append(opt[0].rstrip()+'@@'+name+'@@')
117
def print_suggestions(self, substitution, matches, longest_match_length) :
118
"""print auto-completions by category"""
119
longest_match_length += len(self.completion_prefix)
121
if len(matches) == 1:
122
self.stdout.write(matches[0]+' ')
124
self.stdout.write('\n')
125
l2 = [a[-2:] for a in matches]
127
nb_column = self.getTerminalSize()//(longest_match_length+1)
129
for val in self.completion_matches:
130
if val.endswith('@@'):
131
category = val.rsplit('@@',2)[1]
132
category = category.replace('_',' ')
133
self.stdout.write('\n %s:\n%s\n' % (category, '=' * (len(category)+2)))
137
elif pos and pos % nb_column ==0:
138
self.stdout.write('\n')
139
self.stdout.write(self.completion_prefix + val + \
140
' ' * (longest_match_length +1 -len(val)))
142
self.stdout.write('\n')
145
nb_column = self.getTerminalSize()//(longest_match_length+1)
146
for i,val in enumerate(matches):
147
if i and i%nb_column ==0:
148
self.stdout.write('\n')
149
self.stdout.write(self.completion_prefix + val + \
150
' ' * (longest_match_length +1 -len(val)))
151
self.stdout.write('\n')
153
self.stdout.write(self.prompt+readline.get_line_buffer())
155
except Exception, error:
159
def getTerminalSize(self):
160
def ioctl_GWINSZ(fd):
162
import fcntl, termios, struct
163
cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
168
cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
171
fd = os.open(os.ctermid(), os.O_RDONLY)
172
cr = ioctl_GWINSZ(fd)
178
cr = (os.environ['LINES'], os.environ['COLUMNS'])
183
def complete(self, text, state):
184
"""Return the next possible completion for 'text'.
185
If a command has not been entered, then complete against command list.
186
Otherwise try to call complete_<command> to get list of completions.
191
origline = readline.get_line_buffer()
192
line = origline.lstrip()
193
stripped = len(origline) - len(line)
194
begidx = readline.get_begidx() - stripped
195
endidx = readline.get_endidx() - stripped
198
begin, line = line.rsplit(';',1)
199
begidx = begidx - len(begin) - 1
200
endidx = endidx - len(begin) - 1
201
if line[:begidx] == ' ' * begidx:
205
cmd, args, foo = self.parseline(line)
207
compfunc = self.completedefault
210
compfunc = getattr(self, 'complete_' + cmd)
211
except AttributeError:
212
compfunc = self.completedefault
214
compfunc = self.completenames
216
# correct wrong splittion with '\ '
217
if line and begidx > 2 and line[begidx-2:begidx] == '\ ':
218
Ntext = line.split(os.path.sep)[-1]
219
self.completion_prefix = Ntext.rsplit('\ ', 1)[0] + '\ '
220
to_rm = len(self.completion_prefix) - 1
221
Nbegidx = len(line.rsplit(os.path.sep, 1)[0]) + 1
222
data = compfunc(Ntext.replace('\ ', ' '), line, Nbegidx, endidx)
223
self.completion_matches = [p[to_rm:] for p in data
225
# correct wrong splitting with '-'
226
elif line and line[begidx-1] == '-':
228
Ntext = line.split()[-1]
229
self.completion_prefix = Ntext.rsplit('-',1)[0] +'-'
230
to_rm = len(self.completion_prefix)
231
Nbegidx = len(line.rsplit(None, 1)[0])
232
data = compfunc(Ntext, line, Nbegidx, endidx)
233
self.completion_matches = [p[to_rm:] for p in data
235
except Exception, error:
238
self.completion_prefix = ''
239
self.completion_matches = compfunc(text, line, begidx, endidx)
240
#print self.completion_matches
242
self.completion_matches = [ (l[-1] in [' ','@','=',os.path.sep]
243
and l or (l+' ')) for l in self.completion_matches if l]
246
return self.completion_matches[state]
247
except IndexError, error:
249
# logger.error('\n Completion ERROR:')
250
# logger.error( error)
255
"""Split a line of arguments"""
265
tmp = os.path.expanduser(os.path.expandvars(tmp))
272
def list_completion(text, list, line=''):
273
"""Propose completions of text in list"""
280
if f.startswith(text)
287
def path_completion(text, base_dir = None, only_dirs = False,
289
"""Propose completions of text to compose a valid path"""
292
base_dir = os.getcwd()
293
base_dir = os.path.expanduser(os.path.expandvars(base_dir))
297
prefix, text = os.path.split(text)
298
prefix = os.path.expanduser(os.path.expandvars(prefix))
299
base_dir = os.path.join(base_dir, prefix)
301
prefix += os.path.sep
304
completion = [prefix + f
305
for f in os.listdir(base_dir)
306
if f.startswith(text) and \
307
os.path.isdir(os.path.join(base_dir, f)) and \
308
(not f.startswith('.') or text.startswith('.'))
311
completion = [ prefix + f
312
for f in os.listdir(base_dir)
313
if f.startswith(text) and \
314
os.path.isfile(os.path.join(base_dir, f)) and \
315
(not f.startswith('.') or text.startswith('.'))
318
completion = completion + \
319
[prefix + f + os.path.sep
320
for f in os.listdir(base_dir)
321
if f.startswith(text) and \
322
os.path.isdir(os.path.join(base_dir, f)) and \
323
(not f.startswith('.') or text.startswith('.'))
327
completion += [prefix + f for f in ['.'+os.path.sep, '..'+os.path.sep] if \
328
f.startswith(text) and not prefix.startswith('.')]
330
completion = [a.replace(' ','\ ') for a in completion]
336
class CheckCmd(object):
337
"""Extension of the cmd object for only the check command"""
339
def check_history(self, args):
340
"""check the validity of line"""
344
raise self.InvalidCmd('\"history\" command takes at most one argument')
350
if not self._export_dir:
351
raise self.InvalidCmd("No default directory is defined for \'.\' option")
352
elif args[0] != 'clean':
353
dirpath = os.path.dirname(args[0])
354
if dirpath and not os.path.exists(dirpath) or \
355
os.path.isdir(args[0]):
356
raise self.InvalidCmd("invalid path %s " % dirpath)
358
def check_save(self, args):
359
"""check that the line is compatible with save options"""
363
raise self.InvalidCmd, 'too many arguments for save command.'
366
if args[0] != 'options':
368
raise self.InvalidCmd, '\'%s\' is not recognized as first argument.' % \
373
class HelpCmd(object):
374
"""Extension of the cmd object for only the help command"""
377
logger.info("-- terminates the application",'$MG:color:BLUE')
378
logger.info("syntax: quit",'$MG:color:BLACK')
382
def help_history(self):
383
logger.info("-- interact with the command history.",'$MG:color:BLUE')
384
logger.info("syntax: history [FILEPATH|clean|.] ",'$MG:color:BLACK')
385
logger.info(" > If FILEPATH is \'.\' and \'output\' is done,")
386
logger.info(" Cards/proc_card_mg5.dat will be used.")
387
logger.info(" > If FILEPATH is omitted, the history will be output to stdout.")
388
logger.info(" \"clean\" will remove all entries from the history.")
391
logger.info("-- access to the in-line help",'$MG:color:BLUE')
392
logger.info("syntax: help",'$MG:color:BLACK')
395
"""help text for save"""
396
logger.info("-- save options configuration to filepath.",'$MG:color:BLUE')
397
logger.info("syntax: save [options] [FILEPATH]",'$MG:color:BLACK')
399
def help_display(self):
400
"""help for display command"""
401
logger.info("-- display a the status of various internal state variables",'$MG:color:BLUE')
402
logger.info("syntax: display " + "|".join(self._display_opts),'$MG:color:BLACK')
404
class CompleteCmd(object):
405
"""Extension of the cmd object for only the complete command"""
407
def complete_display(self,text, line, begidx, endidx):
408
args = self.split_arg(line[0:begidx])
411
return self.list_completion(text, self._display_opts)
413
def complete_history(self, text, line, begidx, endidx):
414
"Complete the history command"
416
args = self.split_arg(line[0:begidx])
418
# Directory continuation
419
if args[-1].endswith(os.path.sep):
420
return self.path_completion(text,
421
os.path.join('.',*[a for a in args \
422
if a.endswith(os.path.sep)]))
425
return self.path_completion(text)
427
def complete_save(self, text, line, begidx, endidx):
428
"Complete the save command"
430
args = self.split_arg(line[0:begidx])
434
return self.list_completion(text, ['options'])
436
# Directory continuation
437
if args[-1].endswith(os.path.sep):
438
return self.path_completion(text,
439
pjoin('.',*[a for a in args if a.endswith(os.path.sep)]),
442
# Filename if directory is not given
444
return self.path_completion(text)
446
class Cmd(CheckCmd, HelpCmd, CompleteCmd, BasicCmd):
447
"""Extension of the cmd.Cmd command line.
448
This extensions supports line breaking, history, comments,
449
internal call to cmdline, path completion,...
450
this class should be MG5 independent"""
452
#suggested list of command
453
next_possibility = {} # command : [list of suggested command]
456
_display_opts = ['options','variable']
458
class InvalidCmd(Exception):
459
"""expected error for wrong command"""
462
ConfigurationError = InvalidCmd
464
debug_output = 'debug'
465
error_debug = """Please report this bug to developers\n
466
More information is found in '%(debug)s'.\n
467
Please attach this file to your report."""
468
config_debug = error_debug
470
keyboard_stop_msg = """stopping all current operation
471
in order to quit the program please enter exit"""
474
def __init__(self, *arg, **opt):
475
"""Init history and line continuation"""
479
self.save_line = '' # for line splitting
480
cmd.Cmd.__init__(self, *arg, **opt)
481
self.__initpos = os.path.abspath(os.getcwd())
482
self.child = None # sub CMD interface call from this one
483
self.mother = None #This CMD interface was called from another one
484
self.inputfile = None # input file (in non interactive mode)
485
self.haspiping = not sys.stdin.isatty() # check if mg5 is piped
486
self.stored_line = '' # for be able to treat answer to question in input file
487
# answer which are not required.
488
if not hasattr(self, 'helporder'):
489
self.helporder = ['Documented commands']
492
def precmd(self, line):
493
""" A suite of additional function needed for in the cmd
494
this implement history, line breaking, comment treatment,...
501
# Check if we are continuing a line:
503
line = self.save_line + line
506
# Check if the line is complete
507
if line.endswith('\\'):
508
self.save_line = line[:-1]
509
return '' # do nothing
513
line = line.split('#')[0]
515
# Deal with line splitting
517
lines = line.split(';')
518
for subline in lines:
519
if not (subline.startswith("history") or subline.startswith('help') \
520
or subline.startswith('#*')):
521
self.history.append(subline)
522
stop = self.onecmd(subline)
523
stop = self.postcmd(stop, subline)
526
# execute the line command
527
self.history.append(line)
530
def postcmd(self,stop, line):
531
""" finishing a command
532
This looks if the command add a special post part."""
536
cmd, subline = line.split(None, 1)
540
if hasattr(self,'post_%s' %cmd):
541
stop = getattr(self, 'post_%s' % cmd)(stop, subline)
544
def define_child_cmd_interface(self, obj_instance, interface=True):
545
"""Define a sub cmd_interface"""
547
# We are in a file reading mode. So we need to redirect the cmd
548
self.child = obj_instance
549
self.child.mother = self
551
if self.use_rawinput and interface:
552
# We are in interactive mode -> simply call the child
553
obj_instance.cmdloop()
554
stop = obj_instance.postloop()
557
# we are in non interactive mode -> so pass the line information
558
obj_instance.inputfile = self.inputfile
563
#===============================================================================
564
# Ask a question with nice options handling
565
#===============================================================================
566
def ask(self, question, default, choices=[], path_msg=None,
567
timeout = True, fct_timeout=None, ask_class=None, **opt):
568
""" ask a question with some pre-define possibility
572
path_msg = [path_msg]
578
timeout = self.options['timeout']
582
# add choice info to the question
583
if choices + path_msg:
585
question += "\033[%dm%s\033[0m, " % (4, default)
586
for data in choices[:9] + path_msg:
590
question += "%s, " % data
594
question = question[:-2]+']'
596
question += "[\033[%dm%s\033[0m] " % (4, default)
600
obj = OneLinePathCompletion
604
question_instance = obj(question, allow_arg=choices, default=default,
605
mother_interface=self, **opt)
607
answer = self.check_answer_in_input_file(question_instance, default, path_msg)
608
if answer is not None:
610
answer = question_instance.default(answer)
613
question = question_instance.question
614
value = Cmd.timed_input(question, default, timeout=timeout,
615
fct=question_instance, fct_timeout=fct_timeout)
617
if value == default and ask_class:
618
value = question_instance.default(default)
622
def check_answer_in_input_file(self, question_instance, default, path=False):
623
"""Questions can have answer in output file (or not)"""
625
if not self.inputfile:
626
return None# interactive mode
628
line = self.get_stored_line()
629
# line define if a previous answer was not answer correctly
632
line = self.inputfile.next()
633
except StopIteration:
635
logger.debug('piping')
636
self.store_line(line)
637
return None # print the question and use the pipe
638
logger.info(question_instance.question)
639
logger.warning('The answer to the previous question is not set in your input file')
640
logger.warning('Use %s value' % default)
643
line = line.replace('\n','').strip()
645
line = line.split('#')[0]
647
# Comment or empty line, pass to the next one
648
return self.check_answer_in_input_file(question_instance, default, path)
649
options = question_instance.allow_arg
652
elif hasattr(question_instance, 'do_%s' % line.split()[0]):
653
#This is a command line, exec it and check next line
655
fct = getattr(question_instance, 'do_%s' % line.split()[0])
656
fct(' '.join(line.split()[1:]))
657
return self.check_answer_in_input_file(question_instance, default, path)
659
line = os.path.expanduser(os.path.expandvars(line))
660
if os.path.exists(line):
662
# No valid answer provides
664
self.store_line(line)
665
return None # print the question and use the pipe
667
logger.info(question_instance.question)
668
logger.warning('The answer to the previous question is not set in your input file')
669
logger.warning('Use %s value' % default)
670
self.store_line(line)
673
def store_line(self, line):
674
"""store a line of the input file which should be executed by the higher mother"""
677
self.mother.store_line(line)
679
self.stored_line = line
681
def get_stored_line(self):
682
"""return stored line and clean it"""
684
value = self.mother.get_stored_line()
685
self.mother.stored_line = None
687
value = self.stored_line
688
self.stored_line = None
693
def nice_error_handling(self, error, line):
695
# Make sure that we are at the initial position
697
return self.child.nice_error_handling(error, line)
699
os.chdir(self.__initpos)
700
# Create the debug files
702
if os.path.exists(self.debug_output):
703
os.remove(self.debug_output)
705
cmd.Cmd.onecmd(self, 'history %s' % self.debug_output.replace(' ', '\ '))
706
except Exception, error:
709
debug_file = open(self.debug_output, 'a')
710
traceback.print_exc(file=debug_file)
711
# Create a nice error output
712
if self.history and line == self.history[-1]:
713
error_text = 'Command \"%s\" interrupted with error:\n' % line
715
error_text = 'Command \"%s\" interrupted in sub-command:\n' %line
716
error_text += '\"%s\" with error:\n' % self.history[-1]
719
error_text += '%s : %s\n' % (error.__class__.__name__,
720
str(error).replace('\n','\n\t'))
721
error_text += self.error_debug % {'debug':self.debug_output}
722
logger_stderr.critical(error_text)
725
# Add options status to the debug file
727
self.do_display('options', debug_file)
728
except Exception, error:
729
debug_file.write('Fail to write options with error %s' % error)
732
text = open(self.debug_output).read()
734
#stop the execution if on a non interactive mode
735
if self.use_rawinput == False:
741
def nice_user_error(self, error, line):
743
return self.child.nice_user_error(error, line)
744
# Make sure that we are at the initial position
745
os.chdir(self.__initpos)
746
if line == self.history[-1]:
747
error_text = 'Command \"%s\" interrupted with error:\n' % line
749
error_text = 'Command \"%s\" interrupted in sub-command:\n' %line
750
error_text += '\"%s\" with error:\n' % self.history[-1]
751
error_text += '%s : %s' % (error.__class__.__name__,
752
str(error).replace('\n','\n\t'))
753
logger_stderr.error(error_text)
754
#stop the execution if on a non interactive mode
755
if self.use_rawinput == False:
757
# Remove failed command from history
761
def nice_config_error(self, error, line):
763
return self.child.nice_user_error(error, line)
764
# Make sure that we are at the initial position
765
os.chdir(self.__initpos)
766
if not self.history or line == self.history[-1]:
767
error_text = 'Error detected in \"%s\"\n' % line
769
error_text = 'Error detected in sub-command %s\n' % self.history[-1]
770
error_text += 'write debug file %s \n' % self.debug_output
772
cmd.Cmd.onecmd(self, 'history %s' % self.debug_output)
773
debug_file = open(self.debug_output, 'a')
774
traceback.print_exc(file=debug_file)
775
error_text += self.config_debug % {'debug' :self.debug_output}
776
error_text += '%s : %s' % (error.__class__.__name__,
777
str(error).replace('\n','\n\t'))
778
logger_stderr.error(error_text)
780
# Add options status to the debug file
782
self.do_display('options', debug_file)
783
except Exception, error:
784
debug_file.write('Fail to write options with error %s' % error)
785
#stop the execution if on a non interactive mode
786
if self.use_rawinput == False:
788
# Remove failed command from history
793
def onecmd_orig(self, line, **opt):
794
"""Interpret the argument as though it had been typed in response
797
The return value is a flag indicating whether interpretation of
798
commands by the interpreter should stop.
800
This allow to pass extra argument for internal call.
802
if '~/' in line and os.environ.has_key('HOME'):
803
line = line.replace('~/', '%s/' % os.environ['HOME'])
804
line = os.path.expandvars(line)
805
cmd, arg, line = self.parseline(line)
807
return self.emptyline()
809
return self.default(line)
812
return self.default(line)
815
func = getattr(self, 'do_' + cmd)
816
except AttributeError:
817
return self.default(line)
818
return func(arg, **opt)
821
def onecmd(self, line, **opt):
822
"""catch all error and stop properly command accordingly"""
825
return self.onecmd_orig(line, **opt)
826
except self.InvalidCmd as error:
828
self.nice_error_handling(error, line)
831
self.nice_user_error(error, line)
832
except self.ConfigurationError as error:
833
self.nice_config_error(error, line)
834
except Exception as error:
835
self.nice_error_handling(error, line)
838
except KeyboardInterrupt as error:
839
self.stop_on_keyboard_stop()
841
self.nice_config_error(error, line)
842
logger.error(self.keyboard_stop_msg)
844
def stop_on_keyboard_stop(self):
845
"""action to perform to close nicely on a keyboard interupt"""
846
pass # dummy function
848
def exec_cmd(self, line, errorhandling=False, printcmd=True,
849
precmd=False, postcmd=True, **opt):
850
"""for third party call, call the line with pre and postfix treatment
851
without global error handling """
856
current_interface = self.child
858
current_interface = self
861
line = current_interface.precmd(line)
863
stop = current_interface.onecmd(line, **opt)
865
stop = Cmd.onecmd_orig(current_interface, line, **opt)
867
stop = current_interface.postcmd(stop, line)
870
def run_cmd(self, line):
871
"""for third party call, call the line with pre and postfix treatment
872
with global error handling"""
874
return self.exec_cmd(line, errorhandling=True, precmd=True)
877
"""If empty line, do nothing. Default is repeat previous command."""
880
def default(self, line):
881
"""Default action if line is not recognized"""
884
logger.warning("Command \"%s\" not recognized, please try again" % \
886
if line.strip() in ['q', '.q', 'stop']:
887
logger.info("If you want to quit mg5 please type \"exit\".")
889
if self.history and self.history[-1] == line:
896
# Write the list of command line use in this session
897
def do_history(self, line):
898
"""write in a file the suite of command that was used"""
900
args = self.split_arg(line)
901
# Check arguments validity
902
self.check_history(args)
905
logger.info('\n'.join(self.history))
907
elif args[0] == 'clean':
909
logger.info('History is cleaned')
912
output_file = os.path.join(self._export_dir, 'Cards', \
914
output_file = open(output_file, 'w')
916
output_file = open(args[0], 'w')
918
# Create the command file
919
text = self.get_history_header()
920
text += ('\n'.join(self.history) + '\n')
922
#write this information in a file
923
output_file.write(text)
927
logger.info("History written to " + output_file.name)
929
def avoid_history_duplicate(self, line, no_break=[]):
930
"""remove all line in history (but the last) starting with line.
931
up to the point when a line didn't start by something in no_break.
932
(reading in reverse order)"""
935
for i in range(1, len(self.history)+1):
936
cur_line = self.history[-i]
938
new_history.append(cur_line)
939
elif not any((cur_line.startswith(text) for text in no_break)):
940
to_add = self.history[:-i+1]
942
new_history += to_add
944
elif cur_line.startswith(line):
947
new_history.append(cur_line)
949
new_history.reverse()
950
self.history[:] = new_history
953
def import_command_file(self, filepath):
954
# remove this call from history
958
# Read the lines of the file and execute them
959
if isinstance(filepath, str):
960
commandline = open(filepath).readlines()
962
commandline = filepath
963
oldinputfile = self.inputfile
964
oldraw = self.use_rawinput
965
self.inputfile = (l for l in commandline) # make a generator
966
self.use_rawinput = False
967
# Note using "for line in open(filepath)" is not safe since the file
968
# filepath can be overwritten during the run (leading to weird results)
969
# Note also that we need a generator and not a list.
970
for line in self.inputfile:
971
#remove pointless spaces and \n
972
line = line.replace('\n', '').strip()
975
self.exec_cmd(line, precmd=True)
976
stored = self.get_stored_line()
979
self.exec_cmd(line, precmd=True)
980
stored = self.get_stored_line()
982
# If a child was open close it
984
self.child.exec_cmd('quit')
985
self.inputfile = oldinputfile
986
self.use_rawinput = oldraw
989
def get_history_header(self):
990
"""Default history header"""
992
return self.history_header
997
args = self.split_arg(self.lastcmd)
998
if args and args[0] in ['quit','exit']:
1001
if len(args) >1 and args[1].isdigit():
1002
if args[1] not in ['0', '1']:
1006
#===============================================================================
1007
# Ask a question with a maximum amount of time to answer
1008
#===============================================================================
1010
def timed_input(question, default, timeout=None, noerror=True, fct=None,
1012
""" a question with a maximal time to answer take default otherwise"""
1014
def handle_alarm(signum, frame):
1017
signal.signal(signal.SIGALRM, handle_alarm)
1023
signal.alarm(timeout)
1024
question += '[%ss to answer] ' % (timeout)
1026
result = fct(question)
1027
except TimeOutError:
1029
logger.info('\nuse %s' % default)
1048
def do_quit(self, line):
1049
"""Not in help: exit the mainloop() """
1052
self.child.exec_cmd('quit ' + line, printcmd=False)
1055
self.mother.child = None
1059
level = int(line) - 1
1061
self.mother.lastcmd = 'quit %s' % level
1069
def do_help(self, line):
1070
"""Not in help: propose some usefull possible action """
1072
# if they are an argument use the default help
1074
return cmd.Cmd.do_help(self, line)
1077
names = self.get_names()
1080
# There can be duplicates if routines overridden
1083
if name[:3] == 'do_':
1084
if name == prevname:
1089
doc = getattr(self.cmd, name).__doc__
1093
doc = getattr(self, name).__doc__
1095
tag = "Documented commands"
1097
tag = doc.split(':',1)[0]
1099
tag = "Documented commands"
1101
cmds[tag].append(cmdname)
1103
cmds[tag] = [cmdname]
1105
self.stdout.write("%s\n"%str(self.doc_leader))
1106
for tag in self.helporder:
1109
header = "%s (type help <topic>):" % tag
1110
self.print_topics(header, cmds[tag], 15,80)
1111
for name, item in cmds.items():
1112
if name in self.helporder:
1114
if name == "Not in help":
1116
header = "%s (type help <topic>):" % name
1117
self.print_topics(header, item, 15,80)
1120
## Add contextual help
1121
if len(self.history) == 0:
1122
last_action_2 = last_action = 'start'
1124
last_action_2 = last_action = 'none'
1127
authorize = self.next_possibility.keys()
1128
while last_action_2 not in authorize and last_action not in authorize:
1130
if pos > len(self.history):
1131
last_action_2 = last_action = 'start'
1134
args = self.history[-1 * pos].split()
1135
last_action = args[0]
1137
last_action_2 = '%s %s' % (last_action, args[1])
1139
last_action_2 = 'none'
1141
logger.info('Contextual Help')
1142
logger.info('===============')
1143
if last_action_2 in authorize:
1144
options = self.next_possibility[last_action_2]
1145
elif last_action in authorize:
1146
options = self.next_possibility[last_action]
1149
text = 'The following command(s) may be useful in order to continue.\n'
1150
for option in options:
1151
text+='\t %s \n' % option
1154
def do_display(self, line, output=sys.stdout):
1155
"""Advanced commands: basic display"""
1157
args = self.split_arg(line)
1158
#check the validity of the arguments
1162
raise self.InvalidCmd, 'display require at least one argument'
1164
if args[0] == "options":
1165
outstr = "Value of current Options:\n"
1166
for key, value in self.options.items():
1167
outstr += '%25s \t:\t%s\n' %(key,value)
1170
elif args[0] == "variable":
1171
outstr = "Value of Internal Variable:\n"
1175
outstr += 'GLOBAL:\nVariable %s is not a global variable\n' % args[1]
1177
outstr += 'GLOBAL:\n'
1178
outstr += misc.nice_representation(var, nb_space=4)
1181
var = eval('self.%s' % args[1])
1183
outstr += 'LOCAL:\nVariable %s is not a local variable\n' % args[1]
1185
outstr += 'LOCAL:\n'
1186
outstr += misc.nice_representation(var, nb_space=4)
1188
if output == sys.stdout:
1190
output.write(outstr)
1193
def do_save(self, line, check=True):
1194
"""Save the configuration file"""
1196
args = self.split_arg(line)
1197
# Check argument validity
1199
Cmd.check_save(self, args)
1201
# find base file for the configuration
1202
if'HOME' in os.environ and os.environ['HOME'] and \
1203
os.path.exists(pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')):
1204
base = pjoin(os.environ['HOME'], '.mg5', 'mg5_configuration.txt')
1205
if hasattr(self, 'me_dir'):
1206
basedir = self.me_dir
1210
basedir = os.getcwd()
1212
# launch via ./bin/madevent
1213
base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt')
1214
basedir = self.me_dir
1216
if hasattr(self, 'me_dir'):
1217
base = pjoin(self.me_dir, 'Cards', 'me5_configuration.txt')
1218
if len(args) == 0 and os.path.exists(base):
1219
self.write_configuration(base, base, self.me_dir)
1220
base = pjoin(MG5DIR, 'input', 'mg5_configuration.txt')
1225
self.write_configuration(args[0], base, basedir, self.options)
1227
def write_configuration(self, filepath, basefile, basedir, to_keep):
1228
"""Write the configuration file"""
1229
# We use the default configuration file as a template.
1230
# to ensure that all configuration information are written we
1231
# keep track of all key that we need to write.
1233
logger.info('save configuration file to %s' % filepath)
1234
to_write = to_keep.keys()
1236
# Use local configuration => Need to update the path
1237
for line in file(basefile):
1239
data, value = line.split('=',1)
1244
if data.startswith('#'):
1245
key = data[1:].strip()
1249
value, comment = value.split('#',1)
1253
value = str(to_keep[key])
1258
to_write.remove(key)
1262
# special case need to update path
1263
# check if absolute path
1264
if not os.path.isabs(value):
1265
value = os.path.realpath(os.path.join(basedir, value))
1266
text += '%s = %s # %s \n' % (key, value, comment)
1267
for key in to_write:
1269
text += '%s = %s \n' % (key, to_keep[key])
1272
text += """\n# MG5 MAIN DIRECTORY\n"""
1273
text += "mg5_path = %s\n" % MG5DIR
1275
writer = open(filepath,'w')
1282
class CmdShell(Cmd):
1283
"""CMD command with shell activate"""
1286
def do_shell(self, line):
1287
"Run a shell command"
1289
if line.strip() is '':
1292
logging.info("running shell command: " + line)
1293
subprocess.call(line, shell=True)
1295
def complete_shell(self, text, line, begidx, endidx):
1296
""" add path for shell """
1298
# Filename if directory is given
1300
if len(self.split_arg(line[0:begidx])) > 1 and line[begidx - 1] == os.path.sep:
1303
output = self.path_completion(text,
1305
self.split_arg(line[0:begidx])[-1])
1307
output = self.path_completion(text)
1310
def help_shell(self):
1311
"""help for the shell"""
1312
logger.info("-- run the shell command CMD and catch output",'$MG:color:BLUE')
1313
logger.info("syntax: shell CMD (or ! CMD)",'$MG:color:BLACK')
1318
#===============================================================================
1319
# Question with auto-completion
1320
#===============================================================================
1321
class SmartQuestion(BasicCmd):
1322
""" a class for answering a question with the path autocompletion"""
1325
"""Initializing before starting the main loop"""
1328
BasicCmd.preloop(self)
1331
def __init__(self, question, allow_arg=[], default=None,
1332
mother_interface=None, *arg, **opt):
1333
self.question = question
1334
self.wrong_answer = 0 # forbids infinite loop
1335
self.allow_arg = [str(a) for a in allow_arg]
1336
self.history_header = ''
1337
self.default_value = str(default)
1338
self.mother_interface = mother_interface
1339
cmd.Cmd.__init__(self, *arg, **opt)
1341
def __call__(self, question, reprint_opt=True, **opts):
1343
self.question = question
1344
for key,value in opts:
1345
setattr(self, key, value)
1348
return self.cmdloop()
1351
def completenames(self, text, line, *ignored):
1352
prev_timer = signal.alarm(0) # avoid timer if any
1355
self.stdout.write('\b'*nb_back + '[timer stopped]\n')
1356
self.stdout.write(line)
1360
out[' Options'] = Cmd.list_completion(text, self.allow_arg)
1361
out[' Recognized command'] = BasicCmd.completenames(self, text)
1363
return self.deal_multiple_categories(out)
1364
except Exception, error:
1368
def reask(self, reprint_opt=True):
1369
pat = re.compile('\[(\d*)s to answer\]')
1370
prev_timer = signal.alarm(0) # avoid timer if any
1373
if pat.search(self.question):
1374
timeout = int(pat.search(self.question).groups()[0])
1378
signal.alarm(timeout)
1381
self.question = pat.sub('',self.question)
1385
def default(self, line):
1386
"""Default action if line is not recognized"""
1388
if line.strip() == '' and self.default_value is not None:
1389
self.value = self.default_value
1393
def emptyline(self):
1394
"""If empty line, return default"""
1396
if self.default_value is not None:
1397
self.value = self.default_value
1400
def postcmd(self, stop, line):
1403
if self.value in self.allow_arg:
1405
elif str(self.value) == 'EOF':
1406
self.value = self.default_value
1408
elif line and hasattr(self, 'do_%s' % line.split()[0]):
1410
elif self.value == 'repeat':
1412
elif len(self.allow_arg)==0:
1416
except Exception,error:
1417
if self.wrong_answer < 100:
1418
self.wrong_answer += 1
1419
logger.warning("""%s not valid argument. Valid argument are in (%s).""" \
1420
% (self.value,','.join(self.allow_arg)))
1421
logger.warning('please retry')
1424
self.value = self.default_value
1427
def cmdloop(self, intro=None):
1428
cmd.Cmd.cmdloop(self, intro)
1432
def smart_input(input_text, allow_arg=[], default=None):
1434
obj = SmartQuestion(allow_arg=allow_arg, default=default)
1435
return obj.cmdloop()
1437
#===============================================================================
1438
# Question in order to return a path with auto-completion
1439
#===============================================================================
1440
class OneLinePathCompletion(SmartQuestion):
1441
""" a class for answering a question with the path autocompletion"""
1444
def completenames(self, text, line, begidx, endidx):
1445
prev_timer = signal.alarm(0) # avoid timer if any
1448
self.stdout.write('\b'*nb_back + '[timer stopped]\n')
1449
self.stdout.write(line)
1453
out[' Options'] = Cmd.list_completion(text, self.allow_arg)
1454
out[' Path from ./'] = Cmd.path_completion(text, only_dirs = False)
1455
out[' Recognized command'] = BasicCmd.completenames(self, text)
1457
return self.deal_multiple_categories(out)
1458
except Exception, error:
1461
def precmd(self, *args):
1465
return SmartQuestion.precmd(self, *args)
1467
def completedefault(self,text, line, begidx, endidx):
1468
prev_timer = signal.alarm(0) # avoid timer if any
1471
self.stdout.write('\b'*nb_back + '[timer stopped]\n')
1472
self.stdout.write(line)
1475
args = Cmd.split_arg(line[0:begidx])
1476
except Exception, error:
1479
# Directory continuation
1480
if args[-1].endswith(os.path.sep):
1482
return Cmd.path_completion(text,
1483
os.path.join('.',*[a for a in args \
1484
if a.endswith(os.path.sep)]))
1485
self.completenames(line+text)
1488
def postcmd(self, stop, line):
1490
if self.value in self.allow_arg:
1492
elif self.value and os.path.isfile(self.value):
1493
return os.path.relpath(self.value)
1494
elif self.value and str(self.value) == 'EOF':
1495
self.value = self.default_value
1497
elif line and hasattr(self, 'do_%s' % line.split()[0]):
1500
elif self.value == 'repeat':
1504
except Exception, error:
1505
print """not valid argument. Valid argument are file path or value in (%s).""" \
1506
% ','.join(self.allow_arg)
1507
print 'please retry'
1510
return self.reask(reprint_opt)
1514
def raw_path_input(input_text, allow_arg=[], default=None):
1516
obj = OneLinePathCompletion(allow_arg=allow_arg, default=default )
1517
return obj.cmdloop()
1519
#===============================================================================
1521
#===============================================================================
1522
class CmdFile(file):
1523
""" a class for command input file -in order to debug cmd \n problem"""
1525
def __init__(self, name, opt='rU'):
1527
file.__init__(self, name, opt)
1528
self.text = file.read(self)
1530
self.lines = self.text.split('\n')
1532
def readline(self, *arg, **opt):
1533
"""readline method treating correctly a line whithout \n at the end
1537
line = self.lines.pop(0)
1541
if line.endswith('\n'):
1547
return self.lines.__next__()
1550
return self.lines.__iter__()