--- cmd.py.bak 2012-06-21 18:42:21.000000000 -0400 +++ cmd.py 2012-06-22 09:39:34.000000000 -0400 @@ -1,4 +1,4 @@ -"""A generic class to build line-oriented command interpreters. +'''A generic class to build line-oriented command interpreters. Interpreters constructed with this class obey the following conventions: @@ -40,17 +40,19 @@ `self.undoc_header' set the headers used for the help function's listings of documented functions, miscellaneous topics, and undocumented functions respectively. -""" +''' -import string, sys +import string,\ + sys -__all__ = ["Cmd"] +__all__ = ['Cmd'] -PROMPT = '(Cmd) ' -IDENTCHARS = string.ascii_letters + string.digits + '_' +PROMPT = '(Cmd) ' +IDENTCHARS = string.ascii_letters + string.digits + '_' -class Cmd: - """A simple framework for writing line-oriented command interpreters. + +class Cmd(object): + '''A simple framework for writing line-oriented command interpreters. These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface. @@ -59,22 +61,23 @@ framework. There is no good reason to instantiate Cmd itself; rather, it's useful as a superclass of an interpreter class you define yourself in order to inherit Cmd's methods and encapsulate action methods. + ''' - """ - prompt = PROMPT - identchars = IDENTCHARS - ruler = '=' - lastcmd = '' - intro = None - doc_leader = "" - doc_header = "Documented commands (type help ):" - misc_header = "Miscellaneous help topics:" - undoc_header = "Undocumented commands:" - nohelp = "*** No help on %s" - use_rawinput = 1 + prompt = ''.join('\n', PROMPT) + identchars = IDENTCHARS + ruler = '=' + lastcmd = '' + intro = None + doc_leader = '' + doc_header = 'Documented commands (type help ):' + misc_header = 'Miscellaneous help topics:' + undoc_header = 'Undocumented commands:' + nohelp = '*** No help on {}' + use_rawinput = True def __init__(self, completekey='tab', stdin=None, stdout=None): - """Instantiate a line-oriented interpreter framework. + ''' + Instantiate a line-oriented interpreter framework. The optional argument 'completekey' is the readline name of a completion key; it defaults to the Tab key. If completekey is @@ -82,48 +85,49 @@ is done automatically. The optional arguments stdin and stdout specify alternate input and output file objects; if not specified, sys.stdin and sys.stdout are used. + ''' - """ - if stdin is not None: - self.stdin = stdin - else: - self.stdin = sys.stdin - if stdout is not None: - self.stdout = stdout - else: - self.stdout = sys.stdout - self.cmdqueue = [] + self.stdin = stdin if stdin is not None else sys.stdin + self.stdout = stdout if stdout is not None else sys.stdout + self.cmdqueue = [] self.completekey = completekey + # Main Loop + #--------------------------------------------------------------------------- def cmdloop(self, intro=None): - """Repeatedly issue a prompt, accept input, parse an initial prefix + ''' + Repeatedly issue a prompt, accept input, parse an initial prefix off the received input, and dispatch to action methods, passing them the remainder of the line as argument. - - """ + ''' self.preloop() + if self.use_rawinput and self.completekey: try: import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") + readline.parse_and_bind(self.completekey + ': complete') except ImportError: pass + try: if intro is not None: self.intro = intro + if self.intro: - self.stdout.write(str(self.intro)+"\n") + self.stdout.write('{}\n'.format(self.intro)) + stop = None + while not stop: if self.cmdqueue: line = self.cmdqueue.pop(0) else: if self.use_rawinput: try: - line = input(self.prompt) + line = raw_input(self.prompt) except EOFError: line = 'EOF' else: @@ -138,6 +142,7 @@ stop = self.onecmd(line) stop = self.postcmd(stop, line) self.postloop() + finally: if self.use_rawinput and self.completekey: try: @@ -146,34 +151,36 @@ except ImportError: pass - + # Hook Methods + #--------------------------------------------------------------------------- def precmd(self, line): - """Hook method executed just before the command line is + ''' + Hook method executed just before the command line is interpreted, but after the input prompt is generated and issued. - - """ + ''' return line def postcmd(self, stop, line): - """Hook method executed just after a command dispatch is finished.""" + 'Hook method executed just after a command dispatch is finished.' return stop def preloop(self): - """Hook method executed once when the cmdloop() method is called.""" + 'Hook method executed once when the cmdloop() method is called.' pass def postloop(self): - """Hook method executed once when the cmdloop() method is about to - return. - - """ + 'Hook method executed once when the cmdloop() method is about to return.' pass + # Parsing + #--------------------------------------------------------------------------- def parseline(self, line): - """Parse the line into a command name and a string containing + ''' + Parse the line into a command name and a string containing the arguments. Returns a tuple containing (command, args, line). 'command' and 'args' may be None if the line couldn't be parsed. - """ + ''' + line = line.strip() if not line: return None, None, line @@ -184,28 +191,33 @@ line = 'shell ' + line[1:] else: return None, None, line - i, n = 0, len(line) - while i < n and line[i] in self.identchars: i = i+1 - cmd, arg = line[:i], line[i:].strip() + i = 0 + n = len(line) + while i < n and line[i] in self.identchars: + i += 1 + cmd = line[:i] + arg = line[i:].strip() return cmd, arg, line + # Handling Commands + #--------------------------------------------------------------------------- def onecmd(self, line): - """Interpret the argument as though it had been typed in response + '''Interpret the argument as though it had been typed in response to the prompt. This may be overridden, but should not normally need to be; see the precmd() and postcmd() methods for useful execution hooks. The return value is a flag indicating whether interpretation of commands by the interpreter should stop. + ''' - """ cmd, arg, line = self.parseline(line) if not line: return self.emptyline() if cmd is None: return self.default(line) self.lastcmd = line - if line == 'EOF' : + if line == 'EOF': self.lastcmd = '' if cmd == '': return self.default(line) @@ -217,51 +229,54 @@ return func(arg) def emptyline(self): - """Called when an empty line is entered in response to the prompt. + ''' + Called when an empty line is entered in response to the prompt. If this method is not overridden, it repeats the last nonempty command entered. + ''' - """ if self.lastcmd: return self.onecmd(self.lastcmd) def default(self, line): - """Called on an input line when the command prefix is not recognized. + '''Called on an input line when the command prefix is not recognized. If this method is not overridden, it prints an error message and returns. + ''' + self.stdout.write('*** Unknown syntax: {}\n'.format(line)) - """ - self.stdout.write('*** Unknown syntax: %s\n'%line) - + # Completion Methods + #--------------------------------------------------------------------------- def completedefault(self, *ignored): - """Method called to complete an input line when no command-specific + '''Method called to complete an input line when no command-specific complete_*() method is available. - By default, it returns an empty list. - - """ + Returns an empty list by default. + ''' return [] def completenames(self, text, *ignored): - dotext = 'do_'+text + dotext = 'do_' + text return [a[3:] for a in self.get_names() if a.startswith(dotext)] def complete(self, text, state): - """Return the next possible completion for 'text'. + '''Return the next possible completion for 'text'. If a command has not been entered, then complete against command list. Otherwise try to call complete_ to get list of completions. - """ - if state == 0: + ''' + if state is 0: import readline + origline = readline.get_line_buffer() - line = origline.lstrip() + line = origline.lstrip() stripped = len(origline) - len(line) - begidx = readline.get_begidx() - stripped - endidx = readline.get_endidx() - stripped - if begidx>0: + begidx = readline.get_begidx() - stripped + endidx = readline.get_endidx() - stripped + + if begidx > 0: cmd, args, foo = self.parseline(line) if cmd == '': compfunc = self.completedefault @@ -284,11 +299,14 @@ return dir(self.__class__) def complete_help(self, *args): - commands = set(self.completenames(*args)) - topics = set(a[5:] for a in self.get_names() - if a.startswith('help_' + args[0])) + commands = set(self.completenames(*args)) + topics = set(a[5:] for a in self.get_names() + if a.startswith('help_' + args[0]) + ) return list(commands | topics) + # Help & Printing + #--------------------------------------------------------------------------- def do_help(self, arg): 'List available commands with "help" or detailed help with "help cmd".' if arg: @@ -297,23 +315,23 @@ func = getattr(self, 'help_' + arg) except AttributeError: try: - doc=getattr(self, 'do_' + arg).__doc__ + doc = getattr(self, 'do_' + arg).__doc__ if doc: - self.stdout.write("%s\n"%str(doc)) + self.stdout.write('{}\n'.format(str(doc))) return except AttributeError: pass - self.stdout.write("%s\n"%str(self.nohelp % (arg,))) + self.stdout.write('{}\n'.format(self.nohelp.format(arg,))) return func() else: - names = self.get_names() - cmds_doc = [] - cmds_undoc = [] - help = {} + names = self.get_names() + cmds_doc = [] + cmds_undoc = [] + help = {} for name in names: if name[:5] == 'help_': - help[name[5:]]=1 + help[name[5:]] = 1 names.sort() # There can be duplicates if routines overridden prevname = '' @@ -322,7 +340,7 @@ if name == prevname: continue prevname = name - cmd=name[3:] + cmd = name[3:] if cmd in help: cmds_doc.append(cmd) del help[cmd] @@ -330,47 +348,47 @@ cmds_doc.append(cmd) else: cmds_undoc.append(cmd) - self.stdout.write("%s\n"%str(self.doc_leader)) - self.print_topics(self.doc_header, cmds_doc, 15,80) - self.print_topics(self.misc_header, list(help.keys()),15,80) - self.print_topics(self.undoc_header, cmds_undoc, 15,80) + self.stdout.write('{}\n'.format(str(self.doc_leader))) + self.print_topics(self.doc_header, cmds_doc, 15, 80) + self.print_topics(self.misc_header, list(help.keys()), 15, 80) + self.print_topics(self.undoc_header, cmds_undoc, 15, 80) def print_topics(self, header, cmds, cmdlen, maxcol): if cmds: - self.stdout.write("%s\n"%str(header)) + self.stdout.write('{}\n'.format(str(header))) if self.ruler: - self.stdout.write("%s\n"%str(self.ruler * len(header))) - self.columnize(cmds, maxcol-1) - self.stdout.write("\n") + self.stdout.write('{}\n'.format(str(self.ruler * len(header)))) + self.columnize(cmds, maxcol - 1) + self.stdout.write('\n') def columnize(self, list, displaywidth=80): - """Display a list of strings as a compact set of columns. + '''Display a list of strings as a compact set of columns. Each column is only as wide as necessary. Columns are separated by two spaces (one was not legible enough). - """ + ''' if not list: - self.stdout.write("\n") + self.stdout.write('\n') return nonstrings = [i for i in range(len(list)) - if not isinstance(list[i], str)] + if not isinstance(list[i], str)] if nonstrings: - raise TypeError("list[i] not a string for i in %s" - % ", ".join(map(str, nonstrings))) + raise TypeError('list[i] not a string for i in {}'.format( + ', '.join(map(str, nonstrings)))) size = len(list) - if size == 1: - self.stdout.write('%s\n'%str(list[0])) + if size is 1: + self.stdout.write('{}\n'.format( str(list[0])) ) return # Try every row count from 1 upwards for nrows in range(1, len(list)): - ncols = (size+nrows-1) // nrows + ncols = (size + nrows - 1) // nrows colwidths = [] - totwidth = -2 + totwidth = -2 for col in range(ncols): colwidth = 0 for row in range(nrows): - i = row + nrows*col + i = row + nrows * col if i >= size: break x = list[i] @@ -382,20 +400,21 @@ if totwidth <= displaywidth: break else: - nrows = len(list) - ncols = 1 - colwidths = [0] + nrows = len(list) + ncols = 1 + colwidths = [0] for row in range(nrows): texts = [] for col in range(ncols): i = row + nrows*col - if i >= size: - x = "" - else: - x = list[i] + x = '' if i >= size else list[i] texts.append(x) while texts and not texts[-1]: del texts[-1] for col in range(len(texts)): texts[col] = texts[col].ljust(colwidths[col]) - self.stdout.write("%s\n"%str(" ".join(texts))) + self.stdout.write('{}\n'.format(str(' '.join(texts)))) + +if __name__ == '__main__': + c = Cmd() + c.cmdloop() \ No newline at end of file