| 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) |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |