"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_. Flags for the subcommand are defined by defining class or instance variables named flags_. 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 as 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 as 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()