Merge lp:~jml/quickly/commands-cleanup into lp:quickly

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
Reviewer Review Type Date Requested Status
Didier Roche-Tolomelli Approve
Review via email: mp+15018@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

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.

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
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_by_criteria to get_commands_by_criteria to bin/quickly as it's used there to avoid breaking stuff there.
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

Subscribers

People subscribed via source and target branches