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

Powered by Google App Engine
This is Rietveld 408576698