Merge lp:~frankban/lpsetup/steps into lp:lpsetup

Proposed by Francesco Banconi on 2012-03-22
Status: Merged
Approved by: Graham Binns on 2012-03-23
Approved revision: 11
Merged at revision: 7
Proposed branch: lp:~frankban/lpsetup/steps
Merge into: lp:lpsetup
Diff against target: 467 lines (+137/-138)
7 files modified
lpsetup/__init__.py (+1/-1)
lpsetup/argparser.py (+70/-71)
lpsetup/subcommands/install.py (+3/-3)
lpsetup/subcommands/lxcinstall.py (+1/-1)
lpsetup/subcommands/update.py (+2/-2)
lpsetup/tests/examples.py (+27/-27)
lpsetup/tests/test_argparser.py (+33/-33)
To merge this branch: bzr merge lp:~frankban/lpsetup/steps
Reviewer Review Type Date Requested Status
Graham Binns (community) code 2012-03-22 Approve on 2012-03-23
Review via email: mp+98815@code.launchpad.net

Description of the Change

== Changes ==

lpsetup sub commands are able to execute only a subset of their internal functions. Now these functions composing a sub command are called "steps". Gary suggested that the previous name ("actions") could generate confusion since Python argparse has his own concept of actions.

== Tests ==

$ bin/test
Running zope.testrunner.layer.UnitTests tests:
  Set up zope.testrunner.layer.UnitTests in 0.000 seconds.
  Ran 36 tests with 0 failures and 0 errors in 0.024 seconds.
Tearing down left over layers:
  Tear down zope.testrunner.layer.UnitTests in 0.000 seconds.

