blob: e2edd0a86e66e33763e89ac10946238db415f8c0 [file] [log] [blame]
Guido van Rossum318b80d1995-04-27 23:00:17 +00001"Framework for command line interfaces like CVS. See class CmdFrameWork."
2
3
Guido van Rossum78016d81995-04-27 23:32:47 +00004class CommandFrameWork:
Guido van Rossum318b80d1995-04-27 23:00:17 +00005
Tim Peterse6ddc8b2004-07-18 05:56:09 +00006 """Framework class for command line interfaces like CVS.
Guido van Rossum318b80d1995-04-27 23:00:17 +00007
Tim Peterse6ddc8b2004-07-18 05:56:09 +00008 The general command line structure is
Guido van Rossum318b80d1995-04-27 23:00:17 +00009
Tim Peterse6ddc8b2004-07-18 05:56:09 +000010 command [flags] subcommand [subflags] [argument] ...
Guido van Rossum318b80d1995-04-27 23:00:17 +000011
Tim Peterse6ddc8b2004-07-18 05:56:09 +000012 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.]
Guido van Rossum318b80d1995-04-27 23:00:17 +000023
Tim Peterse6ddc8b2004-07-18 05:56:09 +000024 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 """
Guido van Rossum318b80d1995-04-27 23:00:17 +000027
Tim Peterse6ddc8b2004-07-18 05:56:09 +000028 UsageMessage = \
29 "usage: (name)s [flags] subcommand [subflags] [argument] ..."
Guido van Rossum318b80d1995-04-27 23:00:17 +000030
Tim Peterse6ddc8b2004-07-18 05:56:09 +000031 PostUsageMessage = None
Guido van Rossum78016d81995-04-27 23:32:47 +000032
Tim Peterse6ddc8b2004-07-18 05:56:09 +000033 GlobalFlags = ''
Guido van Rossum318b80d1995-04-27 23:00:17 +000034
Tim Peterse6ddc8b2004-07-18 05:56:09 +000035 def __init__(self):
36 """Constructor, present for completeness."""
37 pass
Guido van Rossum318b80d1995-04-27 23:00:17 +000038
Tim Peterse6ddc8b2004-07-18 05:56:09 +000039 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)
Guido van Rossum318b80d1995-04-27 23:00:17 +000070
Tim Peterse6ddc8b2004-07-18 05:56:09 +000071 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
Guido van Rossum318b80d1995-04-27 23:00:17 +000080
Tim Peterse6ddc8b2004-07-18 05:56:09 +000081 def ready(self):
82 """Called just before calling the subcommand."""
83 pass
Guido van Rossum78016d81995-04-27 23:32:47 +000084
Tim Peterse6ddc8b2004-07-18 05:56:09 +000085 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
Guido van Rossum318b80d1995-04-27 23:00:17 +0000114
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000115 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!"
Guido van Rossum318b80d1995-04-27 23:00:17 +0000119
120
121def test():
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000122 """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)
Guido van Rossum318b80d1995-04-27 23:00:17 +0000141
142
143if __name__ == '__main__':
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000144 test()