2 # Copyright (c) 2002-2005 ActiveState Corp.
3 # License: MIT (see LICENSE.txt for license details)
4 # Author: Trent Mick (TrentM@ActiveState.com)
5 # Home: http://trentm.com/projects/cmdln/
7 """An improvement on Python's standard cmd.py module.
9 As with cmd.py, this module provides "a simple framework for writing
10 line-oriented command intepreters." This module provides a 'RawCmdln'
11 class that fixes some design flaws in cmd.Cmd, making it more scalable
12 and nicer to use for good 'cvs'- or 'svn'-style command line interfaces
13 or simple shells. And it provides a 'Cmdln' class that add
14 optparse-based option processing. Basically you use it like this:
18 class MySVN(cmdln.Cmdln):
21 @cmdln.alias('stat', 'st')
22 @cmdln.option('-v', '--verbose', action='store_true'
23 help='print verbose information')
24 def do_status(self, subcmd, opts, *paths):
25 print "handle 'svn status' command"
29 if __name__ == "__main__":
34 See the README.txt or <http://trentm.com/projects/cmdln/> for more
38 __revision__ = "$Id: cmdln.py 1666 2007-05-09 03:13:03Z trentm $"
39 __version_info__ = (1, 0, 0)
40 __version__ = '.'.join(map(str, __version_info__))
46 from pprint import pprint
53 LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3)
55 # An unspecified optional argument when None is a meaningful value.
56 _NOT_SPECIFIED = ("Not", "Specified")
58 # Pattern to match a TypeError message from a call that
59 # failed because of incorrect number of arguments (see
61 _INCORRECT_NUM_ARGS_RE = re.compile(
62 r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
68 class CmdlnError(Exception):
69 """A cmdln.py usage error."""
70 def __init__(self, msg):
75 class CmdlnUserError(Exception):
76 """An error by a user of a cmdln-based tool/shell."""
81 #---- public methods and classes
84 """Decorator to add aliases for Cmdln.do_* command handlers.
87 class MyShell(cmdln.Cmdln):
88 @cmdln.alias("!", "sh")
89 def do_shell(self, argv):
90 #...implement 'shell' command
93 if not hasattr(f, "aliases"):
100 class RawCmdln(cmd.Cmd):
101 """An improved (on cmd.Cmd) framework for building multi-subcommand
102 scripts (think "svn" & "cvs") and simple shells (think "pdb" and
109 class MySVN(cmdln.RawCmdln):
112 @cmdln.aliases('stat', 'st')
113 def do_status(self, argv):
114 print "handle 'svn status' command"
116 if __name__ == "__main__":
118 retval = shell.main()
121 See <http://trentm.com/projects/cmdln> for more information.
123 name = None # if unset, defaults basename(sys.argv[0])
124 prompt = None # if unset, defaults to self.name+"> "
125 version = None # if set, default top-level options include --version
127 # Default messages for some 'help' command error cases.
128 # They are interpolated with one arg: the command.
129 nohelp = "no help on '%s'"
130 unknowncmd = "unknown command: '%s'"
132 helpindent = '' # string with which to indent help output
134 def __init__(self, completekey='tab',
135 stdin=None, stdout=None, stderr=None):
136 """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
138 The optional argument 'completekey' is the readline name of a
139 completion key; it defaults to the Tab key. If completekey is
140 not None and the readline module is available, command completion
141 is done automatically.
143 The optional arguments 'stdin', 'stdout' and 'stderr' specify
144 alternate input, output and error output file objects; if not
145 specified, sys.* are used.
147 If 'stdout' but not 'stderr' is specified, stdout is used for
148 error output. This is to provide least surprise for users used
149 to only the 'stdin' and 'stdout' options with cmd.Cmd.
152 if self.name is None:
153 self.name = os.path.basename(sys.argv[0])
154 if self.prompt is None:
155 self.prompt = self.name+"> "
156 self._name_str = self._str(self.name)
157 self._prompt_str = self._str(self.prompt)
158 if stdin is not None:
161 self.stdin = sys.stdin
162 if stdout is not None:
165 self.stdout = sys.stdout
166 if stderr is not None:
168 elif stdout is not None:
171 self.stderr = sys.stderr
173 self.completekey = completekey
174 self.cmdlooping = False
176 def get_optparser(self):
177 """Hook for subclasses to set the option parser for the
178 top-level command/shell.
180 This option parser is used retrieved and used by `.main()' to
181 handle top-level options.
183 The default implements a single '-h|--help' option. Sub-classes
184 can return None to have no options at the top-level. Typically
185 an instance of CmdlnOptionParser should be returned.
187 version = (self.version is not None
188 and "%s %s" % (self._name_str, self.version)
190 return CmdlnOptionParser(self, version=version)
192 def postoptparse(self):
193 """Hook method executed just after `.main()' parses top-level
196 When called `self.values' holds the results of the option parse.
200 def main(self, argv=None, loop=LOOP_NEVER):
201 """A possible mainline handler for a script, like so:
204 class MyCmd(cmdln.Cmdln):
208 if __name__ == "__main__":
211 By default this will use sys.argv to issue a single command to
212 'MyCmd', then exit. The 'loop' argument can be use to control
213 interactive shell behaviour.
216 "argv" (optional, default sys.argv) is the command to run.
217 It must be a sequence, where the first element is the
218 command name and subsequent elements the args for that
220 "loop" (optional, default LOOP_NEVER) is a constant
221 indicating if a command loop should be started (i.e. an
222 interactive shell). Valid values (constants on this module):
223 LOOP_ALWAYS start loop and run "argv", if any
224 LOOP_NEVER run "argv" (or .emptyline()) and exit
225 LOOP_IF_EMPTY run "argv", if given, and exit;
226 otherwise, start loop
232 argv = argv[:] # don't modify caller's list
234 self.optparser = self.get_optparser()
235 if self.optparser: # i.e. optparser=None means don't process for opts
237 self.options, args = self.optparser.parse_args(argv[1:])
238 except CmdlnUserError, ex:
239 msg = "%s: %s\nTry '%s help' for info.\n"\
240 % (self.name, ex, self.name)
241 self.stderr.write(self._str(msg))
244 except StopOptionProcessing, ex:
247 self.options, args = None, argv[1:]
250 if loop == LOOP_ALWAYS:
252 self.cmdqueue.append(args)
253 return self.cmdloop()
254 elif loop == LOOP_NEVER:
256 return self.cmd(args)
258 return self.emptyline()
259 elif loop == LOOP_IF_EMPTY:
261 return self.cmd(args)
263 return self.cmdloop()
266 """Run one command and exit.
268 "argv" is the arglist for the command to run. argv[0] is the
269 command to run. If argv is an empty list then the
270 'emptyline' handler is run.
272 Returns the return value from the command handler.
274 assert (isinstance(argv, (list, tuple)),
275 "'argv' is not a sequence: %r" % argv)
278 argv = self.precmd(argv)
279 retval = self.onecmd(argv)
282 if not self.cmdexc(argv):
288 """Safely convert the given str/unicode to a string for printing."""
292 #XXX What is the proper encoding to use here? 'utf-8' seems
293 # to work better than "getdefaultencoding" (usually
294 # 'ascii'), on OS X at least.
296 #return s.encode(sys.getdefaultencoding(), "replace")
297 return s.encode("utf-8", "replace")
299 def cmdloop(self, intro=None):
300 """Repeatedly issue a prompt, accept input, parse into an argv, and
301 dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
302 the argv. In other words, start a shell.
304 "intro" (optional) is a introductory message to print when
305 starting the command loop. This overrides the class
306 "intro" attribute, if any.
308 self.cmdlooping = True
313 intro_str = self._str(intro)
314 self.stdout.write(intro_str+'\n')
319 argv = self.cmdqueue.pop(0)
320 assert (isinstance(argv, (list, tuple)),
321 "item on 'cmdqueue' is not a sequence: %r" % argv)
323 if self.use_rawinput:
325 line = raw_input(self._prompt_str)
329 self.stdout.write(self._prompt_str)
331 line = self.stdin.readline()
335 line = line[:-1] # chop '\n'
336 argv = line2argv(line)
338 argv = self.precmd(argv)
339 retval = self.onecmd(argv)
342 if not self.cmdexc(argv):
345 self.lastretval = retval
347 self.cmdlooping = False
350 def precmd(self, argv):
351 """Hook method executed just before the command argv is
352 interpreted, but after the input prompt is generated and issued.
354 "argv" is the cmd to run.
356 Returns an argv to run (i.e. this method can modify the command
361 def postcmd(self, argv):
362 """Hook method executed just after a command dispatch is finished.
364 "argv" is the command that was run.
368 def cmdexc(self, argv):
369 """Called if an exception is raised in any of precmd(), onecmd(),
370 or postcmd(). If True is returned, the exception is deemed to have
371 been dealt with. Otherwise, the exception is re-raised.
373 The default implementation handles CmdlnUserError's, which
374 typically correspond to user error in calling commands (as
375 opposed to programmer error in the design of the script using
379 type, exc, traceback = sys.exc_info()
380 if isinstance(exc, CmdlnUserError):
381 msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
382 % (self.name, argv[0], exc, self.name, argv[0])
383 self.stderr.write(self._str(msg))
387 def onecmd(self, argv):
389 return self.emptyline()
391 cmdname = self._get_canonical_cmd_name(argv[0])
393 handler = self._get_cmd_handler(cmdname)
395 return self._dispatch_cmd(handler, argv)
396 return self.default(argv)
398 def _dispatch_cmd(self, handler, argv):
401 def default(self, argv):
402 """Hook called to handle a command for which there is no handler.
404 "argv" is the command and arguments to run.
406 The default implementation writes and error message to stderr
407 and returns an error exit status.
409 Returns a numeric command exit status.
411 errmsg = self._str(self.unknowncmd % (argv[0],))
413 self.stderr.write(errmsg+"\n")
415 self.stderr.write("%s: %s\nTry '%s help' for info.\n"
416 % (self._name_str, errmsg, self._name_str))
420 def parseline(self, line):
421 # This is used by Cmd.complete (readline completer function) to
422 # massage the current line buffer before completion processing.
423 # We override to drop special '!' handling.
426 return None, None, line
428 line = 'help ' + line[1:]
430 while i < n and line[i] in self.identchars: i = i+1
431 cmd, arg = line[:i], line[i:].strip()
432 return cmd, arg, line
434 def helpdefault(self, cmd, known):
435 """Hook called to handle help on a command for which there is no
438 "cmd" is the command name on which help was requested.
439 "known" is a boolean indicating if this command is known
440 (i.e. if there is a handler for it).
442 Returns a return code.
445 msg = self._str(self.nohelp % (cmd,))
447 self.stderr.write(msg + '\n')
449 self.stderr.write("%s: %s\n" % (self.name, msg))
451 msg = self.unknowncmd % (cmd,)
453 self.stderr.write(msg + '\n')
455 self.stderr.write("%s: %s\n"
456 "Try '%s help' for info.\n"
457 % (self.name, msg, self.name))
461 def do_help(self, argv):
462 """${cmd_name}: give detailed help on a specific sub-command
465 ${name} help [SUBCOMMAND]
467 if len(argv) > 1: # asking for help on a particular command
469 cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1]
471 return self.helpdefault(argv[1], False)
473 helpfunc = getattr(self, "help_"+cmdname, None)
477 handler = self._get_cmd_handler(cmdname)
479 doc = handler.__doc__
481 return self.helpdefault(argv[1], handler != None)
482 else: # bare "help" command
483 doc = self.__class__.__doc__ # try class docstring
485 # Try to provide some reasonable useful default help.
486 if self.cmdlooping: prefix = ""
487 else: prefix = self.name+' '
489 %sSUBCOMMAND [ARGS...]
495 """ % (prefix, prefix)
498 if doc: # *do* have help content, massage and print that
499 doc = self._help_reindent(doc)
500 doc = self._help_preprocess(doc, cmdname)
501 doc = doc.rstrip() + '\n' # trim down trailing space
502 self.stdout.write(self._str(doc))
504 do_help.aliases = ["?"]
506 def _help_reindent(self, help, indent=None):
507 """Hook to re-indent help strings before writing to stdout.
509 "help" is the help content to re-indent
510 "indent" is a string with which to indent each line of the
511 help content after normalizing. If unspecified or None
512 then the default is use: the 'self.helpindent' class
513 attribute. By default this is the empty string, i.e.
516 By default, all common leading whitespace is removed and then
517 the lot is indented by 'self.helpindent'. When calculating the
518 common leading whitespace the first line is ignored -- hence
519 help content for Conan can be written as follows and have the
520 expected indentation:
522 def do_crush(self, ...):
523 '''${cmd_name}: crush your enemies, see them driven before you...
525 c.f. Conan the Barbarian'''
528 indent = self.helpindent
529 lines = help.splitlines(0)
530 _dedentlines(lines, skip_first_line=True)
531 lines = [(indent+line).rstrip() for line in lines]
532 return '\n'.join(lines)
534 def _help_preprocess(self, help, cmdname):
535 """Hook to preprocess a help string before writing to stdout.
537 "help" is the help string to process.
538 "cmdname" is the canonical sub-command name for which help
539 is being given, or None if the help is not specific to a
542 By default the following template variables are interpolated in
543 help content. (Note: these are similar to Python 2.4's
544 string.Template interpolation but not quite.)
547 The tool's/shell's name, i.e. 'self.name'.
549 A formatted table of options for this shell/tool.
551 A formatted table of available sub-commands.
553 A formatted table of additional help topics (i.e. 'help_*'
554 methods with no matching 'do_*' method).
556 The name (and aliases) for this sub-command formatted as:
557 "NAME (ALIAS1, ALIAS2, ...)".
559 A formatted usage block inferred from the command function
562 A formatted table of options for this sub-command. (This is
563 only available for commands using the optparse integration,
564 i.e. using @cmdln.option decorators or manually setting the
565 'optparser' attribute on the 'do_*' method.)
567 Returns the processed help.
570 "${name}": self._help_preprocess_name,
571 "${option_list}": self._help_preprocess_option_list,
572 "${command_list}": self._help_preprocess_command_list,
573 "${help_list}": self._help_preprocess_help_list,
574 "${cmd_name}": self._help_preprocess_cmd_name,
575 "${cmd_usage}": self._help_preprocess_cmd_usage,
576 "${cmd_option_list}": self._help_preprocess_cmd_option_list,
579 for marker, preprocessor in preprocessors.items():
581 help = preprocessor(help, cmdname)
584 def _help_preprocess_name(self, help, cmdname=None):
585 return help.replace("${name}", self.name)
587 def _help_preprocess_option_list(self, help, cmdname=None):
588 marker = "${option_list}"
589 indent, indent_width = _get_indent(marker, help)
590 suffix = _get_trailing_whitespace(marker, help)
593 # Setup formatting options and format.
594 # - Indentation of 4 is better than optparse default of 2.
595 # C.f. Damian Conway's discussion of this in Perl Best
597 self.optparser.formatter.indent_increment = 4
598 self.optparser.formatter.current_indent = indent_width
599 block = self.optparser.format_option_help() + '\n'
603 help = help.replace(indent+marker+suffix, block, 1)
607 def _help_preprocess_command_list(self, help, cmdname=None):
608 marker = "${command_list}"
609 indent, indent_width = _get_indent(marker, help)
610 suffix = _get_trailing_whitespace(marker, help)
612 # Find any aliases for commands.
613 token2canonical = self._get_canonical_map()
615 for token, cmdname in token2canonical.items():
616 if token == cmdname: continue
617 aliases.setdefault(cmdname, []).append(token)
619 # Get the list of (non-hidden) commands and their
620 # documentation, if any.
621 cmdnames = {} # use a dict to strip duplicates
622 for attr in self.get_names():
623 if attr.startswith("do_"):
624 cmdnames[attr[3:]] = True
625 cmdnames = cmdnames.keys()
628 for cmdname in cmdnames:
629 if aliases.get(cmdname):
632 cmdstr = "%s (%s)" % (cmdname, ", ".join(a))
637 helpfunc = getattr(self, 'help_'+cmdname)
638 except AttributeError:
639 handler = self._get_cmd_handler(cmdname)
641 doc = handler.__doc__
645 # Strip "${cmd_name}: " from the start of a command's doc. Best
646 # practice dictates that command help strings begin with this, but
647 # it isn't at all wanted for the command list.
648 to_strip = "${cmd_name}:"
649 if doc and doc.startswith(to_strip):
650 #log.debug("stripping %r from start of %s's help string",
652 doc = doc[len(to_strip):].lstrip()
653 linedata.append( (cmdstr, doc) )
656 subindent = indent + ' '*4
657 lines = _format_linedata(linedata, subindent, indent_width+4)
658 block = indent + "commands:\n" \
659 + '\n'.join(lines) + "\n\n"
660 help = help.replace(indent+marker+suffix, block, 1)
663 def _help_preprocess_help_list(self, help, cmdname=None):
664 marker = "${help_list}"
665 indent, indent_width = _get_indent(marker, help)
666 suffix = _get_trailing_whitespace(marker, help)
668 # Determine the additional help topics, if any.
670 token2cmdname = self._get_canonical_map()
671 for attr in self.get_names():
672 if not attr.startswith("help_"): continue
674 if helpname not in token2cmdname:
675 helpnames[helpname] = True
678 helpnames = helpnames.keys()
680 linedata = [(self.name+" help "+n, "") for n in helpnames]
682 subindent = indent + ' '*4
683 lines = _format_linedata(linedata, subindent, indent_width+4)
684 block = indent + "additional help topics:\n" \
685 + '\n'.join(lines) + "\n\n"
688 help = help.replace(indent+marker+suffix, block, 1)
691 def _help_preprocess_cmd_name(self, help, cmdname=None):
692 marker = "${cmd_name}"
693 handler = self._get_cmd_handler(cmdname)
695 raise CmdlnError("cannot preprocess '%s' into help string: "
696 "could not find command handler for %r"
699 if hasattr(handler, "aliases"):
700 s += " (%s)" % (", ".join(handler.aliases))
701 help = help.replace(marker, s)
704 #TODO: this only makes sense as part of the Cmdln class.
705 # Add hooks to add help preprocessing template vars and put
706 # this one on that class.
707 def _help_preprocess_cmd_usage(self, help, cmdname=None):
708 marker = "${cmd_usage}"
709 handler = self._get_cmd_handler(cmdname)
711 raise CmdlnError("cannot preprocess '%s' into help string: "
712 "could not find command handler for %r"
714 indent, indent_width = _get_indent(marker, help)
715 suffix = _get_trailing_whitespace(marker, help)
717 # Extract the introspection bits we need.
718 func = handler.im_func
719 if func.func_defaults:
720 func_defaults = list(func.func_defaults)
723 co_argcount = func.func_code.co_argcount
724 co_varnames = func.func_code.co_varnames
725 co_flags = func.func_code.co_flags
729 # Adjust argcount for possible *args and **kwargs arguments.
730 argcount = co_argcount
731 if co_flags & CO_FLAGS_ARGS: argcount += 1
732 if co_flags & CO_FLAGS_KWARGS: argcount += 1
734 # Determine the usage string.
735 usage = "%s %s" % (self.name, cmdname)
736 if argcount <= 2: # handler ::= do_FOO(self, argv)
737 usage += " [ARGS...]"
738 elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...)
739 argnames = list(co_varnames[3:argcount])
741 if co_flags & CO_FLAGS_KWARGS:
742 name = argnames.pop(-1)
744 # There is no generally accepted mechanism for passing
745 # keyword arguments from the command line. Could
746 # *perhaps* consider: arg=value arg2=value2 ...
747 warnings.warn("argument '**%s' on '%s.%s' command "
748 "handler will never get values"
749 % (name, self.__class__.__name__,
751 if co_flags & CO_FLAGS_ARGS:
752 name = argnames.pop(-1)
753 tail = "[%s...]" % name.upper()
755 func_defaults.pop(-1)
756 name = argnames.pop(-1)
757 tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail)
759 name = argnames.pop(-1)
760 tail = "%s %s" % (name.upper(), tail)
764 self.helpindent + "usage:",
765 self.helpindent + ' '*4 + usage
767 block = '\n'.join(block_lines) + '\n\n'
769 help = help.replace(indent+marker+suffix, block, 1)
772 #TODO: this only makes sense as part of the Cmdln class.
773 # Add hooks to add help preprocessing template vars and put
774 # this one on that class.
775 def _help_preprocess_cmd_option_list(self, help, cmdname=None):
776 marker = "${cmd_option_list}"
777 handler = self._get_cmd_handler(cmdname)
779 raise CmdlnError("cannot preprocess '%s' into help string: "
780 "could not find command handler for %r"
782 indent, indent_width = _get_indent(marker, help)
783 suffix = _get_trailing_whitespace(marker, help)
784 if hasattr(handler, "optparser"):
785 # Setup formatting options and format.
786 # - Indentation of 4 is better than optparse default of 2.
787 # C.f. Damian Conway's discussion of this in Perl Best
789 handler.optparser.formatter.indent_increment = 4
790 handler.optparser.formatter.current_indent = indent_width
791 block = handler.optparser.format_option_help() + '\n'
795 help = help.replace(indent+marker+suffix, block, 1)
798 def _get_canonical_cmd_name(self, token):
799 map = self._get_canonical_map()
800 return map.get(token, None)
802 def _get_canonical_map(self):
803 """Return a mapping of available command names and aliases to
804 their canonical command name.
806 cacheattr = "_token2canonical"
807 if not hasattr(self, cacheattr):
808 # Get the list of commands and their aliases, if any.
810 cmd2funcname = {} # use a dict to strip duplicates
811 for attr in self.get_names():
812 if attr.startswith("do_"): cmdname = attr[3:]
813 elif attr.startswith("_do_"): cmdname = attr[4:]
816 cmd2funcname[cmdname] = attr
817 token2canonical[cmdname] = cmdname
818 for cmdname, funcname in cmd2funcname.items(): # add aliases
819 func = getattr(self, funcname)
820 aliases = getattr(func, "aliases", [])
821 for alias in aliases:
822 if alias in cmd2funcname:
824 warnings.warn("'%s' alias for '%s' command conflicts "
826 % (alias, cmdname, cmd2funcname[alias]))
828 token2canonical[alias] = cmdname
829 setattr(self, cacheattr, token2canonical)
830 return getattr(self, cacheattr)
832 def _get_cmd_handler(self, cmdname):
835 handler = getattr(self, 'do_' + cmdname)
836 except AttributeError:
838 # Private command handlers begin with "_do_".
839 handler = getattr(self, '_do_' + cmdname)
840 except AttributeError:
844 def _do_EOF(self, argv):
845 # Default EOF handler
846 # Note: an actual EOF is redirected to this command.
847 #TODO: separate name for this. Currently it is available from
848 # command-line. Is that okay?
849 self.stdout.write('\n')
854 # Different from cmd.Cmd: don't repeat the last command for an
859 return self.do_help(["help"])
862 #---- optparse.py extension to fix (IMO) some deficiencies
864 # See the class _OptionParserEx docstring for details.
867 class StopOptionProcessing(Exception):
868 """Indicate that option *and argument* processing should stop
869 cleanly. This is not an error condition. It is similar in spirit to
870 StopIteration. This is raised by _OptionParserEx's default "help"
871 and "version" option actions and can be raised by custom option
874 Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
877 parser = CmdlnOptionParser(mycmd)
878 parser.add_option("-f", "--force", dest="force")
881 opts, args = parser.parse_args()
882 except StopOptionProcessing:
883 # normal termination, "--help" was probably given
887 class _OptionParserEx(optparse.OptionParser):
888 """An optparse.OptionParser that uses exceptions instead of sys.exit.
890 This class is an extension of optparse.OptionParser that differs
892 - Correct (IMO) the default OptionParser error handling to never
893 sys.exit(). Instead OptParseError exceptions are passed through.
894 - Add the StopOptionProcessing exception (a la StopIteration) to
895 indicate normal termination of option processing.
896 See StopOptionProcessing's docstring for details.
898 I'd also like to see the following in the core optparse.py, perhaps
899 as a RawOptionParser which would serve as a base class for the more
900 generally used OptionParser (that works as current):
901 - Remove the implicit addition of the -h|--help and --version
902 options. They can get in the way (e.g. if want '-?' and '-V' for
903 these as well) and it is not hard to do:
904 optparser.add_option("-h", "--help", action="help")
905 optparser.add_option("--version", action="version")
906 These are good practices, just not valid defaults if they can
909 def error(self, msg):
910 raise optparse.OptParseError(msg)
912 def exit(self, status=0, msg=None):
914 raise StopOptionProcessing(msg)
916 #TODO: don't lose status info here
917 raise optparse.OptParseError(msg)
921 #---- optparse.py-based option processing support
923 class CmdlnOptionParser(_OptionParserEx):
924 """An optparse.OptionParser class more appropriate for top-level
925 Cmdln options. For parsing of sub-command options, see
929 - disable_interspersed_args() by default, because a Cmdln instance
930 has sub-commands which may themselves have options.
931 - Redirect print_help() to the Cmdln.do_help() which is better
932 equiped to handle the "help" action.
933 - error() will raise a CmdlnUserError: OptionParse.error() is meant
934 to be called for user errors. Raising a well-known error here can
935 make error handling clearer.
936 - Also see the changes in _OptionParserEx.
938 def __init__(self, cmdln, **kwargs):
940 kwargs["prog"] = self.cmdln.name
941 _OptionParserEx.__init__(self, **kwargs)
942 self.disable_interspersed_args()
944 def print_help(self, file=None):
945 self.cmdln.onecmd(["help"])
947 def error(self, msg):
948 raise CmdlnUserError(msg)
951 class SubCmdOptionParser(_OptionParserEx):
952 def set_cmdln_info(self, cmdln, subcmd):
953 """Called by Cmdln to pass relevant info about itself needed
959 def print_help(self, file=None):
960 self.cmdln.onecmd(["help", self.subcmd])
962 def error(self, msg):
963 raise CmdlnUserError(msg)
966 def option(*args, **kwargs):
967 """Decorator to add an option to the optparser argument of a Cmdln
971 class MyShell(cmdln.Cmdln):
972 @cmdln.option("-f", "--force", help="force removal")
973 def do_remove(self, subcmd, opts, *args):
976 #XXX Is there a possible optimization for many options to not have a
977 # large stack depth here?
979 if not hasattr(f, "optparser"):
980 f.optparser = SubCmdOptionParser()
981 f.optparser.add_option(*args, **kwargs)
986 class Cmdln(RawCmdln):
987 """An improved (on cmd.Cmd) framework for building multi-subcommand
988 scripts (think "svn" & "cvs") and simple shells (think "pdb" and
995 class MySVN(cmdln.Cmdln):
998 @cmdln.aliases('stat', 'st')
999 @cmdln.option('-v', '--verbose', action='store_true'
1000 help='print verbose information')
1001 def do_status(self, subcmd, opts, *paths):
1002 print "handle 'svn status' command"
1006 if __name__ == "__main__":
1008 retval = shell.main()
1011 'Cmdln' extends 'RawCmdln' by providing optparse option processing
1012 integration. See this class' _dispatch_cmd() docstring and
1013 <http://trentm.com/projects/cmdln> for more information.
1015 def _dispatch_cmd(self, handler, argv):
1016 """Introspect sub-command handler signature to determine how to
1017 dispatch the command. The raw handler provided by the base
1018 'RawCmdln' class is still supported:
1020 def do_foo(self, argv):
1021 # 'argv' is the vector of command line args, argv[0] is
1022 # the command name itself (i.e. "foo" or an alias)
1025 In addition, if the handler has more than 2 arguments option
1026 processing is automatically done (using optparse):
1028 @cmdln.option('-v', '--verbose', action='store_true')
1029 def do_bar(self, subcmd, opts, *args):
1030 # subcmd = <"bar" or an alias>
1031 # opts = <an optparse.Values instance>
1033 print "lots of debugging output..."
1034 # args = <tuple of arguments>
1038 TODO: explain that "*args" can be other signatures as well.
1040 The `cmdln.option` decorator corresponds to an `add_option()`
1041 method call on an `optparse.OptionParser` instance.
1043 You can declare a specific number of arguments:
1045 @cmdln.option('-v', '--verbose', action='store_true')
1046 def do_bar2(self, subcmd, opts, bar_one, bar_two):
1049 and an appropriate error message will be raised/printed if the
1050 command is called with a different number of args.
1052 co_argcount = handler.im_func.func_code.co_argcount
1053 if co_argcount == 2: # handler ::= do_foo(self, argv)
1054 return handler(argv)
1055 elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
1057 optparser = handler.optparser
1058 except AttributeError:
1059 optparser = handler.im_func.optparser = SubCmdOptionParser()
1060 assert isinstance(optparser, SubCmdOptionParser)
1061 optparser.set_cmdln_info(self, argv[0])
1063 opts, args = optparser.parse_args(argv[1:])
1064 except StopOptionProcessing:
1065 #TODO: this doesn't really fly for a replacement of
1066 # optparse.py behaviour, does it?
1067 return 0 # Normal command termination
1070 return handler(argv[0], opts, *args)
1071 except TypeError, ex:
1072 # Some TypeError's are user errors:
1073 # do_foo() takes at least 4 arguments (3 given)
1074 # do_foo() takes at most 5 arguments (6 given)
1075 # do_foo() takes exactly 5 arguments (6 given)
1076 # Raise CmdlnUserError for these with a suitably
1077 # massaged error message.
1079 tb = sys.exc_info()[2] # the traceback object
1080 if tb.tb_next is not None:
1081 # If the traceback is more than one level deep, then the
1082 # TypeError do *not* happen on the "handler(...)" call
1083 # above. In that we don't want to handle it specially
1084 # here: it would falsely mask deeper code errors.
1087 match = _INCORRECT_NUM_ARGS_RE.search(msg)
1089 msg = list(match.groups())
1090 msg[1] = int(msg[1]) - 3
1092 msg[2] = msg[2].replace("arguments", "argument")
1093 msg[3] = int(msg[3]) - 3
1094 msg = ''.join(map(str, msg))
1095 raise CmdlnUserError(msg)
1099 raise CmdlnError("incorrect argcount for %s(): takes %d, must "
1100 "take 2 for 'argv' signature or 3+ for 'opts' "
1101 "signature" % (handler.__name__, co_argcount))
1105 #---- internal support functions
1107 def _format_linedata(linedata, indent, indent_width):
1108 """Format specific linedata into a pleasant layout.
1110 "linedata" is a list of 2-tuples of the form:
1111 (<item-display-string>, <item-docstring>)
1112 "indent" is a string to use for one level of indentation
1113 "indent_width" is a number of columns by which the
1114 formatted data will be indented when printed.
1116 The <item-display-string> column is held to 15 columns.
1119 WIDTH = 78 - indent_width
1123 NAME_WIDTH = min(max([len(s) for s,d in linedata]), MAX_NAME_WIDTH)
1124 DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING
1125 for namestr, doc in linedata:
1126 line = indent + namestr
1127 if len(namestr) <= NAME_WIDTH:
1128 line += ' ' * (NAME_WIDTH + SPACING - len(namestr))
1131 line = indent + ' ' * (NAME_WIDTH + SPACING)
1132 line += _summarize_doc(doc, DOC_WIDTH)
1133 lines.append(line.rstrip())
1136 def _summarize_doc(doc, length=60):
1137 r"""Parse out a short one line summary from the given doclines.
1139 "doc" is the doc string to summarize.
1140 "length" is the max length for the summary
1142 >>> _summarize_doc("this function does this")
1143 'this function does this'
1144 >>> _summarize_doc("this function does this", 10)
1146 >>> _summarize_doc("this function does this\nand that")
1147 'this function does this and that'
1148 >>> _summarize_doc("this function does this\n\nand that")
1149 'this function does this'
1154 assert length > 3, "length <= 3 is absurdly short for a doc summary"
1155 doclines = doc.strip().splitlines(0)
1160 for i, line in enumerate(doclines):
1161 stripped = line.strip()
1164 summlines.append(stripped)
1165 if len(''.join(summlines)) >= length:
1168 summary = ' '.join(summlines)
1169 if len(summary) > length:
1170 summary = summary[:length-3] + "..."
1174 def line2argv(line):
1175 r"""Parse the given line into an argument vector.
1177 "line" is the line of input to parse.
1179 This may get niggly when dealing with quoting and escaping. The
1180 current state of this parsing may not be completely thorough/correct
1183 >>> from cmdln import line2argv
1184 >>> line2argv("foo")
1186 >>> line2argv("foo bar")
1188 >>> line2argv("foo bar ")
1190 >>> line2argv(" foo bar")
1195 >>> line2argv("'foo bar'")
1197 >>> line2argv('"foo bar"')
1199 >>> line2argv(r'"foo\"bar"')
1201 >>> line2argv("'foo bar' spam")
1203 >>> line2argv("'foo 'bar spam")
1205 >>> line2argv("'foo")
1206 Traceback (most recent call last):
1208 ValueError: command line is not terminated: unfinished single-quoted segment
1209 >>> line2argv('"foo')
1210 Traceback (most recent call last):
1212 ValueError: command line is not terminated: unfinished double-quoted segment
1213 >>> line2argv('some\tsimple\ttests')
1214 ['some', 'simple', 'tests']
1215 >>> line2argv('a "more complex" test')
1216 ['a', 'more complex', 'test']
1217 >>> line2argv('a more="complex test of " quotes')
1218 ['a', 'more=complex test of ', 'quotes']
1219 >>> line2argv('a more" complex test of " quotes')
1220 ['a', 'more complex test of ', 'quotes']
1221 >>> line2argv('an "embedded \\"quote\\""')
1222 ['an', 'embedded "quote"']
1228 arg = None # the current argument being parsed
1232 if i >= len(line): break
1235 if ch == "\\": # escaped char always added to arg, regardless of state
1236 if arg is None: arg = ""
1241 if state == "single-quoted":
1246 elif state == "double-quoted":
1251 elif state == "default":
1253 if arg is None: arg = ""
1254 state = "double-quoted"
1256 if arg is None: arg = ""
1257 state = "single-quoted"
1258 elif ch in string.whitespace:
1263 if arg is None: arg = ""
1267 if state != "default":
1268 raise ValueError("command line is not terminated: unfinished %s "
1273 def argv2line(argv):
1274 r"""Put together the given argument vector into a command line.
1276 "argv" is the argument vector to process.
1278 >>> from cmdln import argv2line
1279 >>> argv2line(['foo'])
1281 >>> argv2line(['foo', 'bar'])
1283 >>> argv2line(['foo', 'bar baz'])
1285 >>> argv2line(['foo"bar'])
1287 >>> print argv2line(['foo" bar'])
1289 >>> print argv2line(["foo' bar"])
1291 >>> argv2line(["foo'bar"])
1296 if ' ' in arg and '"' not in arg:
1298 elif ' ' in arg and "'" not in arg:
1301 arg = arg.replace('"', r'\"')
1303 escapedArgs.append(arg)
1304 return ' '.join(escapedArgs)
1307 # Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
1308 def _dedentlines(lines, tabsize=8, skip_first_line=False):
1309 """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
1311 "lines" is a list of lines to dedent.
1312 "tabsize" is the tab width to use for indent width calculations.
1313 "skip_first_line" is a boolean indicating if the first line should
1314 be skipped for calculating the indent width and for dedenting.
1315 This is sometimes useful for docstrings and similar.
1317 Same as dedent() except operates on a sequence of lines. Note: the
1318 lines list is modified **in-place**.
1322 print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
1323 % (tabsize, skip_first_line)
1326 for i, line in enumerate(lines):
1327 if i == 0 and skip_first_line: continue
1333 indent += tabsize - (indent % tabsize)
1335 continue # skip all-whitespace lines
1339 continue # skip all-whitespace lines
1340 if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
1344 margin = min(margin, indent)
1345 if DEBUG: print "dedent: margin=%r" % margin
1347 if margin is not None and margin > 0:
1348 for i, line in enumerate(lines):
1349 if i == 0 and skip_first_line: continue
1351 for j, ch in enumerate(line):
1355 removed += tabsize - (removed % tabsize)
1357 if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
1358 lines[i] = lines[i][j:]
1361 raise ValueError("unexpected non-whitespace char %r in "
1362 "line %r while removing %d-space margin"
1363 % (ch, line, margin))
1365 print "dedent: %r: %r -> removed %d/%d"\
1366 % (line, ch, removed, margin)
1367 if removed == margin:
1368 lines[i] = lines[i][j+1:]
1370 elif removed > margin:
1371 lines[i] = ' '*(removed-margin) + lines[i][j+1:]
1375 def _dedent(text, tabsize=8, skip_first_line=False):
1376 """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text
1378 "text" is the text to dedent.
1379 "tabsize" is the tab width to use for indent width calculations.
1380 "skip_first_line" is a boolean indicating if the first line should
1381 be skipped for calculating the indent width and for dedenting.
1382 This is sometimes useful for docstrings and similar.
1384 textwrap.dedent(s), but don't expand tabs to spaces
1386 lines = text.splitlines(1)
1387 _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line)
1388 return ''.join(lines)
1391 def _get_indent(marker, s, tab_width=8):
1392 """_get_indent(marker, s, tab_width=8) ->
1393 (<indentation-of-'marker'>, <indentation-width>)"""
1394 # Figure out how much the marker is indented.
1395 INDENT_CHARS = tuple(' \t')
1396 start = s.index(marker)
1399 if s[i-1] not in INDENT_CHARS:
1408 indent_width += tab_width - (indent_width % tab_width)
1409 return indent, indent_width
1411 def _get_trailing_whitespace(marker, s):
1412 """Return the whitespace content trailing the given 'marker' in string 's',
1413 up to and including a newline.
1416 start = s.index(marker) + len(marker)
1421 elif s[i] in '\r\n':
1423 if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n':