0001 """A generic class to build line-oriented command interpreters. 0002 0003 Interpreters constructed with this class obey the following conventions: 0004 0005 1. End of file on input is processed as the command 'EOF'. 0006 2. A command is parsed out of each line by collecting the prefix composed 0007 of characters in the identchars member. 0008 3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method 0009 is passed a single argument consisting of the remainder of the line. 0010 4. Typing an empty line repeats the last command. (Actually, it calls the 0011 method `emptyline', which may be overridden in a subclass.) 0012 5. There is a predefined `help' method. Given an argument `topic', it 0013 calls the command `help_topic'. With no arguments, it lists all topics 0014 with defined help_ functions, broken into up to three topics; documented 0015 commands, miscellaneous help topics, and undocumented commands. 0016 6. The command '?' is a synonym for `help'. The command '!' is a synonym 0017 for `shell', if a do_shell method exists. 0018 7. If completion is enabled, completing commands will be done automatically, 0019 and completing of commands args is done by calling complete_foo() with 0020 arguments text, line, begidx, endidx. text is string we are matching 0021 against, all returned matches must begin with it. line is the current 0022 input line (lstripped), begidx and endidx are the beginning and end 0023 indexes of the text being matched, which could be used to provide 0024 different completion depending upon which position the argument is in. 0025 0026 The `default' method may be overridden to intercept commands for which there 0027 is no do_ method. 0028 0029 The `completedefault' method may be overridden to intercept completions for 0030 commands that have no complete_ method. 0031 0032 The data member `self.ruler' sets the character used to draw separator lines 0033 in the help messages. If empty, no ruler line is drawn. It defaults to "=". 0034 0035 If the value of `self.intro' is nonempty when the cmdloop method is called, 0036 it is printed out on interpreter startup. This value may be overridden 0037 via an optional argument to the cmdloop() method. 0038 0039 The data members `self.doc_header', `self.misc_header', and 0040 `self.undoc_header' set the headers used for the help function's 0041 listings of documented functions, miscellaneous topics, and undocumented 0042 functions respectively. 0043 0044 These interpreters use raw_input; thus, if the readline module is loaded, 0045 they automatically support Emacs-like command history and editing features. 0046 """ 0047 0048 import string 0049 0050 __all__ = ["Cmd"] 0051 0052 PROMPT = '(Cmd) ' 0053 IDENTCHARS = string.ascii_letters + string.digits + '_' 0054 0055 class Cmd: 0056 """A simple framework for writing line-oriented command interpreters. 0057 0058 These are often useful for test harnesses, administrative tools, and 0059 prototypes that will later be wrapped in a more sophisticated interface. 0060 0061 A Cmd instance or subclass instance is a line-oriented interpreter 0062 framework. There is no good reason to instantiate Cmd itself; rather, 0063 it's useful as a superclass of an interpreter class you define yourself 0064 in order to inherit Cmd's methods and encapsulate action methods. 0065 0066 """ 0067 prompt = PROMPT 0068 identchars = IDENTCHARS 0069 ruler = '=' 0070 lastcmd = '' 0071 intro = None 0072 doc_leader = "" 0073 doc_header = "Documented commands (type help <topic>):" 0074 misc_header = "Miscellaneous help topics:" 0075 undoc_header = "Undocumented commands:" 0076 nohelp = "*** No help on %s" 0077 use_rawinput = 1 0078 0079 def __init__(self, completekey='tab', stdin=None, stdout=None): 0080 """Instantiate a line-oriented interpreter framework. 0081 0082 The optional argument 'completekey' is the readline name of a 0083 completion key; it defaults to the Tab key. If completekey is 0084 not None and the readline module is available, command completion 0085 is done automatically. The optional arguments stdin and stdout 0086 specify alternate input and output file objects; if not specified, 0087 sys.stdin and sys.stdout are used. 0088 0089 """ 0090 import sys 0091 if stdin is not None: 0092 self.stdin = stdin 0093 else: 0094 self.stdin = sys.stdin 0095 if stdout is not None: 0096 self.stdout = stdout 0097 else: 0098 self.stdout = sys.stdout 0099 self.cmdqueue = [] 0100 self.completekey = completekey 0101 0102 def cmdloop(self, intro=None): 0103 """Repeatedly issue a prompt, accept input, parse an initial prefix 0104 off the received input, and dispatch to action methods, passing them 0105 the remainder of the line as argument. 0106 0107 """ 0108 0109 self.preloop() 0110 if self.use_rawinput and self.completekey: 0111 try: 0112 import readline 0113 self.old_completer = readline.get_completer() 0114 readline.set_completer(self.complete) 0115 readline.parse_and_bind(self.completekey+": complete") 0116 except ImportError: 0117 pass 0118 try: 0119 if intro is not None: 0120 self.intro = intro 0121 if self.intro: 0122 self.stdout.write(str(self.intro)+"\n") 0123 stop = None 0124 while not stop: 0125 if self.cmdqueue: 0126 line = self.cmdqueue.pop(0) 0127 else: 0128 if self.use_rawinput: 0129 try: 0130 line = raw_input(self.prompt) 0131 except EOFError: 0132 line = 'EOF' 0133 else: 0134 self.stdout.write(self.prompt) 0135 self.stdout.flush() 0136 line = self.stdin.readline() 0137 if not len(line): 0138 line = 'EOF' 0139 else: 0140 line = line[:-1] # chop \n 0141 line = self.precmd(line) 0142 stop = self.onecmd(line) 0143 stop = self.postcmd(stop, line) 0144 self.postloop() 0145 finally: 0146 if self.use_rawinput and self.completekey: 0147 try: 0148 import readline 0149 readline.set_completer(self.old_completer) 0150 except ImportError: 0151 pass 0152 0153 0154 def precmd(self, line): 0155 """Hook method executed just before the command line is 0156 interpreted, but after the input prompt is generated and issued. 0157 0158 """ 0159 return line 0160 0161 def postcmd(self, stop, line): 0162 """Hook method executed just after a command dispatch is finished.""" 0163 return stop 0164 0165 def preloop(self): 0166 """Hook method executed once when the cmdloop() method is called.""" 0167 pass 0168 0169 def postloop(self): 0170 """Hook method executed once when the cmdloop() method is about to 0171 return. 0172 0173 """ 0174 pass 0175 0176 def parseline(self, line): 0177 """Parse the line into a command name and a string containing 0178 the arguments. Returns a tuple containing (command, args, line). 0179 'command' and 'args' may be None if the line couldn't be parsed. 0180 """ 0181 line = line.strip() 0182 if not line: 0183 return None, None, line 0184 elif line[0] == '?': 0185 line = 'help ' + line[1:] 0186 elif line[0] == '!': 0187 if hasattr(self, 'do_shell'): 0188 line = 'shell ' + line[1:] 0189 else: 0190 return None, None, line 0191 i, n = 0, len(line) 0192 while i < n and line[i] in self.identchars: i = i+1 0193 cmd, arg = line[:i], line[i:].strip() 0194 return cmd, arg, line 0195 0196 def onecmd(self, line): 0197 """Interpret the argument as though it had been typed in response 0198 to the prompt. 0199 0200 This may be overridden, but should not normally need to be; 0201 see the precmd() and postcmd() methods for useful execution hooks. 0202 The return value is a flag indicating whether interpretation of 0203 commands by the interpreter should stop. 0204 0205 """ 0206 cmd, arg, line = self.parseline(line) 0207 if not line: 0208 return self.emptyline() 0209 if cmd is None: 0210 return self.default(line) 0211 self.lastcmd = line 0212 if cmd == '': 0213 return self.default(line) 0214 else: 0215 try: 0216 func = getattr(self, 'do_' + cmd) 0217 except AttributeError: 0218 return self.default(line) 0219 return func(arg) 0220 0221 def emptyline(self): 0222 """Called when an empty line is entered in response to the prompt. 0223 0224 If this method is not overridden, it repeats the last nonempty 0225 command entered. 0226 0227 """ 0228 if self.lastcmd: 0229 return self.onecmd(self.lastcmd) 0230 0231 def default(self, line): 0232 """Called on an input line when the command prefix is not recognized. 0233 0234 If this method is not overridden, it prints an error message and 0235 returns. 0236 0237 """ 0238 self.stdout.write('*** Unknown syntax: %s\n'%line) 0239 0240 def completedefault(self, *ignored): 0241 """Method called to complete an input line when no command-specific 0242 complete_*() method is available. 0243 0244 By default, it returns an empty list. 0245 0246 """ 0247 return [] 0248 0249 def completenames(self, text, *ignored): 0250 dotext = 'do_'+text 0251 return [a[3:] for a in self.get_names() if a.startswith(dotext)] 0252 0253 def complete(self, text, state): 0254 """Return the next possible completion for 'text'. 0255 0256 If a command has not been entered, then complete against command list. 0257 Otherwise try to call complete_<command> to get list of completions. 0258 """ 0259 if state == 0: 0260 import readline 0261 origline = readline.get_line_buffer() 0262 line = origline.lstrip() 0263 stripped = len(origline) - len(line) 0264 begidx = readline.get_begidx() - stripped 0265 endidx = readline.get_endidx() - stripped 0266 if begidx>0: 0267 cmd, args, foo = self.parseline(line) 0268 if cmd == '': 0269 compfunc = self.completedefault 0270 else: 0271 try: 0272 compfunc = getattr(self, 'complete_' + cmd) 0273 except AttributeError: 0274 compfunc = self.completedefault 0275 else: 0276 compfunc = self.completenames 0277 self.completion_matches = compfunc(text, line, begidx, endidx) 0278 try: 0279 return self.completion_matches[state] 0280 except IndexError: 0281 return None 0282 0283 def get_names(self): 0284 # Inheritance says we have to look in class and 0285 # base classes; order is not important. 0286 names = [] 0287 classes = [self.__class__] 0288 while classes: 0289 aclass = classes.pop(0) 0290 if aclass.__bases__: 0291 classes = classes + list(aclass.__bases__) 0292 names = names + dir(aclass) 0293 return names 0294 0295 def complete_help(self, *args): 0296 return self.completenames(*args) 0297 0298 def do_help(self, arg): 0299 if arg: 0300 # XXX check arg syntax 0301 try: 0302 func = getattr(self, 'help_' + arg) 0303 except AttributeError: 0304 try: 0305 doc=getattr(self, 'do_' + arg).__doc__ 0306 if doc: 0307 self.stdout.write("%s\n"%str(doc)) 0308 return 0309 except AttributeError: 0310 pass 0311 self.stdout.write("%s\n"%str(self.nohelp % (arg,))) 0312 return 0313 func() 0314 else: 0315 names = self.get_names() 0316 cmds_doc = [] 0317 cmds_undoc = [] 0318 help = {} 0319 for name in names: 0320 if name[:5] == 'help_': 0321 help[name[5:]]=1 0322 names.sort() 0323 # There can be duplicates if routines overridden 0324 prevname = '' 0325 for name in names: 0326 if name[:3] == 'do_': 0327 if name == prevname: 0328 continue 0329 prevname = name 0330 cmd=name[3:] 0331 if cmd in help: 0332 cmds_doc.append(cmd) 0333 del help[cmd] 0334 elif getattr(self, name).__doc__: 0335 cmds_doc.append(cmd) 0336 else: 0337 cmds_undoc.append(cmd) 0338 self.stdout.write("%s\n"%str(self.doc_leader)) 0339 self.print_topics(self.doc_header, cmds_doc, 15,80) 0340 self.print_topics(self.misc_header, help.keys(),15,80) 0341 self.print_topics(self.undoc_header, cmds_undoc, 15,80) 0342 0343 def print_topics(self, header, cmds, cmdlen, maxcol): 0344 if cmds: 0345 self.stdout.write("%s\n"%str(header)) 0346 if self.ruler: 0347 self.stdout.write("%s\n"%str(self.ruler * len(header))) 0348 self.columnize(cmds, maxcol-1) 0349 self.stdout.write("\n") 0350 0351 def columnize(self, list, displaywidth=80): 0352 """Display a list of strings as a compact set of columns. 0353 0354 Each column is only as wide as necessary. 0355 Columns are separated by two spaces (one was not legible enough). 0356 """ 0357 if not list: 0358 self.stdout.write("<empty>\n") 0359 return 0360 nonstrings = [i for i in range(len(list)) 0361 if not isinstance(list[i], str)] 0362 if nonstrings: 0363 raise TypeError, ("list[i] not a string for i in %s" % 0364 ", ".join(map(str, nonstrings))) 0365 size = len(list) 0366 if size == 1: 0367 self.stdout.write('%s\n'%str(list[0])) 0368 return 0369 # Try every row count from 1 upwards 0370 for nrows in range(1, len(list)): 0371 ncols = (size+nrows-1) // nrows 0372 colwidths = [] 0373 totwidth = -2 0374 for col in range(ncols): 0375 colwidth = 0 0376 for row in range(nrows): 0377 i = row + nrows*col 0378 if i >= size: 0379 break 0380 x = list[i] 0381 colwidth = max(colwidth, len(x)) 0382 colwidths.append(colwidth) 0383 totwidth += colwidth + 2 0384 if totwidth > displaywidth: 0385 break 0386 if totwidth <= displaywidth: 0387 break 0388 else: 0389 nrows = len(list) 0390 ncols = 1 0391 colwidths = [0] 0392 for row in range(nrows): 0393 texts = [] 0394 for col in range(ncols): 0395 i = row + nrows*col 0396 if i >= size: 0397 x = "" 0398 else: 0399 x = list[i] 0400 texts.append(x) 0401 while texts and not texts[-1]: 0402 del texts[-1] 0403 for col in range(len(texts)): 0404 texts[col] = texts[col].ljust(colwidths[col]) 0405 self.stdout.write("%s\n"%str(" ".join(texts))) 0406
Generated by PyXR 0.9.4