summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Delaruelle <xavier.delaruelle@cea.fr>2023-06-26 11:55:57 +0200
committerGitHub <noreply@github.com>2023-06-26 11:55:57 +0200
commit81647122645477ee42c40e0683de044a4e7722fc (patch)
tree847b7ab093a9d2e553e0389bfa7b8716f7fbda92
parentce0b9ea8cf184c9048cac1ae88f2d69f0e4520c7 (diff)
downloadspack-81647122645477ee42c40e0683de044a4e7722fc.tar.gz
spack-81647122645477ee42c40e0683de044a4e7722fc.tar.bz2
spack-81647122645477ee42c40e0683de044a4e7722fc.tar.xz
spack-81647122645477ee42c40e0683de044a4e7722fc.zip
Add raw attribute to env.set command (#38465)
Update `env.set` command and underlying `SetEnv` object to add the `raw` boolean attribute. `raw` is optional and set to False by default. When set to True, value format is skipped for object when generating environment modifications. With this change it is now possible to define environment variable whose value contains variable reference syntax (like `{foo}` or `{}`) that should be set as-is. Fixes #29578
-rw-r--r--lib/spack/spack/modules/common.py17
-rw-r--r--lib/spack/spack/test/modules/lmod.py9
-rw-r--r--lib/spack/spack/test/modules/tcl.py9
-rw-r--r--lib/spack/spack/util/environment.py16
-rw-r--r--var/spack/repos/builtin.mock/packages/module-setenv-raw/package.py16
5 files changed, 58 insertions, 9 deletions
diff --git a/lib/spack/spack/modules/common.py b/lib/spack/spack/modules/common.py
index 6384069c42..08ef690953 100644
--- a/lib/spack/spack/modules/common.py
+++ b/lib/spack/spack/modules/common.py
@@ -671,6 +671,12 @@ class BaseContext(tengine.Context):
# the configure option section
return None
+ def modification_needs_formatting(self, modification):
+ """Returns True if environment modification entry needs to be formatted."""
+ return (
+ not isinstance(modification, (spack.util.environment.SetEnv)) or not modification.raw
+ )
+
@tengine.context_property
@memoized
def environment_modifications(self):
@@ -734,11 +740,12 @@ class BaseContext(tengine.Context):
_check_tokens_are_valid(x.name, message=msg)
# Transform them
x.name = spec.format(x.name, transform=transform)
- try:
- # Not every command has a value
- x.value = spec.format(x.value)
- except AttributeError:
- pass
+ if self.modification_needs_formatting(x):
+ try:
+ # Not every command has a value
+ x.value = spec.format(x.value)
+ except AttributeError:
+ pass
x.name = str(x.name).replace("-", "_")
return [(type(x).__name__, x) for x in env if x.name not in exclude]
diff --git a/lib/spack/spack/test/modules/lmod.py b/lib/spack/spack/test/modules/lmod.py
index 5cfb6896c3..20c7e30ce7 100644
--- a/lib/spack/spack/test/modules/lmod.py
+++ b/lib/spack/spack/test/modules/lmod.py
@@ -198,6 +198,15 @@ class TestLmod(object):
assert len([x for x in content if 'setenv("MANPATH", "/path/to/man")' in x]) == 1
assert len([x for x in content if 'append_path("MANPATH", "", ":")' in x]) == 0
+ @pytest.mark.regression("29578")
+ def test_setenv_raw_value(self, modulefile_content, module_configuration):
+ """Tests that we can set environment variable value without formatting it."""
+
+ module_configuration("autoload_direct")
+ content = modulefile_content("module-setenv-raw")
+
+ assert len([x for x in content if 'setenv("FOO", "{{name}}, {name}, {{}}, {}")' in x]) == 1
+
def test_help_message(self, modulefile_content, module_configuration):
"""Tests the generation of module help message."""
diff --git a/lib/spack/spack/test/modules/tcl.py b/lib/spack/spack/test/modules/tcl.py
index 283fad2583..9dee83bf0d 100644
--- a/lib/spack/spack/test/modules/tcl.py
+++ b/lib/spack/spack/test/modules/tcl.py
@@ -182,6 +182,15 @@ class TestTcl(object):
assert len([x for x in content if 'setenv MANPATH "/path/to/man"' in x]) == 1
assert len([x for x in content if 'append-path --delim ":" MANPATH ""' in x]) == 0
+ @pytest.mark.regression("29578")
+ def test_setenv_raw_value(self, modulefile_content, module_configuration):
+ """Tests that we can set environment variable value without formatting it."""
+
+ module_configuration("autoload_direct")
+ content = modulefile_content("module-setenv-raw")
+
+ assert len([x for x in content if 'setenv FOO "{{name}}, {name}, {{}}, {}"' in x]) == 1
+
def test_help_message(self, modulefile_content, module_configuration):
"""Tests the generation of module help message."""
diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py
index ded31dcf39..bde7ce3780 100644
--- a/lib/spack/spack/util/environment.py
+++ b/lib/spack/spack/util/environment.py
@@ -351,13 +351,20 @@ class NameValueModifier:
class SetEnv(NameValueModifier):
- __slots__ = ("force",)
+ __slots__ = ("force", "raw")
def __init__(
- self, name: str, value: str, *, trace: Optional[Trace] = None, force: bool = False
+ self,
+ name: str,
+ value: str,
+ *,
+ trace: Optional[Trace] = None,
+ force: bool = False,
+ raw: bool = False,
):
super().__init__(name, value, trace=trace)
self.force = force
+ self.raw = raw
def execute(self, env: MutableMapping[str, str]):
tty.debug(f"SetEnv: {self.name}={str(self.value)}", level=3)
@@ -501,15 +508,16 @@ class EnvironmentModifications:
return Trace(filename=filename, lineno=lineno, context=current_context)
@system_env_normalize
- def set(self, name: str, value: str, *, force: bool = False):
+ def set(self, name: str, value: str, *, force: bool = False, raw: bool = False):
"""Stores a request to set an environment variable.
Args:
name: name of the environment variable
value: value of the environment variable
force: if True, audit will not consider this modification a warning
+ raw: if True, format of value string is skipped
"""
- item = SetEnv(name, value, trace=self._trace(), force=force)
+ item = SetEnv(name, value, trace=self._trace(), force=force, raw=raw)
self.env_modifications.append(item)
@system_env_normalize
diff --git a/var/spack/repos/builtin.mock/packages/module-setenv-raw/package.py b/var/spack/repos/builtin.mock/packages/module-setenv-raw/package.py
new file mode 100644
index 0000000000..106ff2da22
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/module-setenv-raw/package.py
@@ -0,0 +1,16 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from spack.package import *
+
+
+class ModuleSetenvRaw(Package):
+ homepage = "http://www.llnl.gov"
+ url = "http://www.llnl.gov/module-setenv-raw-1.0.tar.gz"
+
+ version("1.0", "0123456789abcdef0123456789abcdef")
+
+ def setup_run_environment(self, env):
+ env.set("FOO", "{{name}}, {name}, {{}}, {}", raw=True)