| 1 | "Framework for command line interfaces like CVS. See class CmdFrameWork."
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 | class CommandFrameWork:
|
|---|
| 5 |
|
|---|
| 6 | """Framework class for command line interfaces like CVS.
|
|---|
| 7 |
|
|---|
| 8 | The general command line structure is
|
|---|
| 9 |
|
|---|
| 10 | command [flags] subcommand [subflags] [argument] ...
|
|---|
| 11 |
|
|---|
| 12 | There's a class variable GlobalFlags which specifies the
|
|---|
| 13 | global flags options. Subcommands are defined by defining
|
|---|
| 14 | methods named do_<subcommand>. Flags for the subcommand are
|
|---|
| 15 | defined by defining class or instance variables named
|
|---|
| 16 | flags_<subcommand>. If there's no command, method default()
|
|---|
| 17 | is called. The __doc__ strings for the do_ methods are used
|
|---|
| 18 | for the usage message, printed after the general usage message
|
|---|
| 19 | which is the class variable UsageMessage. The class variable
|
|---|
| 20 | PostUsageMessage is printed after all the do_ methods' __doc__
|
|---|
| 21 | strings. The method's return value can be a suggested exit
|
|---|
| 22 | status. [XXX Need to rewrite this to clarify it.]
|
|---|
| 23 |
|
|---|
| 24 | Common usage is to derive a class, instantiate it, and then call its
|
|---|
| 25 | run() method; by default this takes its arguments from sys.argv[1:].
|
|---|
| 26 | """
|
|---|
| 27 |
|
|---|
| 28 | UsageMessage = \
|
|---|
| 29 | "usage: (name)s [flags] subcommand [subflags] [argument] ..."
|
|---|
| 30 |
|
|---|
| 31 | PostUsageMessage = None
|
|---|
| 32 |
|
|---|
| 33 | GlobalFlags = ''
|
|---|
| 34 |
|
|---|
| 35 | def __init__(self):
|
|---|
| 36 | """Constructor, present for completeness."""
|
|---|
| 37 | pass
|
|---|
| 38 |
|
|---|
| 39 | def run(self, args = None):
|
|---|
| 40 | """Process flags, subcommand and options, then run it."""
|
|---|
| 41 | import getopt, sys
|
|---|
| 42 | if args is None: args = sys.argv[1:]
|
|---|
| 43 | try:
|
|---|
| 44 | opts, args = getopt.getopt(args, self.GlobalFlags)
|
|---|
| 45 | except getopt.error, msg:
|
|---|
| 46 | return self.usage(msg)
|
|---|
| 47 | self.options(opts)
|
|---|
| 48 | if not args:
|
|---|
| 49 | self.ready()
|
|---|
| 50 | return self.default()
|
|---|
| 51 | else:
|
|---|
| 52 | cmd = args[0]
|
|---|
| 53 | mname = 'do_' + cmd
|
|---|
| 54 | fname = 'flags_' + cmd
|
|---|
| 55 | try:
|
|---|
| 56 | method = getattr(self, mname)
|
|---|
| 57 | except AttributeError:
|
|---|
| 58 | return self.usage("command %r unknown" % (cmd,))
|
|---|
| 59 | try:
|
|---|
| 60 | flags = getattr(self, fname)
|
|---|
| 61 | except AttributeError:
|
|---|
| 62 | flags = ''
|
|---|
| 63 | try:
|
|---|
| 64 | opts, args = getopt.getopt(args[1:], flags)
|
|---|
| 65 | except getopt.error, msg:
|
|---|
| 66 | return self.usage(
|
|---|
| 67 | "subcommand %s: " % cmd + str(msg))
|
|---|
| 68 | self.ready()
|
|---|
| 69 | return method(opts, args)
|
|---|
| 70 |
|
|---|
| 71 | def options(self, opts):
|
|---|
| 72 | """Process the options retrieved by getopt.
|
|---|
| 73 | Override this if you have any options."""
|
|---|
| 74 | if opts:
|
|---|
| 75 | print "-"*40
|
|---|
| 76 | print "Options:"
|
|---|
| 77 | for o, a in opts:
|
|---|
| 78 | print 'option', o, 'value', repr(a)
|
|---|
| 79 | print "-"*40
|
|---|
| 80 |
|
|---|
| 81 | def ready(self):
|
|---|
| 82 | """Called just before calling the subcommand."""
|
|---|
| 83 | pass
|
|---|
| 84 |
|
|---|
| 85 | def usage(self, msg = None):
|
|---|
| 86 | """Print usage message. Return suitable exit code (2)."""
|
|---|
| 87 | if msg: print msg
|
|---|
| 88 | print self.UsageMessage % {'name': self.__class__.__name__}
|
|---|
| 89 | docstrings = {}
|
|---|
| 90 | c = self.__class__
|
|---|
| 91 | while 1:
|
|---|
| 92 | for name in dir(c):
|
|---|
| 93 | if name[:3] == 'do_':
|
|---|
| 94 | if docstrings.has_key(name):
|
|---|
| 95 | continue
|
|---|
| 96 | try:
|
|---|
| 97 | doc = getattr(c, name).__doc__
|
|---|
| 98 | except:
|
|---|
| 99 | doc = None
|
|---|
| 100 | if doc:
|
|---|
| 101 | docstrings[name] = doc
|
|---|
| 102 | if not c.__bases__:
|
|---|
| 103 | break
|
|---|
| 104 | c = c.__bases__[0]
|
|---|
| 105 | if docstrings:
|
|---|
| 106 | print "where subcommand can be:"
|
|---|
| 107 | names = docstrings.keys()
|
|---|
| 108 | names.sort()
|
|---|
| 109 | for name in names:
|
|---|
| 110 | print docstrings[name]
|
|---|
| 111 | if self.PostUsageMessage:
|
|---|
| 112 | print self.PostUsageMessage
|
|---|
| 113 | return 2
|
|---|
| 114 |
|
|---|
| 115 | def default(self):
|
|---|
| 116 | """Default method, called when no subcommand is given.
|
|---|
| 117 | You should always override this."""
|
|---|
| 118 | print "Nobody expects the Spanish Inquisition!"
|
|---|
| 119 |
|
|---|
| 120 |
|
|---|
| 121 | def test():
|
|---|
| 122 | """Test script -- called when this module is run as a script."""
|
|---|
| 123 | import sys
|
|---|
| 124 | class Hello(CommandFrameWork):
|
|---|
| 125 | def do_hello(self, opts, args):
|
|---|
| 126 | "hello -- print 'hello world', needs no arguments"
|
|---|
| 127 | print "Hello, world"
|
|---|
| 128 | x = Hello()
|
|---|
| 129 | tests = [
|
|---|
| 130 | [],
|
|---|
| 131 | ['hello'],
|
|---|
| 132 | ['spam'],
|
|---|
| 133 | ['-x'],
|
|---|
| 134 | ['hello', '-x'],
|
|---|
| 135 | None,
|
|---|
| 136 | ]
|
|---|
| 137 | for t in tests:
|
|---|
| 138 | print '-'*10, t, '-'*10
|
|---|
| 139 | sts = x.run(t)
|
|---|
| 140 | print "Exit status:", repr(sts)
|
|---|
| 141 |
|
|---|
| 142 |
|
|---|
| 143 | if __name__ == '__main__':
|
|---|
| 144 | test()
|
|---|