~frankban/lpsetup/urandom-hack

« back to all changes in this revision

Viewing changes to lpsetup/argparser.py

  • Committer: Francesco Banconi
  • Date: 2012-03-23 11:37:37 UTC
  • mfrom: (6.1.5 steps)
  • Revision ID: francesco.banconi@canonical.com-20120323113737-mog8bayyv31tgbf5
[r=gmb] s/actions/steps.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 
7
7
__metaclass__ = type
8
8
__all__ = [
9
 
    'ActionsBasedSubCommand',
 
9
    'StepsBasedSubCommand',
10
10
    'ArgumentParser',
11
11
    'BaseSubCommand',
12
12
    ]
311
311
        pass
312
312
 
313
313
 
314
 
class ActionsBasedSubCommand(BaseSubCommand):
315
 
    """A sub command that uses "actions" to handle its execution.
 
314
class StepsBasedSubCommand(BaseSubCommand):
 
315
    """A sub command that uses "steps" to handle its execution.
316
316
 
317
 
    Actions are callables stored in the `actions` attribute, together
 
317
    Steps are callables stored in the `steps` attribute, together
318
318
    with the arguments they expect. Those arguments are strings
319
319
    representing attributes of the argparse namespace::
320
320
 
321
321
        >>> trace = []
322
322
 
323
 
        >>> def action1(foo):
324
 
        ...     trace.append('action1 received ' + foo)
325
 
 
326
 
        >>> def action2(foo, bar):
327
 
        ...     trace.append('action2 received {0} and {1}'.format(foo, bar))
328
 
 
329
 
        >>> class SubCommand(ActionsBasedSubCommand):
330
 
        ...     actions = (
331
 
        ...         (action1, 'foo'),
332
 
        ...         (action2, 'foo', 'bar'),
 
323
        >>> def step1(foo):
 
324
        ...     trace.append('step1 received ' + foo)
 
325
 
 
326
        >>> def step2(foo, bar):
 
327
        ...     trace.append('step2 received {0} and {1}'.format(foo, bar))
 
328
 
 
329
        >>> class SubCommand(StepsBasedSubCommand):
 
330
        ...     steps = (
 
331
        ...         (step1, 'foo'),
 
332
        ...         (step2, 'foo', 'bar'),
333
333
        ...         )
334
334
        ...
335
335
        ...     def add_arguments(self, parser):
337
337
        ...         parser.add_argument('--foo')
338
338
        ...         parser.add_argument('--bar')
339
339
 
340
 
    This class implements an handler method that executes actions in the
 
340
    This class implements an handler method that executes steps in the
341
341
    order they are provided::
342
342
 
343
343
        >>> parser = ArgumentParser()
345
345
        >>> namespace = parser.parse_args('sub --foo eggs --bar spam'.split())
346
346
        >>> namespace.main(namespace)
347
347
        >>> trace
348
 
        ['action1 received eggs', 'action2 received eggs and spam']
 
348
        ['step1 received eggs', 'step2 received eggs and spam']
349
349
 
350
 
    A special argument `-a` or `--actions` is automatically added to the
351
 
    parser. It can be used to execute only one or a subset of actions::
 
350
    A special argument `-s` or `--steps` is automatically added to the
 
351
    parser. It can be used to execute only one or a subset of steps::
352
352
 
353
353
        >>> trace = []
354
354
 
355
 
        >>> namespace = parser.parse_args('sub --foo eggs -a action1'.split())
 
355
        >>> namespace = parser.parse_args('sub --foo eggs -s step1'.split())
356
356
        >>> namespace.main(namespace)
357
357
        >>> trace
358
 
        ['action1 received eggs']
 
358
        ['step1 received eggs']
359
359
 
360
 
    A special argument `--skip-actions` is automatically added to the
361
 
    parser. It can be used to skip one or more actions::
 
360
    A special argument `--skip-steps` is automatically added to the
 
361
    parser. It can be used to skip one or more steps::
362
362
 
363
363
        >>> trace = []
364
364
 
365
365
        >>> namespace = parser.parse_args(
366
 
        ...     'sub --foo eggs --skip-actions action1'.split())
 
366
        ...     'sub --foo eggs --skip-steps step1'.split())
367
367
        >>> namespace.main(namespace)
368
368
        >>> trace
369
 
        ['action2 received eggs and None']
 
369
        ['step2 received eggs and None']
370
370
 
371
 
    The actions execution is stopped if an action raises
372
 
    `subprocess.CalledProcessError`.
 
371
    The steps execution is stopped if a step raises `CalledProcessError`.
373
372
    In that case, the error is returned by the handler.
374
373
 
375
374
        >>> trace = []
376
375
 
377
 
        >>> def erroneous_action(foo):
 
376
        >>> def erroneous_step(foo):
378
377
        ...     raise subprocess.CalledProcessError(1, 'command')
379
378
 
