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