OLD | NEW |
(Empty) | |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import contextlib |
| 6 |
| 7 from recipe_engine import recipe_api |
| 8 |
| 9 |
| 10 # Inherit from RecipeApiPlain because the only thing which is a step is |
| 11 # run_from_dict() |
| 12 class StepApi(recipe_api.RecipeApiPlain): |
| 13 def __init__(self, **kwargs): |
| 14 super(StepApi, self).__init__(**kwargs) |
| 15 self._step_names = {} |
| 16 |
| 17 EXCEPTION = 'EXCEPTION' |
| 18 FAILURE = 'FAILURE' |
| 19 SUCCESS = 'SUCCESS' |
| 20 WARNING = 'WARNING' |
| 21 |
| 22 @property |
| 23 def StepFailure(self): |
| 24 """ See recipe_api.py for docs. """ |
| 25 return recipe_api.StepFailure |
| 26 |
| 27 @property |
| 28 def StepWarning(self): |
| 29 """ See recipe_api.py for docs. """ |
| 30 return recipe_api.StepWarning |
| 31 |
| 32 @property |
| 33 def InfraFailure(self): |
| 34 """ See recipe_api.py for docs. """ |
| 35 return recipe_api.InfraFailure |
| 36 |
| 37 @property |
| 38 def active_result(self): |
| 39 """The currently active (open) result from the last step that was run. |
| 40 |
| 41 Allows you to do things like: |
| 42 try: |
| 43 api.step('run test', [..., api.json.output()]) |
| 44 finally: |
| 45 result = api.step.active_result |
| 46 if result.json.output: |
| 47 new_step_text = result.json.output['step_text'] |
| 48 api.step.active_result.presentation.step_text = new_step_text |
| 49 |
| 50 This will update the step_text of the test, even if the test fails. Without |
| 51 this api, the above code would look like: |
| 52 |
| 53 try: |
| 54 result = api.step('run test', [..., api.json.output()]) |
| 55 except api.StepFailure as f: |
| 56 result = f.result |
| 57 raise |
| 58 finally: |
| 59 if result.json.output: |
| 60 new_step_text = result.json.output['step_text'] |
| 61 api.step.active_result.presentation.step_text = new_step_text |
| 62 """ |
| 63 return self._engine.previous_step_result |
| 64 |
| 65 @property |
| 66 def context(self): |
| 67 """ See recipe_api.py for docs. """ |
| 68 return recipe_api.context |
| 69 |
| 70 @contextlib.contextmanager |
| 71 def nest(self, name): |
| 72 """Nest is the high-level interface to annotated hierarchical steps. |
| 73 |
| 74 Calling |
| 75 |
| 76 with api.step.nest(<name>): |
| 77 ... |
| 78 |
| 79 will generate a dummy step and implicitly create a new context (as |
| 80 above); the dummy step will govern annotation emission, while the implicit |
| 81 context will propagate the dummy step's name to subordinate steps. |
| 82 """ |
| 83 self(name, []) |
| 84 context_dict = {'name': name, 'nest_level': 1} |
| 85 with self.context(context_dict): |
| 86 yield |
| 87 |
| 88 @property |
| 89 def defer_results(self): |
| 90 """ See recipe_api.py for docs. """ |
| 91 return recipe_api.defer_results |
| 92 |
| 93 def __call__(self, name, cmd, ok_ret=None, infra_step=False, wrapper=(), |
| 94 **kwargs): |
| 95 """Returns a step dictionary which is compatible with annotator.py. |
| 96 |
| 97 Args: |
| 98 name (string): The name of this step. |
| 99 cmd (list of strings): in the style of subprocess.Popen or None to create |
| 100 a no-op fake step. |
| 101 ok_ret (tuple or set of ints, str): allowed return codes. Any unexpected |
| 102 return codes will cause an exception to be thrown. If you pass in the |
| 103 value 'any' or 'all', the engine will allow any return code to be |
| 104 returned. Defaults to {0} |
| 105 infra_step: Whether or not this is an infrastructure step. Infrastructure |
| 106 steps will place the step in an EXCEPTION state and raise InfraFailure. |
| 107 wrapper: If supplied, a command to prepend to the executed step as a |
| 108 command wrapper. |
| 109 **kwargs: Additional entries to add to the annotator.py step dictionary. |
| 110 |
| 111 Returns: |
| 112 Opaque step object produced and understood by recipe engine. |
| 113 """ |
| 114 assert 'shell' not in kwargs |
| 115 assert cmd is None or isinstance(cmd, list) |
| 116 if not ok_ret: |
| 117 ok_ret = {0} |
| 118 if ok_ret in ('any', 'all'): |
| 119 ok_ret = set(range(-256, 256)) |
| 120 |
| 121 if cmd is not None: |
| 122 command = list(wrapper) |
| 123 command += cmd |
| 124 kwargs['cmd'] = command |
| 125 |
| 126 kwargs['ok_ret'] = ok_ret |
| 127 kwargs['infra_step'] = bool(infra_step) |
| 128 |
| 129 # Obtain information from composite step parent. |
| 130 compositor = recipe_api._STEP_CONTEXT |
| 131 name = compositor.get_with_context('name', name) |
| 132 kwargs['env'] = compositor.get_with_context('env', kwargs.get('env', {})) |
| 133 kwargs['step_nest_level'] = compositor.get_with_context('nest_level', 0) |
| 134 kwargs.setdefault('cwd', self.m.path['slave_build']) |
| 135 |
| 136 # Disambiguate repeated names |
| 137 step_count = self._step_names.setdefault(name, 0) + 1 |
| 138 self._step_names[name] = step_count |
| 139 if step_count > 1: |
| 140 name = "%s (%d)" % (name, step_count) |
| 141 kwargs['name'] = name |
| 142 |
| 143 schema = self.make_config() |
| 144 schema.set_val(kwargs) |
| 145 return self.run_from_dict(self._engine.create_step(schema)) |
| 146 |
| 147 # TODO(martiniss) delete, and make generator_script use **kwargs on step() |
| 148 @recipe_api.composite_step |
| 149 def run_from_dict(self, dct): |
| 150 return self._engine.run_step(dct) |
OLD | NEW |