blob: e2edd0a86e66e33763e89ac10946238db415f8c0 [file] [log] [blame]
"Framework for command line interfaces like CVS. See class CmdFrameWork."
class CommandFrameWork:
"""Framework class for command line interfaces like CVS.
The general command line structure is
command [flags] subcommand [subflags] [argument] ...
There's a class variable GlobalFlags which specifies the
global flags options. Subcommands are defined by defining
methods named do_<subcommand>. Flags for the subcommand are
defined by defining class or instance variables named
flags_<subcommand>. If there's no command, method default()
is called. The __doc__ strings for the do_ methods are used
for the usage message, printed after the general usage message
which is the class variable UsageMessage. The class variable
PostUsageMessage is printed after all the do_ methods' __doc__
strings. The method's return value can be a suggested exit
status. [XXX Need to rewrite this to clarify it.]
Common usage is to derive a class, instantiate it, and then call its
run() method; by default this takes its arguments from sys.argv[1:].
"""
UsageMessage = \
"usage: (name)s [flags] subcommand [subflags] [argument] ..."
PostUsageMessage = None
GlobalFlags = ''
def __init__(self):
"""Constructor, present for completeness."""
pass
def run(self, args = None):
"""Process flags, subcommand and options, then run it."""
import getopt, sys
if args is None: args = sys.argv[1:]
try:
opts, args = getopt.getopt(args, self.GlobalFlags)
except getopt.error, msg:
return self.usage(msg)
self.options(opts)
if not args:
self.ready()
return self.default()
else:
cmd = args[0]
mname = 'do_' + cmd
fname = 'flags_' + cmd
try:
method = getattr(self, mname)
except AttributeError:
return self.usage("command %r unknown" % (cmd,))
try:
flags = getattr(self, fname)
except AttributeError:
flags = ''
try:
opts, args = getopt.getopt(args[1:], flags)
except getopt.error, msg:
return self.usage(
"subcommand %s: " % cmd + str(msg))
self.ready()
return method(opts, args)
def options(self, opts):
"""Process the options retrieved by getopt.
Override this if you have any options."""
if opts:
print "-"*40
print "Options:"
for o, a in opts:
print 'option', o, 'value', repr(a)
print "-"*40
def ready(self):
"""Called just before calling the subcommand."""
pass
def usage(self, msg = None):
"""Print usage message. Return suitable exit code (2)."""
if msg: print msg
print self.UsageMessage % {'name': self.__class__.__name__}
docstrings = {}
c = self.__class__
while 1:
for name in dir(c):
if name[:3] == 'do_':
if docstrings.has_key(name):
continue
try:
doc = getattr(c, name).__doc__
except:
doc = None
if doc:
docstrings[name] = doc
if not c.__bases__:
break
c = c.__bases__[0]
if docstrings:
print "where subcommand can be:"
names = docstrings.keys()
names.sort()
for name in names:
print docstrings[name]
if self.PostUsageMessage:
print self.PostUsageMessage
return 2
def default(self):
"""Default method, called when no subcommand is given.
You should always override this."""
print "Nobody expects the Spanish Inquisition!"
def test():
"""Test script -- called when this module is run as a script."""
import sys
class Hello(CommandFrameWork):
def do_hello(self, opts, args):
"hello -- print 'hello world', needs no arguments"
print "Hello, world"
x = Hello()
tests = [
[],
['hello'],
['spam'],
['-x'],
['hello', '-x'],
None,
]
for t in tests:
print '-'*10, t, '-'*10
sts = x.run(t)
print "Exit status:", repr(sts)
if __name__ == '__main__':
test()