Merge lp:~jml/quickly/commands-cleanup into lp:quickly
- commands-cleanup
- Merge into trunk
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~jml/quickly/commands-cleanup |
Merge into: | lp:quickly |
Diff against target: |
493 lines (+185/-106) 2 files modified
quickly/builtincommands.py (+3/-3) quickly/commands.py (+182/-103) |
To merge this branch: | bzr merge lp:~jml/quickly/commands-cleanup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Didier Roche-Tolomelli | Approve | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jonathan Lange (jml) wrote : | # |
lp:~jml/quickly/commands-cleanup
updated
- 363. By Jonathan Lange
-
Remove lots of XXXs, mostly by adding more verbose comments.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Didier Roche-Tolomelli (didrocks) wrote : | # |
Thanks for your patch.
Again, do not hesitate to share your 7 years of python experience with a stupid C/C++/shell programmer but beginner in python :)
I added a replacement for get_command_
Merged now, thanks.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'quickly/builtincommands.py' |
2 | --- quickly/builtincommands.py 2009-11-15 15:18:26 +0000 |
3 | +++ quickly/builtincommands.py 2009-11-19 00:25:20 +0000 |
4 | @@ -146,17 +146,17 @@ |
5 | if template is None: |
6 | template = "builtins" |
7 | try: |
8 | - command = commands_module.get_command_by_criteria(name=command_name, template=template)[0] |
9 | + command = commands_module.get_commands_by_criteria(name=command_name, template=template)[0] |
10 | except IndexError: |
11 | # check if a builtin commands corresponds |
12 | template = "builtins" |
13 | try: |
14 | - command = commands_module.get_command_by_criteria(name=command_name, template=template)[0] |
15 | + command = commands_module.get_commands_by_criteria(name=command_name, template=template)[0] |
16 | except IndexError: |
17 | # there is really not such command |
18 | if template == "builtins": |
19 | # to help the user, we can search if this command_name corresponds to a command in a template |
20 | - list_possible_commands = commands_module.get_command_by_criteria(name=command_name, followed_by_template=True) |
21 | + list_possible_commands = commands_module.get_commands_by_criteria(name=command_name, followed_by_template=True) |
22 | if list_possible_commands: |
23 | print _("ERROR: help command must be followed by a template name for getting help from templates commands like %s." % command_name) |
24 | print _("Candidates template are: %s") % ", ".join([command.template for command in list_possible_commands]) |
25 | |
26 | === modified file 'quickly/commands.py' |
27 | --- quickly/commands.py 2009-11-15 23:06:02 +0000 |
28 | +++ quickly/commands.py 2009-11-19 00:25:20 +0000 |
29 | @@ -4,16 +4,16 @@ |
30 | # |
31 | # This file is part of Quickly |
32 | # |
33 | -#This program is free software: you can redistribute it and/or modify it |
34 | -#under the terms of the GNU General Public License version 3, as published |
35 | +#This program is free software: you can redistribute it and/or modify it |
36 | +#under the terms of the GNU General Public License version 3, as published |
37 | #by the Free Software Foundation. |
38 | |
39 | -#This program is distributed in the hope that it will be useful, but |
40 | -#WITHOUT ANY WARRANTY; without even the implied warranties of |
41 | -#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
42 | +#This program is distributed in the hope that it will be useful, but |
43 | +#WITHOUT ANY WARRANTY; without even the implied warranties of |
44 | +#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
45 | #PURPOSE. See the GNU General Public License for more details. |
46 | |
47 | -#You should have received a copy of the GNU General Public License along |
48 | +#You should have received a copy of the GNU General Public License along |
49 | #with this program. If not, see <http://www.gnu.org/licenses/>. |
50 | |
51 | import os |
52 | @@ -29,16 +29,19 @@ |
53 | |
54 | gettext.textdomain('quickly') |
55 | |
56 | -# double depths tabular : template (or "builtin"), name |
57 | +# A dictionary with keys either 'builtins' or the names of templates. Values |
58 | +# are another dictionary mapping command names to their command objects. This |
59 | +# is used as a cache of all commands that we've found in templates. |
60 | __commands = {} |
61 | |
62 | |
63 | def get_all_commands(): |
64 | """Load all commands |
65 | - |
66 | - First, load template command and then builtins one. Push right parameters depending |
67 | - if hooks are available, or if the command execution is special |
68 | - You can note that create command is automatically overloaded atm""" |
69 | + |
70 | + First, load template command and then builtins one. Push right parameters |
71 | + depending if hooks are available, or if the command execution is special |
72 | + You can note that create command is automatically overloaded atm. |
73 | + """ |
74 | |
75 | if len(__commands) > 0: |
76 | return __commands |
77 | @@ -57,21 +60,32 @@ |
78 | launch_outside_project_command_list = [] |
79 | command_followed_by_command_list = [] |
80 | try: |
81 | - files_command_parameters = file(os.path.join(template_path, "commandsconfig"), 'rb') |
82 | - for line in files_command_parameters: |
83 | - fields = line.split('#')[0] # Suppress commentary after the value in configuration file and in full line |
84 | + files_command_parameters = file( |
85 | + os.path.join(template_path, "commandsconfig"), 'rb') |
86 | + for line in files_command_parameters: |
87 | + # Suppress commentary after the value in configuration |
88 | + # file and in full line. |
89 | + fields = line.split('#')[0] |
90 | fields = fields.split('=') # Separate variable from value |
91 | # normally, we have two fields in "fields" |
92 | if len(fields) == 2: |
93 | targeted_property = fields[0].strip() |
94 | - command_list = [command.strip() for command in fields[1].split(';')] |
95 | - if targeted_property == 'COMMANDS_LAUNCHED_IN_OR_OUTSIDE_PROJECT': |
96 | - launch_inside_project_command_list.extend(command_list) |
97 | - launch_outside_project_command_list.extend(command_list) |
98 | - if targeted_property == 'COMMANDS_LAUNCHED_OUTSIDE_PROJECT_ONLY': |
99 | - launch_outside_project_command_list.extend(command_list) |
100 | + command_list = [ |
101 | + command.strip() |
102 | + for command in fields[1].split(';')] |
103 | + if (targeted_property |
104 | + == 'COMMANDS_LAUNCHED_IN_OR_OUTSIDE_PROJECT'): |
105 | + launch_inside_project_command_list.extend( |
106 | + command_list) |
107 | + launch_outside_project_command_list.extend( |
108 | + command_list) |
109 | + if (targeted_property |
110 | + == 'COMMANDS_LAUNCHED_OUTSIDE_PROJECT_ONLY'): |
111 | + launch_outside_project_command_list.extend( |
112 | + command_list) |
113 | if targeted_property == 'COMMANDS_FOLLOWED_BY_COMMAND': |
114 | - command_followed_by_command_list.extend(command_list) |
115 | + command_followed_by_command_list.extend( |
116 | + command_list) |
117 | except (OSError, IOError): |
118 | pass |
119 | |
120 | @@ -81,13 +95,20 @@ |
121 | if "." in command_name: |
122 | command_name = ".".join(command_name.split('.')[0:-1]) |
123 | |
124 | - if os.path.isfile(file_path) and os.access(file_path, os.X_OK): # add the command to the list if is executable |
125 | - hooks = {'pre': None, 'post':None} |
126 | + # add the command to the list if is executable |
127 | + # XXX: It's generally a bad idea to check if you can read to a |
128 | + # file. Instead, you should just read it. The file might |
129 | + # become unreadable between here and when you actually read |
130 | + # it. -- jml, 2009-11-18 |
131 | + if os.path.isfile(file_path) and os.access(file_path, os.X_OK): |
132 | + hooks = {'pre': None, 'post': None} |
133 | for event in ('pre', 'post'): |
134 | - if hasattr(builtincommands, event + '_' + command_name): |
135 | - hooks[event] = getattr(builtincommands, event + '_' + command_name) |
136 | - |
137 | - # define special options for command |
138 | + event_hook = getattr( |
139 | + builtincommands, event + '_' + command_name, None) |
140 | + if event_hook is not None: |
141 | + hooks[event] = event_hook |
142 | + |
143 | + # define special options for command |
144 | launch_inside_project = False |
145 | launch_outside_project = False |
146 | followed_by_template = False |
147 | @@ -99,18 +120,25 @@ |
148 | followed_by_template = True |
149 | if command_name in builtincommands.followed_by_command: |
150 | followed_by_command = True |
151 | - # default for commands: if not inside nor outside, and it's a template command, make it launch inside a project only |
152 | - if not launch_inside_project and not launch_outside_project: |
153 | + # default for commands: if not inside nor outside, and |
154 | + # it's a template command, make it launch inside a project |
155 | + # only |
156 | + if not (launch_inside_project or launch_outside_project): |
157 | launch_inside_project = True |
158 | - |
159 | - __commands[template][command_name] = Command(command_name, file_path, template, launch_inside_project, launch_outside_project, followed_by_template, followed_by_command, hooks['pre'], hooks['post']) |
160 | - |
161 | - |
162 | + |
163 | + __commands[template][command_name] = Command( |
164 | + command_name, file_path, template, |
165 | + launch_inside_project, launch_outside_project, |
166 | + followed_by_template, followed_by_command, |
167 | + hooks['pre'], hooks['post']) |
168 | + |
169 | + |
170 | # add builtin commands (avoiding gettext and hooks) |
171 | __commands['builtins'] = {} |
172 | for elem in dir(builtincommands): |
173 | command = getattr(builtincommands, elem) |
174 | - if callable(command) and not command.__name__.startswith(('pre_', 'post_', 'gettext')): |
175 | + if (callable(command) |
176 | + and not command.__name__.startswith(('pre_', 'post_', 'gettext'))): |
177 | command_name = command.__name__ |
178 | # here, special case for some commands |
179 | launch_inside_project = False |
180 | @@ -127,51 +155,73 @@ |
181 | if command_name in builtincommands.followed_by_command: |
182 | followed_by_command = True |
183 | |
184 | - # default for commands: if not inside nor outside only, and it's a builtin command, make it launch wherever |
185 | + # default for commands: if not inside nor outside only, and it's a |
186 | + # builtin command, make it launch wherever |
187 | if not launch_inside_project and not launch_outside_project: |
188 | launch_inside_project = True |
189 | launch_outside_project = True |
190 | |
191 | - hooks = {'pre': None, 'post':None} |
192 | + hooks = {'pre': None, 'post': None} |
193 | for event in ('pre', 'post'): |
194 | if hasattr(builtincommands, event + '_' + command_name): |
195 | - hooks[event] = getattr(builtincommands, event + '_' + command_name) |
196 | - |
197 | - __commands['builtins'][command_name] = Command(command_name, command, None, launch_inside_project, launch_outside_project, followed_by_template, followed_by_command, hooks['pre'], hooks['post']) |
198 | - |
199 | + hooks[event] = getattr( |
200 | + builtincommands, event + '_' + command_name) |
201 | + |
202 | + __commands['builtins'][command_name] = Command( |
203 | + command_name, command, None, launch_inside_project, |
204 | + launch_outside_project, followed_by_template, |
205 | + followed_by_command, hooks['pre'], hooks['post']) |
206 | + |
207 | return __commands |
208 | - |
209 | - |
210 | -def get_command_by_criteria(**criterias): |
211 | + |
212 | + |
213 | +def get_commands_by_criteria(**criterias): |
214 | """Get a list of all commands corresponding to criterias |
215 | - |
216 | - Criterias correponds to Command object properties""" |
217 | - |
218 | - # all criterias are None by default, which means, don't care about the value |
219 | + |
220 | + Criterias correponds to Command object properties. |
221 | + """ |
222 | + |
223 | + # all criterias are None by default, which means, don't care about the |
224 | + # value. |
225 | matched_commands = [] |
226 | all_commands = get_all_commands() |
227 | |
228 | for template_available in all_commands: |
229 | - if criterias.has_key('template') and criterias['template'] != template_available: |
230 | + if ('template' in criterias |
231 | + and criterias['template'] != template_available): |
232 | + # XXX: I'm sure this speeds up the search, but what exactly is it |
233 | + # that we're skipping and why is it ok to skip this? -- jml, |
234 | + # 2009-11-18. |
235 | continue # to speed up the search |
236 | for candidate_command_name in all_commands[template_available]: |
237 | - candidate_command = all_commands[template_available][candidate_command_name] |
238 | + candidate_command = all_commands[ |
239 | + template_available][candidate_command_name] |
240 | command_ok = True |
241 | # check all criterias (template has already been checked) |
242 | for elem in criterias: |
243 | - if elem is not 'template' and getattr(candidate_command, elem) != criterias[elem]: |
244 | + if (elem is not 'template' |
245 | + and getattr(candidate_command, elem) != criterias[elem]): |
246 | command_ok = False |
247 | continue # no need to check other criterias |
248 | if command_ok: |
249 | - matched_commands.append(candidate_command) |
250 | - |
251 | + matched_commands.append(candidate_command) |
252 | + |
253 | return matched_commands |
254 | |
255 | |
256 | +def get_command_names_by_criteria(**criteria): |
257 | + """Get a list of all command names corresponding to criteria. |
258 | + |
259 | + 'criteria' correponds to Command object properties. |
260 | + """ |
261 | + return (command.name for command in get_commands_by_criteria(**criteria)) |
262 | + |
263 | + |
264 | def get_all_templates(): |
265 | """Get a list of all templates""" |
266 | - |
267 | - return [template for template in get_all_commands().keys() if template != "builtins"] |
268 | + return [ |
269 | + template for template in get_all_commands().keys() |
270 | + if template != "builtins"] |
271 | |
272 | |
273 | class Command: |
274 | @@ -181,7 +231,10 @@ |
275 | print _("Aborting") |
276 | sys.exit(return_code) |
277 | |
278 | - def __init__(self, command_name, command, template=None, inside_project=True, outside_project=False, followed_by_template=False, followed_by_command=False, prehook=None, posthook=None): |
279 | + def __init__(self, command_name, command, template=None, |
280 | + inside_project=True, outside_project=False, |
281 | + followed_by_template=False, followed_by_command=False, |
282 | + prehook=None, posthook=None): |
283 | self.command = command |
284 | self.template = template |
285 | self.prehook = prehook |
286 | @@ -194,10 +247,12 @@ |
287 | |
288 | def shell_completion(self, template_in_cli, args): |
289 | """Smart completion of a command |
290 | - |
291 | - This command try to see if the command is followed by a template and present template |
292 | - if it's the case. Otherwise, it calls the corresponding command argument""" |
293 | - |
294 | + |
295 | + This command try to see if the command is followed by a template and |
296 | + present template if it's the case. Otherwise, it calls the |
297 | + corresponding command argument. |
298 | + """ |
299 | + |
300 | completion = [] |
301 | |
302 | if len(args) == 1: |
303 | @@ -205,101 +260,127 @@ |
304 | if self.followed_by_template: # template completion |
305 | if not self.template: # builtins command case |
306 | completion.extend(get_all_templates()) |
307 | - else: # complete with current template (which != from template_in_cli: ex create command (multiple templates)) |
308 | + else: |
309 | + # complete with current template (which != from |
310 | + # template_in_cli: ex create command (multiple |
311 | + # templates)) |
312 | completion.extend([self.template]) |
313 | else: # there is a template, add template commands |
314 | if self.followed_by_command: # template command completion |
315 | - completion.extend([command.name for command in get_command_by_criteria(template=template_in_cli)]) |
316 | + completion.extend( |
317 | + get_command_names_by_criteria( |
318 | + template=template_in_cli)) |
319 | if self.followed_by_command: # builtin command completion |
320 | - completion.extend([command.name for command in get_command_by_criteria(template="builtins")]) |
321 | + completion.extend( |
322 | + get_command_names_by_criteria(template="builtins")) |
323 | |
324 | elif len(args) == 2: |
325 | if not template_in_cli and self.followed_by_template: |
326 | template_in_cli = args[0] |
327 | - if self.followed_by_command: # template command completion and builtins command |
328 | - completion.extend([command.name for command in get_command_by_criteria(template=template_in_cli)]) |
329 | - completion.extend([command.name for command in get_command_by_criteria(template="builtins")]) |
330 | + # template command completion and builtins command. |
331 | + if self.followed_by_command: |
332 | + completion.extend( |
333 | + get_command_names_by_criteria( |
334 | + template=template_in_cli)) |
335 | + completion.extend( |
336 | + get_command_names_by_criteria(template="builtins")) |
337 | |
338 | - # give to the command the opportunity of giving some shell-completion features |
339 | + # give to the command the opportunity of giving some shell-completion |
340 | + # features |
341 | if template_in_cli == self.template and len(completion) == 0: |
342 | if callable(self.command): # Internal function |
343 | - completion.extend(self.command(template_in_cli, "", args, True)) |
344 | + completion.extend( |
345 | + self.command(template_in_cli, "", args, True)) |
346 | else: # External command |
347 | - instance = subprocess.Popen([self.command, "shell-completion"] + args, stdout=subprocess.PIPE) |
348 | + instance = subprocess.Popen( |
349 | + [self.command, "shell-completion"] + args, |
350 | + stdout=subprocess.PIPE) |
351 | command_return_completion, err = instance.communicate() |
352 | if instance.returncode != 0: |
353 | print err |
354 | - sys.exit(1) |
355 | + sys.exit(1) |
356 | completion.extend(command_return_completion.split(',')) |
357 | |
358 | - return(" ".join(completion)) |
359 | + return " ".join(completion) |
360 | |
361 | - def help(self, dest_path,command_args): |
362 | + def help(self, dest_path, command_args): |
363 | """Print help of the current command""" |
364 | |
365 | - return_code = 0 |
366 | + return_code = 0 |
367 | if callable(self.command): # intern function, return __doc__ |
368 | print (self.command.__doc__) |
369 | else: # launch command with "help" parameter |
370 | - return_code = subprocess.call([self.command, "help"] + command_args, cwd=dest_path) |
371 | + return_code = subprocess.call( |
372 | + [self.command, "help"] + command_args, cwd=dest_path) |
373 | |
374 | - return(return_code) |
375 | + return return_code |
376 | |
377 | def is_right_context(self, dest_path, verbose=True): |
378 | - """Check if we are in the right context for launching the command""" |
379 | - |
380 | - # verbose à false pour l'introspection des commandes dispos |
381 | - |
382 | + """Check if we are in the right context for launching the command. |
383 | + |
384 | + If you are using this to introspect available commands, then set |
385 | + verbose to False. |
386 | + """ |
387 | # check if dest_path check outside or inside only project :) |
388 | if self.inside_project and not self.outside_project: |
389 | try: |
390 | project_path = tools.get_root_project_path(dest_path) |
391 | except tools.project_path_not_found: |
392 | if verbose: |
393 | - print _("ERROR: Can't find project in %s.\nEnsure you launch this command from a quickly project directory.") % dest_path |
394 | + print (_( |
395 | + "ERROR: Can't find project in %s.\nEnsure you launch " |
396 | + "this command from a quickly project directory.") |
397 | + % dest_path) |
398 | print _("Aborting") |
399 | return False |
400 | if self.outside_project and not self.inside_project: |
401 | try: |
402 | project_path = tools.get_root_project_path(dest_path) |
403 | if verbose: |
404 | - print _("ERROR: %s is a project. You can't launch %s command within a project. " \ |
405 | - "Please choose another path." % (project_path, self.command)) |
406 | + print _( |
407 | + "ERROR: %s is a project. You can't launch %s command " |
408 | + "within a project. Please choose another path." |
409 | + % (project_path, self.command)) |
410 | print _("Aborting") |
411 | return False |
412 | except tools.project_path_not_found: |
413 | pass |
414 | - |
415 | + |
416 | return True |
417 | |
418 | - |
419 | def launch(self, current_dir, command_args, template=None): |
420 | """Launch command and hooks for it |
421 | - |
422 | - This command will perform the right action (insider function or script execution) after having |
423 | - checked the context""" |
424 | - |
425 | - # template is current template (it will be useful for builtin commands) |
426 | - |
427 | - # if template not specified, take the one for the command |
428 | - # the template argument is useful when builtin commands which behavior take into account the template name |
429 | + |
430 | + This command will perform the right action (insider function or script |
431 | + execution) after having checked the context. |
432 | + """ |
433 | + |
434 | + # template is current template (it will be useful for builtin |
435 | + # commands) |
436 | + |
437 | + # if template not specified, take the one for the command the template |
438 | + # argument is useful when builtin commands which behavior take into |
439 | + # account the template name |
440 | if template is None: |
441 | - template = self.template # (which can be None if it's a builtin command launched outside a project) |
442 | + # (which can be None if it's a builtin command launched outside a |
443 | + # project) |
444 | + template = self.template |
445 | |
446 | if not self.is_right_context(current_dir): # check in verbose mode |
447 | - return(1) |
448 | + return 1 |
449 | |
450 | # get root project dir |
451 | try: |
452 | project_path = tools.get_root_project_path(current_dir) |
453 | - except tools.project_path_not_found: |
454 | + except tools.project_path_not_found: |
455 | # launch in current project |
456 | project_path = current_dir |
457 | |
458 | # transition if needed |
459 | if self.inside_project and self.name != "upgrade": |
460 | try: |
461 | - get_all_commands()[self.template]['upgrade'].launch(current_dir, [], template) |
462 | + get_all_commands()[self.template]['upgrade'].launch( |
463 | + current_dir, [], template) |
464 | except KeyError: # if KeyError, no upgrade command. |
465 | pass |
466 | |
467 | @@ -307,20 +388,18 @@ |
468 | return_code = self.prehook(template, project_path, command_args) |
469 | if return_code != 0: |
470 | self._die(self.prehook.__name__, return_code) |
471 | - |
472 | + |
473 | if callable(self.command): # Internal function |
474 | return_code = self.command(template, project_path, command_args) |
475 | else: # External command |
476 | - return_code = subprocess.call([self.command] + command_args, cwd=project_path) |
477 | + return_code = subprocess.call( |
478 | + [self.command] + command_args, cwd=project_path) |
479 | if return_code != 0: |
480 | - self._die(self.name,return_code) |
481 | + self._die(self.name, return_code) |
482 | |
483 | if self.posthook: |
484 | return_code = self.posthook(template, project_path, command_args) |
485 | if return_code != 0: |
486 | self._die(self.posthook.__name__, return_code) |
487 | - |
488 | - return(0) |
489 | - |
490 | - |
491 | - |
492 | + |
493 | + return 0 |
This script makes quickly/commands.py follow PEP 8. It also adds a wrapper around get_command_ by_criteria to return the command names, since that's the only way it's used.
I've also added some XXX comments for things I don't understand.