Table of Contents
#!/usr/bin/env python import sys import cmdln class Conan(cmdln.Cmdln): pass if __name__ == "__main__": conan = Conan() sys.exit( conan.main() )
This is the absolute bare minimum usage of Cmdln
and, predictably, with
this you don't get much. But you don't get nothing. Save the above to a file
called "conan.py" and try it out. (Or you can download the finished conan.py.):
$ python conan.py Usage: conan.py COMMAND [ARGS...] conan.py help [COMMAND] Options: -h, --help show this help message and exit Commands: help (?) give detailed help on a specific sub-command
Basically you get help. A good command-line tool should make it easy to
figure out how to use the thing. You (mostly) get this for free with
cmdln.py
. The help message is available (as with most good tools) with the
'-h' or '--help' option:
$ python conan.py --help Usage: conan.py COMMAND [ARGS...] ...
And a reasonable error message is shown for incorrect usage:
$ python conan.py -X conan.py: no such option: -X Try 'conan.py help' for info.
Good multi-subcommand tools (like 'svn' and 'conan') also provide a 'help' command that is used to learn about the various subcommands. But first we have to add some.
#!/usr/bin/env python import sys import cmdln class Conan(cmdln.Cmdln): name = "conan" # (1) def do_hello(self, subcmd, opts): # (2) """Conan greets thee""" # (3) print "Ugh!" if __name__ == "__main__": conan = Conan() sys.exit( conan.main() )
We've added the "hello" command to Conan's vocabulary. Things to note:
A "name" value is used in Cmdln's various standard user messages. Generally you want to specify a name matching the command that users use to invoke your script. If not specified it is inferred from
sys.argv
.A command is defined with a
do_COMMANDNAME
method with the following signature:def do_COMMANDNAME(self, subcmd, opts, ...): """help content for the command""" ...
More on the ...'s later.
A command function's docstring acts as its help string.
$ python conan.py Usage: conan COMMAND [ARGS...] conan help COMMAND Options: -h, --help show this help message and exit Commands: hello Conan greets thee help (?) give detailed help on a specific sub-command $ python conan.py hello Ugh!
As mentioned above, Cmdln
provides a "help" command. The "help" command
provides help on other commands:
$ python conan.py help help help (?): give detailed help on a specific sub-command conan help COMMAND $ python conan.py help hello Conan greets thee
#!/usr/bin/env python import sys import cmdln class Conan(cmdln.Cmdln): name = "conan" def do_hello(self, subcmd, opts): """Conan greets thee""" print "Ugh!" def do_crush(self, subcmd, opts, enemy): print "Crush %s!" % enemy if __name__ == "__main__": conan = Conan() sys.exit( conan.main() )
The "hello" command isn't that interesting. Let's work on a "crush"
command to show the facilities that Cmdln
provides. In the first
incarnation "crush" takes one argument:
$ python conan.py crush Trent Crush Trent!
exactly one argument:
$ python conan.py crush conan crush: takes exactly 1 argument (0 given) Try 'conan help crush' for info. $ python conan.py crush Trent Guido conan crush: takes exactly 1 argument (2 given) Try 'conan help crush' for info. $ python conan.py help crush conan: no help on 'crush'
We haven't provided any help for the "crush" command. Let's do that:
... def do_crush(self, subcmd, opts, enemy): """${cmd_name}: crush your enemy! ${cmd_usage} # (1) """ print "Crush %s!" % enemy ...
Giving us:
$ python conan.py help crush crush: crush your enemy! Usage: conan crush ENEMY
The
Cmdln
class tries to make it easy for you to write decent help. It provides a number of template variables that you can use in your command help strings. Here we've used${cmd_usage}
. The authoritative list of these is theRawCmdln._help_preprocess
method incmdln.py
, but here are some of them:${name} The tool's/shell's name, i.e. 'self.name'. ${option_list} A formatted table of options for this shell/tool. ${command_list} A formatted table of available sub-commands. ${help_list} A formatted table of additional help topics (i.e. 'help_*' methods with no matching 'do_*' method). ${cmd_name} The name (and aliases) for this sub-command formatted as: "NAME (ALIAS1, ALIAS2, ...)". ${cmd_usage} A formatted usage block inferred from the command function signature. ${cmd_option_list} A formatted table of options for this sub-command.
Sometimes you'll want to hardcode your own help strings for better documentation, but often these template vars will do a good enough job.
We probably want Conan to be able to crush many enemies and perhaps use different weapons:
@cmdln.option("-w", "--weapon", # (1) help="what weapon should Conan use?") def do_crush(self, subcmd, opts, *enemies): # (2) """${cmd_name}: crush your enemies! ${cmd_usage} ${cmd_option_list} # (3) C.f. Conan the Barbarian. """ action = { None: "Crush", "sword": "Swipe", "spear": "Pierce", "maul": "Crush", }.get(opts.weapon, None) # (4) if not action: print "Conan confused." else: for enemy in enemies: print "%s %s!" % (action, enemy) print "Yargh!"
We've changed a few things here:
We specified the '-w' option for 'crush'. Every command function has an associated
optparser
-- which is an instance ofcmdln.SubCmdOptionParser
(derived fromoptparse.OptionParser
in the Python stdlib). By default each command supports a-h/--help
option. More can be added (as we've done here) with thecmdln.option
decorator. This is synonymous to callingadd_option
on the underlying OptionParser as described here.Note: Decorators were added in Python 2.4 so you'll have to have Python 2.4 or greater to use the
option
decorator. An alternative is to create your ownSubCmdOptionParser
instance and assign it to theoptparser
attribute of the command handler (which is pretty ugly but does the job):def do_crush(self, subcmd, opts, *enemies): # ... do_foo.optparser = cmdln.SubCmdOptionParser() do_foo.optparser.add_option( "-w", "--weapon", help="what weapon should Conan use?")
We've changed the function signature to take a number of enemies using Python's syntax for declaring a variable number of arguments. This tells the underlying dispatcher in
cmdln.py
thatcrush
accepts any number of arguments.We've used the
${cmd_option_list}
template variable. This usesoptparse
's facility to nicely print out the available options and their help strings.The parsed options are given to the third argument -- typically called
opts
. This is a standardoptparse.Values
instance.
Let's try it out:
$ python conan.py help crush crush: crush your enemies! Usage: conan crush [ENEMIES...] Options: -h, --help show this help message and exit -w WEAPON, --weapon=WEAPON what weapon should Conan use? C.f. Conan the Barbarian. $ python conan.py crush Trent Guido Crush Trent! Crush Guido! Yargh! $ python conan.py crush Trent Guido -w spear Pierce Trent! Pierce Guido! Yargh! $ python conan.py crush Trent Guido -w axe Conan confused. $ python conan.py crush Trent Guido -w sword Swipe Trent! Swipe Guido! Yargh!
With options it is often advisable to have both a long (descriptive) name and a short (convenient) one. The same can be nice with commands. You can use aliases for this. Lets show this with a new command:
... class Conan(cmdln.Cmdln): ... @cmdln.alias("what_is_best", "best") def do_what_is_best_in_life(self, subcmd, opts): """${cmd_name}: Big monologue""" print textwrap.dedent("""\ To crush your enemies, see them driven before you, and hear the lamentations of the women.""") ...
Here we've defined two aliases for the what_is_best_in_life
command:
what_is_best
and best
. These will be shown in the list of commands:
$ python conan.py help ... Commands: crush crush your enemies! hello Conan greets thee help (?) give detailed help on a specific sub-command what_is_best_in_life (best, what_is_best) Big monologue
and in the help just for this command:
$ python conan.py help what_is_best what_is_best_in_life (what_is_best, best): Big monologue
Note that the standard help command has ?
as an alias so that last command
could have been written python conan.py ? best
.
We can now ask Conan what is best in life:
$ python conan.py best To crush your enemies, see them driven before you, and hear the lamentations of the women.
Eventually I'll add discussion of the following cmdln.py
features in this
document. Until then, use the source.
- use "loop" option to main to show creating a shell: control prompt, intro message, error messages
- a different example to show "help_list" and separate
help_*
commands - show passing in optparser to main()
- perhaps come back to Conan shell to show overriding the postcmd to make a shell counter: i.e. prompt has an incrementing number
- show usage of CmdlnOptionParser to add more base-level options Explain what is diff about it, i.e. why to use over optparse.OptionParser.
- hidden command _do_*
- "Don't do me any Favours": talk about RawCmdln and (self, argv)-style command signatures