1"Framework for command line interfaces like CVS. See class CmdFrameWork." 2 3 4class 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 121def 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 143if __name__ == '__main__': 144 test() 145