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