PyXR

c:\python24\lib \ cmd.py



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
SourceForge.net Logo