Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/rez/rex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1114,8 +1114,15 @@ def __init__(self, manager) -> None:
self._var_cache = dict((k, EnvironmentVariable(k, self))
for k in manager.parent_environ.keys())

def _defined_keys(self):
# A key is cached on first access, even if it was never defined (eg a
# package's commands() referencing a variable that does not exist).
# Such keys must not be exposed by the mapping interface, otherwise
# iterating the environment raises RexUndefinedVariableError.
return [k for k in self._var_cache if not self.manager.undefined(k)]

def keys(self):
return self._var_cache.keys()
return dict.fromkeys(self._defined_keys()).keys()

def __repr__(self) -> str:
return '%s(%s)' % (self.__class__.__name__, str(self._var_cache))
Expand All @@ -1129,17 +1136,17 @@ def __setitem__(self, key, value) -> None:
self[key].set(value)

def __contains__(self, key) -> bool:
return (key in self._var_cache)
return (key in self._var_cache and not self.manager.undefined(key))

def __delitem__(self, key) -> None:
del self._var_cache[key]

def __iter__(self):
for key in self._var_cache.keys():
for key in self._defined_keys():
yield key

def __len__(self) -> int:
return len(self._var_cache)
return len(self._defined_keys())


class EnvironmentVariable(object):
Expand Down
21 changes: 21 additions & 0 deletions src/rez/tests/test_rex.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,27 @@ def _rex_3():
_test(_rex_2, env={"A": "foo"}, expected={"A": True, "B": False})
_test(_rex_3, env={}, expected="not b")

def test_11(self) -> None:
"""Test that a failed env lookup doesn't leak into the env mapping."""

def _rex():
try:
env.NOT_SET
except Exception:
pass
return {
"keys": sorted(env.keys()),
"contains": "NOT_SET" in env,
"len": len(env),
"values": [str(x) for x in env.values()],
}

ex = self._create_executor(env={"A": "foo"})
self.assertEqual(
ex.execute_function(_rex),
{"keys": ["A"], "contains": False, "len": 1, "values": ["foo"]},
)

def test_version_binding(self) -> None:
"""Test the Rex binding of the Version class."""
v = VersionBinding(Version("1.2.3alpha"))
Expand Down