blob: 7bcb4616b78e607ced01132a0ef6dac216a4b1e4 [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)
Guido van Rossumb940e112007-01-10 16:19:56 +000045 except getopt.error as msg:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000046 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)
Guido van Rossumb940e112007-01-10 16:19:56 +000065 except getopt.error as msg:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000066 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:
Collin Winter6f2df4d2007-07-17 20:59:35 +000075 print("-"*40)
76 print("Options:")
Tim Peterse6ddc8b2004-07-18 05:56:09 +000077 for o, a in opts:
Collin Winter6f2df4d2007-07-17 20:59:35 +000078 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)."""
Collin Winter6f2df4d2007-07-17 20:59:35 +000087 if msg: print(msg)
88 print(self.UsageMessage % {'name': self.__class__.__name__})
Tim Peterse6ddc8b2004-07-18 05:56:09 +000089 docstrings = {}
90 c = self.__class__
91 while 1:
92 for name in dir(c):
93 if name[:3] == 'do_':
Collin Winter6f2df4d2007-07-17 20:59:35 +000094 if name in docstrings:
Tim Peterse6ddc8b2004-07-18 05:56:09 +000095 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:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000106 print("where subcommand can be:")
107 names = list(docstrings.keys())
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000108 names.sort()
109 for name in names:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000110 print(docstrings[name])
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000111 if self.PostUsageMessage:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000112 print(self.PostUsageMessage)
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000113 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."""
Collin Winter6f2df4d2007-07-17 20:59:35 +0000118 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"
Collin Winter6f2df4d2007-07-17 20:59:35 +0000127 print("Hello, world")
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000128 x = Hello()
129 tests = [
130 [],
131 ['hello'],
132 ['spam'],
133 ['-x'],
134 ['hello', '-x'],
135 None,
136 ]
137 for t in tests:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000138 print('-'*10, t, '-'*10)
Tim Peterse6ddc8b2004-07-18 05:56:09 +0000139 sts = x.run(t)
Collin Winter6f2df4d2007-07-17 20:59:35 +0000140 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()