| "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() |