Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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] | |
|
iannucci
2017/06/12 19:58:40
oof, this is starting to get weird; this and __get
dnj
2017/06/13 19:31:21
How don't they? This returns __getitem__.
| |
| 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 Loading... | |
| 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 Loading... | |
| 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 # Sentinel used by "_merge_envs" to indicate a missing value. |
| 705 _MISSING = object() | |
| 706 | |
| 707 | |
| 708 def _merge_envs(original, overrides, prefixes, pathsep): | |
| 692 """Merges two environments. | 709 """Merges two environments. |
| 693 | 710 |
| 694 Returns a new environment dict with entries from |override| overwriting | 711 Returns a new environment dict with entries from |override| overwriting |
| 695 corresponding entries in |original|. Keys whose value is None will completely | 712 corresponding entries in |original|. Keys whose value is None will completely |
| 696 remove the environment variable. Values can contain %(KEY)s strings, which | 713 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 | 714 will be substituted with the values from the original (useful for amending, as |
| 698 opposed to overwriting, variables like PATH). | 715 opposed to overwriting, variables like PATH). |
| 716 | |
| 717 See recipe_api.StepConfig for environment construction rules. | |
| 699 """ | 718 """ |
| 700 result = original.copy() | 719 result = original.copy() |
| 701 subst = (original if isinstance(original, fakeEnviron) | 720 subst = (original if isinstance(original, fakeEnviron) |
| 702 else collections.defaultdict(lambda: '', **original)) | 721 else collections.defaultdict(lambda: '', **original)) |
| 703 if not override: | 722 |
| 723 if not any((prefixes, overrides)): | |
| 704 return result | 724 return result |
| 705 for k, v in override.items(): | 725 |
| 726 merged = set() | |
| 727 for k, path_tuple in prefixes.iteritems(): | |
| 728 if not path_tuple: | |
| 729 continue | |
| 730 merged.add(k) | |
| 731 | |
| 732 # If the same key is defined in "overrides", we need to interact with it. | |
| 733 # We'll do so here, and skip it in the "overrides" construction. | |
| 734 val = overrides.get(k, _MISSING) | |
| 735 if val is _MISSING: | |
|
iannucci
2017/06/12 19:58:40
this might be clearer
val = overrides.get(k, or
dnj
2017/06/13 19:31:21
Can't do that b/c we need to apply subst only if i
| |
| 736 # Not defined. Append "val" iff it is defined in "original" and not empty. | |
| 737 val = original.get(k, '') | |
| 738 elif val is not None: | |
| 739 val = str(val) % subst | |
| 740 if val: | |
| 741 path_tuple += (val,) | |
| 742 result[k] = pathsep.join(str(v) for v in path_tuple) | |
| 743 | |
| 744 for k, v in overrides.iteritems(): | |
| 745 if k in merged: | |
| 746 continue | |
| 706 if v is None: | 747 if v is None: |
| 707 if k in result: | 748 result.pop(k, None) |
| 708 del result[k] | |
| 709 else: | 749 else: |
| 710 result[str(k)] = str(v) % subst | 750 result[k] = str(v) % subst |
| 711 return result | 751 return result |
| 712 | 752 |
| 713 | 753 |
| 714 if sys.platform == "win32": | 754 if sys.platform == "win32": |
| 715 _hunt_path_exts = ('.exe', '.bat') | 755 _hunt_path_exts = ('.exe', '.bat') |
| 716 def _hunt_path(rendered_step, step_env): | 756 def _hunt_path(rendered_step, step_env): |
| 717 """This takes the lazy cross-product of PATH and ('.exe', '.bat') to find | 757 """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. | 758 what cmd.exe would have found for the command if we used shell=True. |
| 719 | 759 |
| 720 This must be called on the render_step AFTER _merge_envs has produced | 760 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 Loading... | |
| 788 supplied command, and only uses the |env| kwarg for modifying the environment | 828 supplied command, and only uses the |env| kwarg for modifying the environment |
| 789 of the child process. | 829 of the child process. |
| 790 """ | 830 """ |
| 791 saved_path = os.environ['PATH'] | 831 saved_path = os.environ['PATH'] |
| 792 try: | 832 try: |
| 793 if path is not None: | 833 if path is not None: |
| 794 os.environ['PATH'] = path | 834 os.environ['PATH'] = path |
| 795 yield | 835 yield |
| 796 finally: | 836 finally: |
| 797 os.environ['PATH'] = saved_path | 837 os.environ['PATH'] = saved_path |
| OLD | NEW |