To post a comment you must log in.
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lpsetup/__init__.py'
2--- lpsetup/__init__.py 2012-03-16 11:03:51 +0000
3+++ lpsetup/__init__.py 2012-03-22 11:09:20 +0000
4@@ -9,7 +9,7 @@
5 'get_version',
6 ]
7
8-VERSION = (0, 1, 1)
9+VERSION = (0, 1, 2)
10
11
12 def get_version():
13
14=== modified file 'lpsetup/argparser.py'
15--- lpsetup/argparser.py 2012-03-12 14:13:55 +0000
16+++ lpsetup/argparser.py 2012-03-22 11:09:20 +0000
17@@ -6,7 +6,7 @@
18
19 __metaclass__ = type
20 __all__ = [
21- 'ActionsBasedSubCommand',
22+ 'StepsBasedSubCommand',
23 'ArgumentParser',
24 'BaseSubCommand',
25 ]
26@@ -311,25 +311,25 @@
27 pass
28
29
30-class ActionsBasedSubCommand(BaseSubCommand):
31- """A sub command that uses "actions" to handle its execution.
32+class StepsBasedSubCommand(BaseSubCommand):
33+ """A sub command that uses "steps" to handle its execution.
34
35- Actions are callables stored in the `actions` attribute, together
36+ Steps are callables stored in the `steps` attribute, together
37 with the arguments they expect. Those arguments are strings
38 representing attributes of the argparse namespace::
39
40 >>> trace = []
41
42- >>> def action1(foo):
43- ... trace.append('action1 received ' + foo)
44-
45- >>> def action2(foo, bar):
46- ... trace.append('action2 received {0} and {1}'.format(foo, bar))
47-
48- >>> class SubCommand(ActionsBasedSubCommand):
49- ... actions = (
50- ... (action1, 'foo'),
51- ... (action2, 'foo', 'bar'),
52+ >>> def step1(foo):
53+ ... trace.append('step1 received ' + foo)
54+
55+ >>> def step2(foo, bar):
56+ ... trace.append('step2 received {0} and {1}'.format(foo, bar))
57+
58+ >>> class SubCommand(StepsBasedSubCommand):
59+ ... steps = (
60+ ... (step1, 'foo'),
61+ ... (step2, 'foo', 'bar'),
62 ... )
63 ...
64 ... def add_arguments(self, parser):
65@@ -337,7 +337,7 @@
66 ... parser.add_argument('--foo')
67 ... parser.add_argument('--bar')
68
69- This class implements an handler method that executes actions in the
70+ This class implements an handler method that executes steps in the
71 order they are provided::
72
73 >>> parser = ArgumentParser()
74@@ -345,43 +345,42 @@
75 >>> namespace = parser.parse_args('sub --foo eggs --bar spam'.split())
76 >>> namespace.main(namespace)
77 >>> trace
78- ['action1 received eggs', 'action2 received eggs and spam']
79+ ['step1 received eggs', 'step2 received eggs and spam']
80
81- A special argument `-a` or `--actions` is automatically added to the
82- parser. It can be used to execute only one or a subset of actions::
83+ A special argument `-s` or `--steps` is automatically added to the
84+ parser. It can be used to execute only one or a subset of steps::
85
86 >>> trace = []
87
88- >>> namespace = parser.parse_args('sub --foo eggs -a action1'.split())
89+ >>> namespace = parser.parse_args('sub --foo eggs -s step1'.split())
90 >>> namespace.main(namespace)
91 >>> trace
92- ['action1 received eggs']
93+ ['step1 received eggs']
94
95- A special argument `--skip-actions` is automatically added to the
96- parser. It can be used to skip one or more actions::
97+ A special argument `--skip-steps` is automatically added to the
98+ parser. It can be used to skip one or more steps::
99
100 >>> trace = []
101
102 >>> namespace = parser.parse_args(
103- ... 'sub --foo eggs --skip-actions action1'.split())
104+ ... 'sub --foo eggs --skip-steps step1'.split())
105 >>> namespace.main(namespace)
106 >>> trace
107- ['action2 received eggs and None']
108+ ['step2 received eggs and None']
109
110- The actions execution is stopped if an action raises
111- `subprocess.CalledProcessError`.
112+ The steps execution is stopped if a step raises `CalledProcessError`.
113 In that case, the error is returned by the handler.
114
115 >>> trace = []
116
117- >>> def erroneous_action(foo):
118+ >>> def erroneous_step(foo):
119 ... raise subprocess.CalledProcessError(1, 'command')
120
121 >>> class SubCommandWithErrors(SubCommand):
122- ... actions = (
123- ... (action1, 'foo'),
124- ... (erroneous_action, 'foo'),
125- ... (action2, 'foo', 'bar'),
126+ ... steps = (
127+ ... (step1, 'foo'),
128+ ... (erroneous_step, 'foo'),
129+ ... (step2, 'foo', 'bar'),
130 ... )
131
132 >>> parser = ArgumentParser()
133@@ -391,66 +390,66 @@
134 >>> str(error)
135 "Command 'command' returned non-zero exit status 1"
136
137- The action `action2` is not executed::
138+ The step `step2` is not executed::
139
140 >>> trace
141- ['action1 received eggs']
142+ ['step1 received eggs']
143 """
144
145- actions = ()
146+ steps = ()
147
148 def __init__(self, *args, **kwargs):
149- super(ActionsBasedSubCommand, self).__init__(*args, **kwargs)
150- self._action_names = []
151- self._actions = {}
152- for action_args in self.actions:
153- action, args = action_args[0], action_args[1:]
154- action_name = self._get_action_name(action)
155- self._action_names.append(action_name)
156- self._actions[action_name] = (action, args)
157-
158- def _get_action_name(self, action):
159- """Return the string representation of an action callable.
160-
161- The name is retrieved using attributes lookup for `action_name`
162+ super(StepsBasedSubCommand, self).__init__(*args, **kwargs)
163+ self._step_names = []
164+ self._steps = {}
165+ for step_args in self.steps:
166+ step, args = step_args[0], step_args[1:]
167+ step_name = self._get_step_name(step)
168+ self._step_names.append(step_name)
169+ self._steps[step_name] = (step, args)
170+
171+ def _get_step_name(self, step):
172+ """Return the string representation of a step callable.
173+
174+ The name is retrieved using attributes lookup for `step_name`
175 and then `__name__`::
176
177- >>> def action1():
178- ... pass
179- >>> action1.action_name = 'myaction'
180-
181- >>> def action2():
182- ... pass
183-
184- >>> sub_command = ActionsBasedSubCommand('foo')
185- >>> sub_command._get_action_name(action1)
186- 'myaction'
187- >>> sub_command._get_action_name(action2)
188- 'action2'
189+ >>> def step1():
190+ ... pass
191+ >>> step1.step_name = 'mystep'
192+
193+ >>> def step2():
194+ ... pass
195+
196+ >>> sub_command = StepsBasedSubCommand('foo')
197+ >>> sub_command._get_step_name(step1)
198+ 'mystep'
199+ >>> sub_command._get_step_name(step2)
200+ 'step2'
201 """
202 try:
203- return action.action_name
204+ return step.step_name
205 except AttributeError:
206- return action.__name__
207+ return step.__name__
208
209 def add_arguments(self, parser):
210- super(ActionsBasedSubCommand, self).add_arguments(parser)
211+ super(StepsBasedSubCommand, self).add_arguments(parser)
212 parser.add_argument(
213- '-a', '--actions', nargs='+', choices=self._action_names,
214+ '-s', '--steps', nargs='+', choices=self._step_names,
215 help='Call one or more internal functions.')
216 parser.add_argument(
217- '--skip-actions', nargs='+', choices=self._action_names,
218+ '--skip-steps', nargs='+', choices=self._step_names,
219 help='Skip one or more internal functions.')
220
221 def handle(self, namespace):
222- skip_actions = namespace.skip_actions or []
223- action_names = filter(
224- lambda action_name: action_name not in skip_actions,
225- namespace.actions or self._action_names)
226- for action_name in action_names:
227- action, arg_names = self._actions[action_name]
228+ skip_steps = namespace.skip_steps or []
229+ step_names = filter(
230+ lambda step_name: step_name not in skip_steps,
231+ namespace.steps or self._step_names)
232+ for step_name in step_names:
233+ step, arg_names = self._steps[step_name]
234 args = [getattr(namespace, i) for i in arg_names]
235 try:
236- action(*args)
237+ step(*args)
238 except subprocess.CalledProcessError as err:
239 return err
240
241=== modified file 'lpsetup/subcommands/install.py'
242--- lpsetup/subcommands/install.py 2012-03-16 17:40:22 +0000
243+++ lpsetup/subcommands/install.py 2012-03-22 11:09:20 +0000
244@@ -211,10 +211,10 @@
245 file_append(HOSTS_FILE, line)
246
247
248-class SubCommand(argparser.ActionsBasedSubCommand):
249+class SubCommand(argparser.StepsBasedSubCommand):
250 """Install the Launchpad environment."""
251
252- actions = (
253+ steps = (
254 (initialize,
255 'user', 'full_name', 'email', 'lpuser',
256 'private_key', 'public_key', 'valid_ssh_keys', 'ssh_key_path',
257@@ -283,7 +283,7 @@
258 'given user (see -u argument). '
259 '[DEFAULT={0}]'.format(CHECKOUT_DIR))
260 parser.add_argument(
261- '-s', '--ssh-key-name', default=SSH_KEY_NAME,
262+ '-S', '--ssh-key-name', default=SSH_KEY_NAME,
263 help='{0} [DEFAULT={1}]'.format(
264 self.ssh_key_name_help, SSH_KEY_NAME))
265 parser.add_argument(
266
267=== modified file 'lpsetup/subcommands/lxcinstall.py'
268--- lpsetup/subcommands/lxcinstall.py 2012-03-16 17:40:22 +0000
269+++ lpsetup/subcommands/lxcinstall.py 2012-03-22 11:09:20 +0000
270@@ -150,7 +150,7 @@
271 class SubCommand(install.SubCommand):
272 """Install the Launchpad environment inside an LXC."""
273
274- actions = (
275+ steps = (
276 (install.initialize,
277 'user', 'full_name', 'email', 'lpuser', 'private_key',
278 'public_key', 'valid_ssh_keys', 'dependencies_dir', 'directory'),
279
280=== modified file 'lpsetup/subcommands/update.py'
281--- lpsetup/subcommands/update.py 2012-03-16 17:40:22 +0000
282+++ lpsetup/subcommands/update.py 2012-03-22 11:09:20 +0000
283@@ -56,10 +56,10 @@
284 call(cmd, '--parent', dependencies_dir, '--target', branch)
285
286
287-class SubCommand(argparser.ActionsBasedSubCommand):
288+class SubCommand(argparser.StepsBasedSubCommand):
289 """Update the Launchpad environment to latest version."""
290
291- actions = (
292+ steps = (
293 (update_launchpad,
294 'user', 'valid_ssh_keys', 'dependencies_dir', 'directory', 'apt'),
295 (link_sourcecode_in_branches,
296
297=== modified file 'lpsetup/tests/examples.py'
298--- lpsetup/tests/examples.py 2012-03-15 17:13:37 +0000
299+++ lpsetup/tests/examples.py 2012-03-22 11:09:20 +0000
300@@ -6,11 +6,11 @@
301
302 __metaclass__ = type
303 __all__ = [
304- 'action1',
305- 'action2',
306- 'ActionsBasedSubCommand',
307- 'ActionsBasedSubCommandWithErrors',
308- 'bad_action',
309+ 'step1',
310+ 'step2',
311+ 'StepsBasedSubCommand',
312+ 'StepsBasedSubCommandWithErrors',
313+ 'bad_step',
314 'SubCommand',
315 ]
316
317@@ -19,16 +19,16 @@
318 from lpsetup import argparser
319
320
321-def action1(foo):
322- print 'action1 received ' + foo
323-
324-
325-def action2(foo, bar):
326- print 'action2 received {0} and {1}'.format(foo, bar)
327-action2.action_name = 'myaction'
328-
329-
330-def bad_action(foo):
331+def step1(foo):
332+ print 'step1 received ' + foo
333+
334+
335+def step2(foo, bar):
336+ print 'step2 received {0} and {1}'.format(foo, bar)
337+step2.step_name = 'mystep'
338+
339+
340+def bad_step(foo):
341 raise subprocess.CalledProcessError(1, 'command')
342
343
344@@ -45,25 +45,25 @@
345 return namespace
346
347
348-class ActionsBasedSubCommand(argparser.ActionsBasedSubCommand):
349- """An example action based sub command."""
350+class StepsBasedSubCommand(argparser.StepsBasedSubCommand):
351+ """An example steps based sub command."""
352
353- actions = (
354- (action1, 'foo'),
355- (action2, 'foo', 'bar'),
356+ steps = (
357+ (step1, 'foo'),
358+ (step2, 'foo', 'bar'),
359 )
360
361 def add_arguments(self, parser):
362- super(ActionsBasedSubCommand, self).add_arguments(parser)
363+ super(StepsBasedSubCommand, self).add_arguments(parser)
364 parser.add_argument('--foo')
365 parser.add_argument('--bar')
366
367
368-class ActionsBasedSubCommandWithErrors(ActionsBasedSubCommand):
369- """An example action based sub command (containing a failing action)."""
370+class StepsBasedSubCommandWithErrors(StepsBasedSubCommand):
371+ """An example steps based sub command (containing a failing step)."""
372
373- actions = (
374- (action1, 'foo'),
375- (bad_action, 'foo'),
376- (action2, 'foo', 'bar'),
377+ steps = (
378+ (step1, 'foo'),
379+ (bad_step, 'foo'),
380+ (step2, 'foo', 'bar'),
381 )
382
383=== modified file 'lpsetup/tests/test_argparser.py'
384--- lpsetup/tests/test_argparser.py 2012-03-15 17:13:37 +0000
385+++ lpsetup/tests/test_argparser.py 2012-03-22 11:09:20 +0000
386@@ -143,48 +143,48 @@
387 self.assertIn(self.sub_command.help, help)
388
389
390-class ActionsBasedSubCommandTest(SubCommandTestMixin, unittest.TestCase):
391-
392- sub_command_class = examples.ActionsBasedSubCommand
393-
394- def test_actions(self):
395- # Ensure actions are executed in the order they are provided.
396+class StepsBasedSubCommandTest(SubCommandTestMixin, unittest.TestCase):
397+
398+ sub_command_class = examples.StepsBasedSubCommand
399+
400+ def test_steps(self):
401+ # Ensure steps are executed in the order they are provided.
402 with capture_output() as output:
403 self.parse_and_call_main('--foo eggs --bar spam')
404 self.check_output(
405- ['action1 received eggs', 'action2 received eggs and spam'],
406+ ['step1 received eggs', 'step2 received eggs and spam'],
407 output)
408
409- def test_action_flag(self):
410- # A special argument `-a` or `--actions` is automatically added to the
411- # parser. It can be used to execute only one or a subset of actions.
412- with capture_output() as output:
413- self.parse_and_call_main('--foo eggs -a action1')
414- self.check_output(['action1 received eggs'], output)
415-
416- def test_skip_action(self):
417- # A special argument `--skip-actions` is automatically added to the
418- # parser. It can be used to skip one or more actions.
419- with capture_output() as output:
420- self.parse_and_call_main('--foo eggs --skip-actions action1')
421- self.check_output(['action2 received eggs and None'], output)
422-
423- def test_action_name(self):
424- # Ensure the string representation of an action i correctly retrieved.
425- method = self.sub_command._get_action_name
426- self.assertEqual('action1', method(examples.action1))
427- self.assertEqual('myaction', method(examples.action2))
428-
429-
430-class ActionsBasedSubCommandWithErrorsTest(
431+ def test_step_flag(self):
432+ # A special argument `-s` or `--steps` is automatically added to the
433+ # parser. It can be used to execute only one or a subset of steps.
434+ with capture_output() as output:
435+ self.parse_and_call_main('--foo eggs -s step1')
436+ self.check_output(['step1 received eggs'], output)
437+
438+ def test_skip_steps(self):
439+ # A special argument `--skip-steps` is automatically added to the
440+ # parser. It can be used to skip one or more steps.
441+ with capture_output() as output:
442+ self.parse_and_call_main('--foo eggs --skip-steps step1')
443+ self.check_output(['step2 received eggs and None'], output)
444+
445+ def test_step_name(self):
446+ # Ensure the string representation of a step is correctly retrieved.
447+ method = self.sub_command._get_step_name
448+ self.assertEqual('step1', method(examples.step1))
449+ self.assertEqual('mystep', method(examples.step2))
450+
451+
452+class StepsBasedSubCommandWithErrorsTest(
453 SubCommandTestMixin, unittest.TestCase):
454
455- sub_command_class = examples.ActionsBasedSubCommandWithErrors
456+ sub_command_class = examples.StepsBasedSubCommandWithErrors
457
458- def test_failing_action(self):
459- # Ensure the actions execution is stopped if an action raises
460+ def test_failing_step(self):
461+ # Ensure the steps execution is stopped if a step raises
462 # `subprocess.CalledProcessError`.
463 with capture_output() as output:
464 error = self.parse_and_call_main('--foo eggs')
465 self.assertEqual(1, error.returncode)
466- self.check_output(['action1 received eggs'], output)
467+ self.check_output(['step1 received eggs'], output)

Subscribers

People subscribed via source and target branches

to all changes: