From d0310394c17b68bddb1afe745c3a89bee62260e4 Mon Sep 17 00:00:00 2001 From: ajohns Date: Tue, 17 Dec 2019 10:47:46 +1100 Subject: [PATCH] -added more executor control - ubind, reset_globals --- src/rez/build_system.py | 5 ++-- src/rez/resolved_context.py | 7 +++++ src/rez/rex.py | 58 +++++++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/rez/build_system.py b/src/rez/build_system.py index aa6206fa9..8ade6953f 100644 --- a/src/rez/build_system.py +++ b/src/rez/build_system.py @@ -274,8 +274,9 @@ def add_standard_build_actions(cls, executor, context, variant, build_type, pre_build_commands = getattr(variant, "pre_build_commands") if pre_build_commands: - executor.bind("this", variant) - executor.execute_code(pre_build_commands, isolate=True) + with executor.reset_globals(): + executor.bind("this", variant) + executor.execute_code(pre_build_commands) # Copyright 2013-2016 Allan Johns. diff --git a/src/rez/resolved_context.py b/src/rez/resolved_context.py index aaf7b8017..e2eb9e22d 100644 --- a/src/rez/resolved_context.py +++ b/src/rez/resolved_context.py @@ -1678,6 +1678,13 @@ def _execute(self, executor): raise PackageCommandError(msg) + # clear bindings from last variant. Note that we could've used + # executor.reset_globals to do this, however manually clearing the last + # bindings avoid lots of dict copies and updates. + # + for name in ("this", "version", "root", "base"): + executor.unbind(name) + header_comment(executor, "post system setup") # append suite paths based on suite visibility setting diff --git a/src/rez/rex.py b/src/rez/rex.py index 02d9ae709..2abd076c3 100644 --- a/src/rez/rex.py +++ b/src/rez/rex.py @@ -7,7 +7,9 @@ import re import inspect import traceback +from contextlib import contextmanager from string import Formatter + from rez.system import system from rez.config import config from rez.exceptions import RexError, RexUndefinedVariableError, RezSystemError @@ -1191,9 +1193,47 @@ def __getattr__(self, attr): else getattr(super(RexExecutor, self), attr) def bind(self, name, obj): - """Binds an object to the execution context.""" + """Binds an object to the execution context. + + Args: + name (str) Variable name to bind to. + obj (object): Object to bind. + """ self.globals[name] = obj + def unbind(self, name): + """Unbind an object from the execution context. + + Has no effect if the binding does not exist. + + Args: + name (str) Variable name to bind to. + """ + self.globals.pop(name, None) + + @contextmanager + def reset_globals(self): + """Remove changes to globals dict post-context. + + Any bindings (self.bind) will only be visible during this context. + """ + + # we want to execute the code using self.globals - if for no other + # reason that self.formatter is pointing at self.globals, so if we + # passed in a copy, we would also need to make self.formatter "look" at + # the same copy - but we don't want to "pollute" our namespace, because + # the same executor may be used to run multiple packages. Therefore, + # we save a copy of self.globals before execution, and restore it after + # + saved_globals = dict(self.globals) + + try: + yield + + finally: + self.globals.clear() + self.globals.update(saved_globals) + def append_system_paths(self): """Append system paths to $PATH.""" from rez.shells import Shell, create_shell @@ -1272,28 +1312,16 @@ def execute_code(self, code, filename=None, isolate=False): code (str or SourceCode): Rex code to execute. filename (str): Filename to report if there are syntax errors. isolate (bool): If True, do not affect `self.globals` by executing - this code. + this code. DEPRECATED - use `self.reset_globals` instead. """ def _apply(): self.compile_code(code=code, filename=filename, exec_namespace=self.globals) - # we want to execute the code using self.globals - if for no other - # reason that self.formatter is pointing at self.globals, so if we - # passed in a copy, we would also need to make self.formatter "look" at - # the same copy - but we don't want to "pollute" our namespace, because - # the same executor may be used to run multiple packages. Therefore, - # we save a copy of self.globals before execution, and restore it after - # if isolate: - saved_globals = dict(self.globals) - - try: + with self.reset_globals(): _apply() - finally: - self.globals.clear() - self.globals.update(saved_globals) else: _apply()