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

Side by Side Diff: recipe_engine/step_runner.py

Issue 2933473002: [context] Split "env" values and prefixes. (Closed)
Patch Set: comments, tests, better definition Created 3 years, 6 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
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 calendar 5 import calendar
6 import collections 6 import collections
7 import contextlib 7 import contextlib
8 import datetime 8 import datetime
9 import itertools 9 import itertools
10 import json 10 import json
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 return step_stream 171 return step_stream
172 172
173 return EmptyOpenStep() 173 return EmptyOpenStep()
174 174
175 try: 175 try:
176 rendered_step = render_step( 176 rendered_step = render_step(
177 step_config, recipe_test_api.DisabledTestData() 177 step_config, recipe_test_api.DisabledTestData()
178 ) 178 )
179 step_config = None # Make sure we use rendered step config. 179 step_config = None # Make sure we use rendered step config.
180 180
181 step_env = _merge_envs(os.environ, (rendered_step.config.env or {})) 181 step_env = _merge_envs(os.environ,
182 rendered_step.config.env, rendered_step.config.env_prefixes)
182 # Now that the step's environment is all sorted, evaluate PATH on windows 183 # Now that the step's environment is all sorted, evaluate PATH on windows
183 # to find the actual intended executable. 184 # to find the actual intended executable.
184 rendered_step = _hunt_path(rendered_step, step_env) 185 rendered_step = _hunt_path(rendered_step, step_env)
185 self._print_step(step_stream, rendered_step, step_env) 186 self._print_step(step_stream, rendered_step, step_env)
186 except: 187 except:
187 with self.stream_engine.make_step_stream('Step Preparation Exception') as s: 188 with self.stream_engine.make_step_stream('Step Preparation Exception') as s:
188 s.set_step_status('EXCEPTION') 189 s.set_step_status('EXCEPTION')
189 with s.new_log_stream('exception') as l: 190 with s.new_log_stream('exception') as l:
190 l.write_split(traceback.format_exc()) 191 l.write_split(traceback.format_exc())
191 raise 192 raise
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 * copy() returns self 419 * copy() returns self
419 420
420 The 'formatted' result can be obtained by looking at .data. 421 The 'formatted' result can be obtained by looking at .data.
421 """ 422 """
422 def __init__(self): 423 def __init__(self):
423 self.data = {} 424 self.data = {}
424 425
425 def __getitem__(self, key): 426 def __getitem__(self, key):
426 return '<%s>' % key 427 return '<%s>' % key
427 428
429 def get(self, key, default=None):
430 return self[key]
431
428 def keys(self): 432 def keys(self):
429 return self.data.keys() 433 return self.data.keys()
430 434
435 def pop(self, key, default=None):
436 result = self.data.get(key, default)
437 self.data[key] = None
438 return result
439
431 def __delitem__(self, key): 440 def __delitem__(self, key):
432 self.data[key] = None 441 self.data[key] = None
433 442
434 def __contains__(self, key): 443 def __contains__(self, key):
435 return True 444 return True
436 445
437 def __setitem__(self, key, value): 446 def __setitem__(self, key, value):
438 self.data[key] = value 447 self.data[key] = value
439 448
440 def copy(self): 449 def copy(self):
(...skipping 17 matching lines...) Expand all
458 @property 467 @property
459 def stream_engine(self): 468 def stream_engine(self):
460 return self._stream_engine 469 return self._stream_engine
461 470
462 def open_step(self, step_config): 471 def open_step(self, step_config):
463 try: 472 try:
464 test_data_fn = step_config.step_test_data or recipe_test_api.StepTestData 473 test_data_fn = step_config.step_test_data or recipe_test_api.StepTestData
465 step_test = self._test_data.pop_step_test_data(step_config.name, 474 step_test = self._test_data.pop_step_test_data(step_config.name,
466 test_data_fn) 475 test_data_fn)
467 rendered_step = render_step(step_config, step_test) 476 rendered_step = render_step(step_config, step_test)
468 step_env = _merge_envs(fakeEnviron(), (rendered_step.config.env or {})) 477
478 # Merge our environment. Note that do NOT apply prefixes when rendering
479 # expectations, as they are rendered independently.
480 step_env = _merge_envs(fakeEnviron(), rendered_step.config.env, {})
469 rendered_step = rendered_step._replace( 481 rendered_step = rendered_step._replace(
470 config=rendered_step.config._replace(env=step_env.data)) 482 config=rendered_step.config._replace(env=step_env.data))
471 step_config = None # Make sure we use rendered step config. 483 step_config = None # Make sure we use rendered step config.
472 484
473 # Layer the simulation step on top of the given stream engine. 485 # Layer the simulation step on top of the given stream engine.
474 step_stream = self._stream_engine.new_step_stream(rendered_step.config) 486 step_stream = self._stream_engine.new_step_stream(rendered_step.config)
475 except: 487 except:
476 with self.stream_engine.make_step_stream('Step Preparation Exception') as s: 488 with self.stream_engine.make_step_stream('Step Preparation Exception') as s:
477 s.set_step_status('EXCEPTION') 489 s.set_step_status('EXCEPTION')
478 with s.new_log_stream('exception') as l: 490 with s.new_log_stream('exception') as l:
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
681 if isinstance(ph, util.OutputPlaceholder): 693 if isinstance(ph, util.OutputPlaceholder):
682 setattr(step_result, key, ph.result(step_result.presentation, td)) 694 setattr(step_result, key, ph.result(step_result.presentation, td))
683 else: 695 else:
684 assert isinstance(ph, util.InputPlaceholder), ( 696 assert isinstance(ph, util.InputPlaceholder), (
685 '%s(%r) should be an InputPlaceholder.' % (key, ph)) 697 '%s(%r) should be an InputPlaceholder.' % (key, ph))
686 ph.cleanup(td.enabled) 698 ph.cleanup(td.enabled)
687 699
688 return step_result 700 return step_result
689 701
690 702
691 def _merge_envs(original, override): 703 # Sentinel used by "_merge_envs" to indicate a missing value.
704 _MISSING = object()
705
706
707 def _merge_envs(original, overrides, prefixes):
692 """Merges two environments. 708 """Merges two environments.
693 709
694 Returns a new environment dict with entries from |override| overwriting 710 Returns a new environment dict with entries from |override| overwriting
695 corresponding entries in |original|. Keys whose value is None will completely 711 corresponding entries in |original|. Keys whose value is None will completely
696 remove the environment variable. Values can contain %(KEY)s strings, which 712 remove the environment variable. Values can contain %(KEY)s strings, which
697 will be substituted with the values from the original (useful for amending, as 713 will be substituted with the values from the original (useful for amending, as
698 opposed to overwriting, variables like PATH). 714 opposed to overwriting, variables like PATH).
715
716 See recipe_api.StepConfig for environment construction rules.
699 """ 717 """
700 result = original.copy() 718 result = original.copy()
701 subst = (original if isinstance(original, fakeEnviron) 719 subst = (original if isinstance(original, fakeEnviron)
702 else collections.defaultdict(lambda: '', **original)) 720 else collections.defaultdict(lambda: '', **original))
703 if not override: 721
722 if not any((prefixes, overrides)):
704 return result 723 return result
705 for k, v in override.items(): 724
725 merged = set()
726 for k, path_tuple in prefixes.iteritems():
727 if not path_tuple:
728 continue
729 merged.add(k)
730
731 # If the same key is defined in "overrides", we need to interact with it.
732 # We'll do so here, and skip it in the "overrides" construction.
733 val = overrides.get(k, _MISSING)
734 if val is _MISSING:
735 # Not defined. Append "val" iff it is defined in "original" and not empty.
736 val = original.get(k, '')
737 elif val is not None:
738 val = str(val) % subst
739 if val:
740 path_tuple += (val,)
741 result[k] = os.pathsep.join(str(v) for v in path_tuple)
742
743 for k, v in overrides.iteritems():
744 if k in merged:
745 continue
706 if v is None: 746 if v is None:
707 if k in result: 747 result.pop(k, None)
708 del result[k]
709 else: 748 else:
710 result[str(k)] = str(v) % subst 749 result[k] = str(v) % subst
711 return result 750 return result
712 751
713 752
714 if sys.platform == "win32": 753 if sys.platform == "win32":
715 _hunt_path_exts = ('.exe', '.bat') 754 _hunt_path_exts = ('.exe', '.bat')
716 def _hunt_path(rendered_step, step_env): 755 def _hunt_path(rendered_step, step_env):
717 """This takes the lazy cross-product of PATH and ('.exe', '.bat') to find 756 """This takes the lazy cross-product of PATH and ('.exe', '.bat') to find
718 what cmd.exe would have found for the command if we used shell=True. 757 what cmd.exe would have found for the command if we used shell=True.
719 758
720 This must be called on the render_step AFTER _merge_envs has produced 759 This must be called on the render_step AFTER _merge_envs has produced
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 supplied command, and only uses the |env| kwarg for modifying the environment 827 supplied command, and only uses the |env| kwarg for modifying the environment
789 of the child process. 828 of the child process.
790 """ 829 """
791 saved_path = os.environ['PATH'] 830 saved_path = os.environ['PATH']
792 try: 831 try:
793 if path is not None: 832 if path is not None:
794 os.environ['PATH'] = path 833 os.environ['PATH'] = path
795 yield 834 yield
796 finally: 835 finally:
797 os.environ['PATH'] = saved_path 836 os.environ['PATH'] = saved_path
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698