380
379
        >>> class SubCommandWithErrors(SubCommand):
381
 
        ...     actions = (
382
 
        ...         (action1, 'foo'),
383
 
        ...         (erroneous_action, 'foo'),
384
 
        ...         (action2, 'foo', 'bar'),
 
380
        ...     steps = (
 
381
        ...         (step1, 'foo'),
 
382
        ...         (erroneous_step, 'foo'),
 
383
        ...         (step2, 'foo', 'bar'),
385
384
        ...         )
386
385
 
387
386
        >>> parser = ArgumentParser()
391
390
        >>> str(error)
392
391
        "Command 'command' returned non-zero exit status 1"
393
392
 
394
 
    The action `action2` is not executed::
 
393
    The step `step2` is not executed::
395
394
 
396
395
        >>> trace
397
 
        ['action1 received eggs']
 
396
        ['step1 received eggs']
398
397
    """
399
398
 
400
 
    actions = ()
 
399
    steps = ()
401
400
 
402
401
    def __init__(self, *args, **kwargs):
403
 
        super(ActionsBasedSubCommand, self).__init__(*args, **kwargs)
404
 
        self._action_names = []
405
 
        self._actions = {}
406
 
        for action_args in self.actions:
407
 
            action, args = action_args[0], action_args[1:]
408
 
            action_name = self._get_action_name(action)
409
 
            self._action_names.append(action_name)
410
 
            self._actions[action_name] = (action, args)
411
 
 
412
 
    def _get_action_name(self, action):
413
 
        """Return the string representation of an action callable.
414
 
 
415
 
        The name is retrieved using attributes lookup for `action_name`
 
402
        super(StepsBasedSubCommand, self).__init__(*args, **kwargs)
 
403
        self._step_names = []
 
404
        self._steps = {}
 
405
        for step_args in self.steps:
 
406
            step, args = step_args[0], step_args[1:]
 
407
            step_name = self._get_step_name(step)
 
408
            self._step_names.append(step_name)
 
409
            self._steps[step_name] = (step, args)
 
410
 
 
411
    def _get_step_name(self, step):
 
412
        """Return the string representation of a step callable.
 
413
 
 
414
        The name is retrieved using attributes lookup for `step_name`
416
415
        and then `__name__`::
417
416
 
418
 
            >>> def action1():
419
 
            ...     pass
420
 
            >>> action1.action_name = 'myaction'
421
 
 
422
 
            >>> def action2():
423
 
            ...     pass
424
 
 
425
 
            >>> sub_command = ActionsBasedSubCommand('foo')
426
 
            >>> sub_command._get_action_name(action1)
427
 
            'myaction'
428
 
            >>> sub_command._get_action_name(action2)
429
 
            'action2'
 
417
            >>> def step1():
 
418
            ...     pass
 
419
            >>> step1.step_name = 'mystep'
 
420
 
 
421
            >>> def step2():
 
422
            ...     pass
 
423
 
 
424
            >>> sub_command = StepsBasedSubCommand('foo')
 
425
            >>> sub_command._get_step_name(step1)
 
426
            'mystep'
 
427
            >>> sub_command._get_step_name(step2)
 
428
            'step2'
430
429
        """
431
430
        try:
432
 
            return action.action_name
 
431
            return step.step_name
433
432
        except AttributeError:
434
 
            return action.__name__
 
433
            return step.__name__
435
434
 
436
435
    def add_arguments(self, parser):
437
 
        super(ActionsBasedSubCommand, self).add_arguments(parser)
 
436
        super(StepsBasedSubCommand, self).add_arguments(parser)
438
437
        parser.add_argument(
439
 
            '-a', '--actions', nargs='+', choices=self._action_names,
 
438
            '-s', '--steps', nargs='+', choices=self._step_names,
440
439
            help='Call one or more internal functions.')
441
440
        parser.add_argument(
442
 
            '--skip-actions', nargs='+', choices=self._action_names,
 
441
            '--skip-steps', nargs='+', choices=self._step_names,
443
442
            help='Skip one or more internal functions.')
444
443
 
445
444
    def handle(self, namespace):
446
 
        skip_actions = namespace.skip_actions or []
447
 
        action_names = filter(
448
 
            lambda action_name: action_name not in skip_actions,
449
 
            namespace.actions or self._action_names)
450
 
        for action_name in action_names:
451
 
            action, arg_names = self._actions[action_name]
 
445
        skip_steps = namespace.skip_steps or []
 
446
        step_names = filter(
 
447
            lambda step_name: step_name not in skip_steps,
 
448
            namespace.steps or self._step_names)
 
449
        for step_name in step_names:
 
450
            step, arg_names = self._steps[step_name]
452
451
            args = [getattr(namespace, i) for i in arg_names]
453
452
            try:
454
 
                action(*args)
 
453
                step(*args)
455
454
            except subprocess.CalledProcessError as err:
456
455
                return err