Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: recipe_engine/step_runner.py

Issue 2245113002: Track step nesting in StreamEngine. (Closed) Base URL: https://github.com/luci/recipes-py@emit-initial-properties
Patch Set: Rebase, comments. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « recipe_engine/simulation_test.py ('k') | recipe_engine/stream.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2016 The LUCI Authors. All rights reserved. 1 # Copyright 2016 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0 2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file. 3 # that can be found in the LICENSE file.
4 4
5 import StringIO 5 import StringIO
6 import collections 6 import collections
7 import contextlib 7 import contextlib
8 import datetime 8 import datetime
9 import json 9 import json
10 import os 10 import os
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 """Constructs an OpenStep object which can be used to actually run a step. 90 """Constructs an OpenStep object which can be used to actually run a step.
91 91
92 step_dict parameters: 92 step_dict parameters:
93 name: name of the step, will appear in buildbots waterfall 93 name: name of the step, will appear in buildbots waterfall
94 cmd: command to run, list of one or more strings 94 cmd: command to run, list of one or more strings
95 cwd: absolute path to working directory for the command 95 cwd: absolute path to working directory for the command
96 env: dict with overrides for environment variables 96 env: dict with overrides for environment variables
97 allow_subannotations: if True, lets the step emit its own annotations 97 allow_subannotations: if True, lets the step emit its own annotations
98 trigger_specs: a list of trigger specifications, see also _trigger_builds. 98 trigger_specs: a list of trigger specifications, see also _trigger_builds.
99 stdout: Path to a file to put step stdout into. If used, stdout won't 99 stdout: Path to a file to put step stdout into. If used, stdout won't
100 appear in annotator's stdout (and |allow_subannotations| is 100 appear in annotator's stdout (and |allow_subannotations| is
101 ignored). 101 ignored).
102 stderr: Path to a file to put step stderr into. If used, stderr won't 102 stderr: Path to a file to put step stderr into. If used, stderr won't
103 appear in annotator's stderr. 103 appear in annotator's stderr.
104 stdin: Path to a file to read step stdin from. 104 stdin: Path to a file to read step stdin from.
105 step_nest_level: the step's nesting level.
105 106
106 Returns an OpenStep object. 107 Returns an OpenStep object.
107 """ 108 """
108 raise NotImplementedError() 109 raise NotImplementedError()
109 110
110 def run_recipe(self, universe, recipe, properties): 111 def run_recipe(self, universe, recipe, properties):
111 """Run the recipe named |recipe|. 112 """Run the recipe named |recipe|.
112 113
113 Args: 114 Args:
114 universe: The RecipeUniverse where the recipe lives. 115 universe: The RecipeUniverse where the recipe lives.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 158
158 def __init__(self, stream_engine): 159 def __init__(self, stream_engine):
159 self._stream_engine = stream_engine 160 self._stream_engine = stream_engine
160 161
161 @property 162 @property
162 def stream_engine(self): 163 def stream_engine(self):
163 return self._stream_engine 164 return self._stream_engine
164 165
165 def open_step(self, step_dict): 166 def open_step(self, step_dict):
166 allow_subannotations = step_dict.get('allow_subannotations', False) 167 allow_subannotations = step_dict.get('allow_subannotations', False)
168 nest_level = step_dict.pop('step_nest_level', 0)
169
167 step_stream = self._stream_engine.new_step_stream( 170 step_stream = self._stream_engine.new_step_stream(
168 step_dict['name'], 171 step_dict['name'],
169 allow_subannotations=allow_subannotations) 172 allow_subannotations=allow_subannotations,
173 nest_level=nest_level)
170 if not step_dict.get('cmd'): 174 if not step_dict.get('cmd'):
171 class EmptyOpenStep(OpenStep): 175 class EmptyOpenStep(OpenStep):
172 def run(inner): 176 def run(inner):
173 if 'trigger_specs' in step_dict: 177 if 'trigger_specs' in step_dict:
174 self._trigger_builds(step_stream, step_dict['trigger_specs']) 178 self._trigger_builds(step_stream, step_dict['trigger_specs'])
175 return types.StepData(step_dict, 0) 179 return types.StepData(step_dict, 0)
176 180
177 def finalize(inner): 181 def finalize(inner):
178 step_stream.close() 182 step_stream.close()
179 183
180 @property 184 @property
181 def stream(inner): 185 def stream(inner):
182 return step_stream 186 return step_stream
183 187
184 return EmptyOpenStep() 188 return EmptyOpenStep()
185 189
186 step_dict, placeholders = render_step( 190 step_dict, placeholders = render_step(
187 step_dict, recipe_test_api.DisabledTestData()) 191 step_dict, recipe_test_api.DisabledTestData())
188 cmd = map(str, step_dict['cmd']) 192 cmd = map(str, step_dict['cmd'])
189 step_env = _merge_envs(os.environ, step_dict.get('env', {})) 193 step_env = _merge_envs(os.environ, step_dict.get('env', {}))
190 if 'nest_level' in step_dict:
191 step_stream.step_nest_level(step_dict['nest_level'])
192 self._print_step(step_stream, step_dict, step_env) 194 self._print_step(step_stream, step_dict, step_env)
193 195
194 class ReturnOpenStep(OpenStep): 196 class ReturnOpenStep(OpenStep):
195 def run(inner): 197 def run(inner):
196 try: 198 try:
197 # Open file handles for IO redirection based on file names in 199 # Open file handles for IO redirection based on file names in
198 # step_dict. 200 # step_dict.
199 handles = { 201 handles = {
200 'stdout': step_stream, 202 'stdout': step_stream,
201 'stderr': step_stream, 203 'stderr': step_stream,
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 return change 394 return change
393 395
394 396
395 class SimulationStepRunner(StepRunner): 397 class SimulationStepRunner(StepRunner):
396 """Pretends to run steps, instead recording what would have been run. 398 """Pretends to run steps, instead recording what would have been run.
397 399
398 This is the main workhorse of recipes.py simulation_test. Returns the log of 400 This is the main workhorse of recipes.py simulation_test. Returns the log of
399 steps that would have been run in steps_ran. Uses test_data to mock return 401 steps that would have been run in steps_ran. Uses test_data to mock return
400 values. 402 values.
401 """ 403 """
402 def __init__(self, stream_engine, test_data): 404 def __init__(self, stream_engine, test_data, annotator):
403 self._test_data = test_data 405 self._test_data = test_data
404 self._stream_engine = stream_engine 406 self._stream_engine = stream_engine
407 self._annotator = annotator
405 self._step_history = collections.OrderedDict() 408 self._step_history = collections.OrderedDict()
406 409
407 @property 410 @property
408 def stream_engine(self): 411 def stream_engine(self):
409 return self._stream_engine 412 return self._stream_engine
410 413
411 def open_step(self, step_dict): 414 def open_step(self, step_dict):
412 # We modify step_dict. In particular, we add ~followup_annotations during 415 # We modify step_dict. In particular, we add ~followup_annotations during
413 # finalize, and depend on that side-effect being carried into what we 416 # finalize, and depend on that side-effect being carried into what we
414 # added to self._step_history, earlier. So copy it here so at least we 417 # added to self._step_history, earlier. So copy it here so at least we
415 # keep the modifications local. 418 # keep the modifications local.
416 step_dict = dict(step_dict) 419 step_dict = dict(step_dict)
420 nest_level = step_dict.pop('step_nest_level', 0)
417 421
418 test_data_fn = step_dict.pop('step_test_data', recipe_test_api.StepTestData) 422 test_data_fn = step_dict.pop('step_test_data', recipe_test_api.StepTestData)
419 step_test = self._test_data.pop_step_test_data( 423 step_test = self._test_data.pop_step_test_data(
420 step_dict['name'], test_data_fn) 424 step_dict['name'], test_data_fn)
421 step_dict, placeholders = render_step(step_dict, step_test) 425 step_dict, placeholders = render_step(step_dict, step_test)
422 outstream = StringIO.StringIO()
423 426
424 # Layer the simulation step on top of the given stream engine. 427 # Layer the simulation step on top of the given stream engine.
425 step_stream = stream.ProductStreamEngine.StepStream( 428 step_stream = self._stream_engine.new_step_stream(
426 self._stream_engine.new_step_stream(step_dict['name']), 429 step_dict['name'],
427 stream.BareAnnotationStepStream(outstream)) 430 nest_level=nest_level)
428 431
429 class ReturnOpenStep(OpenStep): 432 class ReturnOpenStep(OpenStep):
430 def run(inner): 433 def run(inner):
431 timeout = step_dict.get('timeout') 434 timeout = step_dict.get('timeout')
432 if (timeout and step_test.times_out_after and 435 if (timeout and step_test.times_out_after and
433 step_test.times_out_after > timeout): 436 step_test.times_out_after > timeout):
434 raise recipe_api.StepTimeout(step_dict['name'], timeout) 437 raise recipe_api.StepTimeout(step_dict['name'], timeout)
435 438
436 self._step_history[step_dict['name']] = step_dict 439 self._step_history[step_dict['name']] = step_dict
437 return construct_step_result(step_dict, step_test.retcode, placeholders) 440 return construct_step_result(step_dict, step_test.retcode, placeholders)
438 441
439 def finalize(inner): 442 def finalize(inner):
440 # note that '~' sorts after 'z' so that this will be last on each 443 # note that '~' sorts after 'z' so that this will be last on each
441 # step. also use _step to get access to the mutable step 444 # step. also use _step to get access to the mutable step
442 # dictionary. 445 # dictionary.
443 lines = filter(None, outstream.getvalue()).splitlines() 446 buf = self._annotator.step_buffer(step_dict['name'])
447 lines = filter(None, buf.getvalue()).splitlines()
444 lines = [stream.encode_str(x) for x in lines] 448 lines = [stream.encode_str(x) for x in lines]
445 if lines: 449 if lines:
446 # This magically floats into step_history, which we have already 450 # This magically floats into step_history, which we have already
447 # added step_dict to. 451 # added step_dict to.
448 step_dict['~followup_annotations'] = lines 452 step_dict['~followup_annotations'] = lines
453 step_stream.close()
449 454
450 @property 455 @property
451 def stream(inner): 456 def stream(inner):
452 return step_stream 457 return step_stream
453 458
454 return ReturnOpenStep() 459 return ReturnOpenStep()
455 460
456 def run_recipe(self, universe, recipe, properties): 461 def run_recipe(self, universe, recipe, properties):
457 return self._test_data.depend_on_data.pop(types.freeze((recipe, properties), )) 462 return self._test_data.depend_on_data.pop(types.freeze((recipe, properties), ))
458 463
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 supplied command, and only uses the |env| kwarg for modifying the environment 661 supplied command, and only uses the |env| kwarg for modifying the environment
657 of the child process. 662 of the child process.
658 """ 663 """
659 saved_path = os.environ['PATH'] 664 saved_path = os.environ['PATH']
660 try: 665 try:
661 if path is not None: 666 if path is not None:
662 os.environ['PATH'] = path 667 os.environ['PATH'] = path
663 yield 668 yield
664 finally: 669 finally:
665 os.environ['PATH'] = saved_path 670 os.environ['PATH'] = saved_path
OLDNEW
« no previous file with comments | « recipe_engine/simulation_test.py ('k') | recipe_engine/stream.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698