Index: scripts/slave/annotated_run.py |
diff --git a/scripts/slave/annotated_run.py b/scripts/slave/annotated_run.py |
index 769977c66d12643f6e302eb61aa0862ee4d7cc3e..1c0f7bb69d9f84db336fdb70d86d4b07dc35b157 100755 |
--- a/scripts/slave/annotated_run.py |
+++ b/scripts/slave/annotated_run.py |
@@ -204,11 +204,6 @@ |
@property |
def retcode(self): |
return self._retcode |
- |
- @retcode.setter |
- def retcode(self, val): |
- assert self._retcode is None, 'Can\'t override already-defined retcode' |
- self._retcode = val |
@property |
def presentation(self): |
@@ -452,6 +447,7 @@ |
Recipe modules that are aware of the engine: |
* properties - uses engine.properties. |
+ * step_history - uses engine.step_history. |
* step - uses engine.create_step(...). |
This class acts mostly as a documentation of expected public engine interface. |
@@ -499,13 +495,6 @@ |
raise NotImplementedError |
-def _merge(*dicts): |
- result = {} |
- for d in dicts: |
- result.update(d) |
- return result |
- |
- |
class SequentialRecipeEngine(RecipeEngine): |
"""Always runs step sequentially. Currently the engine used by default.""" |
def __init__(self, stream, properties, test_data): |
@@ -513,11 +502,10 @@ |
self._stream = stream |
self._properties = properties |
self._test_data = test_data |
- self._step_results = collections.OrderedDict() |
- self._step_disambiguation_index = {} |
- |
- self._annotation = None |
- self._step_result = None |
+ self._step_history = collections.OrderedDict() |
+ |
+ self._previous_step_annotation = None |
+ self._previous_step_result = None |
self._api = None |
@property |
@@ -527,14 +515,14 @@ |
@property |
def previous_step_result(self): |
"""Allows api.step to get the active result from any context.""" |
- return self._step_result |
+ return self._previous_step_result |
def _emit_results(self): |
- annotation = self._annotation |
- step_result = self._step_result |
- |
- self._annotation = None |
- self._step_result = None |
+ annotation = self._previous_step_annotation |
+ step_result = self._previous_step_result |
+ |
+ self._previous_step_annotation = None |
+ self._previous_step_result = None |
if not annotation or not step_result: |
return |
@@ -551,124 +539,47 @@ |
step_result._step['~followup_annotations'] = lines |
annotation.step_ended() |
- def _disambiguate_name(self, step_name): |
- if step_name in self._step_disambiguation_index: |
- self._step_disambiguation_index[step_name] += 1 |
- step_name += ' (%s)' % self._step_disambiguation_index[step_name] |
+ def run_step(self, step): |
+ ok_ret = step.pop('ok_ret') |
+ infra_step = step.pop('infra_step') |
+ |
+ test_data_fn = step.pop('step_test_data', recipe_test_api.StepTestData) |
+ step_test = self._test_data.pop_step_test_data(step['name'], |
+ test_data_fn) |
+ placeholders = render_step(step, step_test) |
+ |
+ self._step_history[step['name']] = step |
+ self._emit_results() |
+ |
+ step_result = None |
+ |
+ if not self._test_data.enabled: |
+ self._previous_step_annotation, retcode = annotator.run_step( |
+ self._stream, **step) |
+ |
+ step_result = StepData(step, retcode) |
+ self._previous_step_annotation.annotation_stream.step_cursor(step['name']) |
else: |
- self._step_disambiguation_index[step_name] = 1 |
- return step_name |
- |
- def _disambiguate_step(self, step): |
- """Disambiguates step (destructively) by adding an index afterward. E.g. |
- |
- gclient sync |
- gclient sync (2) |
- ... |
- """ |
- step['name'] = self._disambiguate_name(step['name']) |
- |
- def _subannotator(self): |
- class Subannotator(object): |
- # We use ann as the self argument because we are closing over the |
- # SequentialRecipeEngine self. |
- # pylint: disable=e0213 |
- def BUILD_STEP(ann, name): |
- self._open_step({'name': self._disambiguate_name(name)}) |
- |
- def STEP_WARNINGS(ann): |
- self._step_result.presentation.status = 'WARNING' |
- def STEP_FAILURE(ann): |
- self._step_result.presentation.status = 'FAILURE' |
- def STEP_EXCEPTION(ann): |
- self._step_result.presentation.status = 'EXCEPTION' |
- |
- def STEP_TEXT(ann, msg): |
- self._step_result.presentation.step_text = msg |
- |
- def STEP_LINK(ann, link_label, link_url): |
- self._step_result.presentation.links[link_label] = link_url |
- |
- def STEP_LOG_LINE(ann, log_label, log_line): |
- self._step_result.presentation.logs[log_label] += log_line |
- def STEP_LOG_END(ann, log_label): |
- # We do step finalization all at once. |
- pass |
- |
- def SET_BUILD_PROPERTY(ann, name, value): |
- self._step_result.presentation.properties[name] = value |
- |
- def STEP_SUMMARY_TEXT(ann, msg): |
- self._step_result.presentation.step_summary_text = msg |
- |
- return Subannotator() |
- |
- def _open_step(self, step): |
- self._emit_results() |
- step_result = StepData(step, None) |
- self._step_results[step['name']] = step_result |
- self._step_result = step_result |
- self._annotation = self._stream.step(step['name']) |
- self._annotation.step_started() |
- if self._test_data.enabled: |
- self._annotation.stream = cStringIO.StringIO() |
- |
- def _step_kernel(self, step, step_test, subannotator=None): |
- if not self._test_data.enabled: |
- # Warning: run_step can change the current self._annotation and |
- # self._step_result if it uses a subannotator. |
- retcode = annotator.run_step( |
- self._stream, |
- step_annotation=self._annotation, |
- subannotator=subannotator, |
- **step) |
- self._step_result.retcode = retcode |
- # TODO(luqui): What is the purpose of this line? |
- self._annotation.annotation_stream.step_cursor( |
- self._step_result.step['name']) |
- else: |
+ self._previous_step_annotation = annotation = self._stream.step( |
+ step['name']) |
+ annotation.step_started() |
try: |
- self._step_result.retcode = step_test.retcode |
+ annotation.stream = cStringIO.StringIO() |
+ |
+ step_result = StepData(step, step_test.retcode) |
except OSError: |
exc_type, exc_value, exc_tb = sys.exc_info() |
trace = traceback.format_exception(exc_type, exc_value, exc_tb) |
trace_lines = ''.join(trace).split('\n') |
- self._annotation.write_log_lines( |
- 'exception', filter(None, trace_lines)) |
- self._annotation.step_exception() |
- |
- return self._step_result.retcode |
- |
- def run_step(self, step): |
- self._disambiguate_step(step) |
- ok_ret = step.pop('ok_ret') |
- infra_step = step.pop('infra_step') |
- allow_subannotations = step.get('allow_subannotations', False) |
- |
- test_data_fn = step.pop('step_test_data', recipe_test_api.StepTestData) |
- step_test = self._test_data.pop_step_test_data(step['name'], test_data_fn) |
- placeholders = render_step(step, step_test) |
- |
- if allow_subannotations: |
- # TODO(luqui) Make this hierarchical. |
- self._open_step(step) |
- start_annotation = self._annotation |
- retcode = self._step_kernel(step, step_test, |
- subannotator=self._subannotator()) |
- |
- # Open a closing step for presentation modifications. |
- if self._annotation != start_annotation: |
- self._open_step({ 'name': step['name'] + ' (end)' }) |
- self._step_result.retcode = retcode |
- else: |
- self._open_step(step) |
- self._step_kernel(step, step_test) |
- |
- get_placeholder_results(self._step_result, placeholders) |
- |
- if self._step_result.retcode in ok_ret: |
- self._step_result.presentation.status = 'SUCCESS' |
- return self._step_result |
+ annotation.write_log_lines('exception', filter(None, trace_lines)) |
+ annotation.step_exception() |
+ |
+ get_placeholder_results(step_result, placeholders) |
+ self._previous_step_result = step_result |
+ |
+ if step_result.retcode in ok_ret: |
+ step_result.presentation.status = 'SUCCESS' |
+ return step_result |
else: |
if not infra_step: |
state = 'FAILURE' |
@@ -677,13 +588,13 @@ |
state = 'EXCEPTION' |
exc = recipe_api.InfraFailure |
- self._step_result.presentation.status = state |
+ step_result.presentation.status = state |
if step_test.enabled: |
# To avoid cluttering the expectations, don't emit this in testmode. |
- self._annotation.emit( |
- 'step returned non-zero exit code: %d' % self._step_result.retcode) |
- |
- raise exc(step['name'], self._step_result) |
+ self._previous_step_annotation.emit( |
+ 'step returned non-zero exit code: %d' % step_result.retcode) |
+ |
+ raise exc(step['name'], step_result) |
def run(self, steps_function, api): |
@@ -695,18 +606,18 @@ |
try: |
retcode = steps_function(api) |
assert retcode is None, ( |
- 'Non-None return from GenSteps is not supported yet') |
+ "Non-None return from GenSteps is not supported yet") |
assert not self._test_data.enabled or not self._test_data.step_data, ( |
- 'Unconsumed test data! %s' % (self._test_data.step_data,)) |
+ "Unconsumed test data! %s" % (self._test_data.step_data,)) |
finally: |
self._emit_results() |
except recipe_api.StepFailure as f: |
retcode = f.retcode or 1 |
final_result = { |
- 'name': '$final_result', |
- 'reason': f.reason, |
- 'status_code': retcode |
+ "name": "$final_result", |
+ "reason": f.reason, |
+ "status_code": retcode |
} |
except Exception as ex: |
@@ -714,9 +625,9 @@ |
retcode = -1 |
final_result = { |
- 'name': '$final_result', |
- 'reason': 'Uncaught Exception: %r' % ex, |
- 'status_code': retcode |
+ "name": "$final_result", |
+ "reason": "Uncaught Exception: %r" % ex, |
+ "status_code": retcode |
} |
with self._stream.step('Uncaught Exception') as s: |
@@ -727,10 +638,9 @@ |
raise |
if final_result is not None: |
- self._step_results[final_result['name']] = ( |
- StepData(final_result, final_result['status_code'])) |
- |
- return RecipeExecutionResult(retcode, self._step_results) |
+ self._step_history[final_result['name']] = final_result |
+ |
+ return RecipeExecutionResult(retcode, self._step_history) |
def create_step(self, step): # pylint: disable=R0201 |
# This version of engine doesn't do anything, just converts step to dict |