1 """ |
1 """ |
2 Command implementations |
2 Command-line based command implementation, handling option/argument parsing. |
3 """ |
3 """ |
4 |
4 |
5 import inspect, logging, traceback, concurrent |
5 import concurrent |
6 |
6 |
7 class CommandList (object) : |
7 import inspect, itertools, logging, traceback, optparse |
8 """ |
8 |
9 A list of available Commands |
9 def optify (symbol) : |
10 |
10 """ |
11 XXX: not yet used |
11 Turns the given python symbol into the suitable version for use as a command-line argument. |
12 """ |
12 |
13 |
13 In other words, this replaces '_' with '-'. |
14 def __init__ (self, commands) : |
14 """ |
15 """ |
15 |
16 Store with given initial commands |
16 return symbol.replace('_', '-') |
17 """ |
17 |
18 |
18 # wrapper around optparse.make_option |
19 self.list = commands |
19 Option = optparse.make_option |
20 self.dict = dict((cmd.name, cmd) for cmd in commands) |
|
21 |
|
22 def lookup (self, name) : |
|
23 """ |
|
24 Lookup a command by name |
|
25 """ |
|
26 |
|
27 return self.dict[name] |
|
28 |
20 |
29 class Command (object) : |
21 class Command (object) : |
30 """ |
22 """ |
31 A Command is simply a function that can be executed from the command line with some options/arguments |
23 A Command is simply a function that can be executed from the command line with some options/arguments |
32 """ |
24 """ |
33 |
25 |
34 def __init__ (self, name, func, doc=None) : |
26 def __init__ (self, name, func, doc=None, options=None) : |
35 """ |
27 """ |
36 Create a new Command |
28 Create a new Command |
37 |
29 |
38 name - the name of the command |
30 name - the name of the command |
39 func - the callable python function |
31 func - the callable python function |
40 doc - descriptive help text |
32 doc - descriptive help text |
41 """ |
33 options - named options as Option objects |
42 |
34 """ |
|
35 |
43 self.name = name |
36 self.name = name |
44 self.func = func |
37 self.func = func |
45 self.doc = doc |
38 self.doc = doc |
46 |
39 self.options = options |
47 def setup (self, config, gallery) : |
40 |
48 """ |
41 def parse_args (self, args) : |
49 Run the command with the given context |
42 """ |
50 """ |
43 Pre-parse the given arguments list. |
51 |
44 """ |
52 return CommandContext(self, config, gallery) |
45 |
53 |
46 return args |
54 class CommandContext (object) : |
47 |
55 """ |
48 def apply (self, config, gallery, options, *args, **kwargs) : |
56 A CommandContext is the context that a Command executes in |
49 """ |
57 |
50 Construct a CommandContext for execution of this command with the given environment. |
58 It is bound to a Configuration and a Gallery. |
51 the command with the given context. |
59 """ |
52 """ |
60 |
53 |
61 def __init__ (self, command, config, gallery) : |
54 # apply extra options |
|
55 for k, v in kwargs.iteritems() : |
|
56 setattr(options, k, v) |
|
57 |
|
58 return Environment(self, config, gallery, options, args) |
|
59 |
|
60 def option_group (self, parser) : |
|
61 """ |
|
62 Returns an optparse.OptionGroup for this command's options. |
|
63 """ |
|
64 |
|
65 group = optparse.OptionGroup(parser, "Command-specific options") |
|
66 |
|
67 for opt in self.options : |
|
68 group.add_option(opt) |
|
69 |
|
70 return group |
|
71 |
|
72 def cmdopt_callback (self, option, opt_str, value, parser) : |
|
73 """ |
|
74 Used as a optparse.Option callback-action, adds this command's options to the parser, and stores the |
|
75 selected command. |
|
76 """ |
|
77 |
|
78 # check |
|
79 if hasattr(parser.values, option.dest) : |
|
80 raise ArgumentError("More than one command option given: %s + %s" % (getattr(parser.values, option.dest), self.name)) |
|
81 |
|
82 if self.options : |
|
83 # setup command-specific options |
|
84 parser.add_option_group(self.build_options(parser)) |
|
85 |
|
86 # store selected command |
|
87 setattr(parser.values, option.dest, self) |
|
88 |
|
89 class CommandList (object) : |
|
90 """ |
|
91 A set of available Commands |
|
92 """ |
|
93 |
|
94 def __init__ (self, commands) : |
|
95 """ |
|
96 Store with given initial commands |
|
97 """ |
|
98 |
|
99 self.list = commands |
|
100 self.dict = dict((cmd.name, cmd) for cmd in commands) |
|
101 |
|
102 def lookup (self, name) : |
|
103 """ |
|
104 Lookup a command by name |
|
105 """ |
|
106 |
|
107 return self.dict[name] |
|
108 |
|
109 def option_group (self, parser, default) : |
|
110 """ |
|
111 Returns an optparse.OptionGroup for these commands, using the given parser. |
|
112 """ |
|
113 |
|
114 group = optparse.OptionGroup(parser, "Command Options", "Select what command to execute, may introduce other options") |
|
115 |
|
116 for command in self.list : |
|
117 group.add_option('--%s' % optify(command.name), action='callback', callback=command.cmdopt_callback, dest='command', help=command.doc) |
|
118 |
|
119 # store default |
|
120 parser.set_defaults(command=default) |
|
121 |
|
122 return group |
|
123 |
|
124 class Environment (object) : |
|
125 """ |
|
126 The environment that a Command will execute in. |
|
127 |
|
128 This is bound to a Configuration, a Gallery, options values, argument values and other miscellaneous things. An |
|
129 environment also provides other services, such as status output, concurrent execution and error handling. |
|
130 """ |
|
131 |
|
132 def __init__ (self, command, config, gallery, options, args) : |
62 """ |
133 """ |
63 Create the execution environment |
134 Create the execution environment |
64 """ |
135 """ |
65 |
136 |
66 self.command = command |
137 self.command = command |
67 self.config = config |
138 self.config = config |
68 self.gallery = gallery |
139 self.gallery = gallery |
|
140 self.options = options |
|
141 self.args = args |
69 |
142 |
70 # conccurency |
143 # conccurency |
71 self.concurrent = concurrent.Manager(thread_count=config.thread_count) |
144 self.concurrent = concurrent.Manager(thread_count=config.thread_count) |
72 |
145 |
73 def execute (self, *args, **kwargs) : |
146 def execute (self) : |
74 """ |
147 """ |
75 Run the command in this context |
148 Run the command in this context |
76 """ |
149 """ |
77 |
150 |
78 return self.command.func(self, *args, **kwargs) |
151 return self.command.func(self, *self.args) |
79 |
152 |
80 def run (self, *args, **kwargs) : |
153 def run (self) : |
81 """ |
154 """ |
82 Run the command with error handling |
155 Run the command with error handling |
83 """ |
156 """ |
84 |
157 |
85 try : |
158 try : |
86 # run it |
159 # run it |
87 return self.execute(*args, **kwargs) |
160 return self.execute() |
88 |
161 |
89 except KeyboardInterrupt : |
162 except KeyboardInterrupt : |
90 self.log_error("Interrupted") |
163 self.log_error("Interrupted") |
91 |
164 |
92 except : |
165 except : |
93 # dump traceback |
166 # dump traceback |
94 # XXX: skip all crap up to the actual function |
167 # XXX: skip all crap up to the actual function |
95 self.handle_error() |
168 self.handle_error() |
96 |
169 |
|
170 # XXX: split off to a .log object |
97 def log_msg (self, level, msg, *args, **kwargs) : |
171 def log_msg (self, level, msg, *args, **kwargs) : |
98 """ |
172 """ |
99 Output a log message with the given level |
173 Output a log message with the given level |
100 |
174 |
101 XXX: unicode |
175 XXX: unicode |