From b72a268bc565d370bd64589e86929acfd73c08a1 Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:05:02 -0400 Subject: "spack config add": allow values with a ":" (#39279) If you wanted to set a configuration option like `config:install_tree:root` to "C:/path/to/config.yaml", Spack had trouble parsing this because of the ":" in the value. This adds logic to allow using quotes to enclose the value, so you can add `config:install_tree:root:"C:/path/to/config.yaml"`. Configuration keys should never contain a quote character, so the presence of any quote is taken to mean that the rest of the string is specifying the value. --- lib/spack/spack/config.py | 31 ++++++++++++++++++++++++------- lib/spack/spack/test/config.py | 6 ++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py index b3fb5648ac..ea9d12d2bd 100644 --- a/lib/spack/spack/config.py +++ b/lib/spack/spack/config.py @@ -863,6 +863,7 @@ def add(fullpath, scope=None): has_existing_value = True path = "" override = False + value = syaml.load_config(components[-1]) for idx, name in enumerate(components[:-1]): # First handle double colons in constructing path colon = "::" if override else ":" if path else "" @@ -883,14 +884,14 @@ def add(fullpath, scope=None): existing = get_valid_type(path) # construct value from this point down - value = syaml.load_config(components[-1]) for component in reversed(components[idx + 1 : -1]): value = {component: value} break + if override: + path += "::" + if has_existing_value: - path, _, value = fullpath.rpartition(":") - value = syaml.load_config(value) existing = get(path, scope=scope) # append values to lists @@ -1231,11 +1232,17 @@ def merge_yaml(dest, source, prepend=False, append=False): return copy.copy(source) -# -# Process a path argument to config.set() that may contain overrides ('::' or -# trailing ':') -# def process_config_path(path): + """Process a path argument to config.set() that may contain overrides ('::' or + trailing ':') + + Note: quoted value path components will be processed as a single value (escaping colons) + quoted path components outside of the value will be considered ill formed and will + raise. + e.g. `this:is:a:path:'value:with:colon'` will yield: + + [this, is, a, path, value:with:colon] + """ result = [] if path.startswith(":"): raise syaml.SpackYAMLError("Illegal leading `:' in path `{0}'".format(path), "") @@ -1262,6 +1269,16 @@ def process_config_path(path): front = syaml.syaml_str(front) front.append = True + quote = "['\"]" + not_quote = "[^'\"]" + + if re.match(f"^{quote}", path): + m = re.match(rf"^{quote}({not_quote}+){quote}$", path) + if not m: + raise ValueError("Quotes indicate value, but there are additional path entries") + result.append(m.group(1)) + break + result.append(front) return result diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py index d3b5a544ba..1dbbba9cde 100644 --- a/lib/spack/spack/test/config.py +++ b/lib/spack/spack/test/config.py @@ -277,6 +277,12 @@ def test_add_config_path(mutable_config): compilers = spack.config.get("packages")["all"]["compiler"] assert "gcc" in compilers + # Try with an escaped colon + path = 'config:install_tree:root:"C:/path/to/config.yaml"' + spack.config.add(path) + set_value = spack.config.get("config")["install_tree"]["root"] + assert set_value == "C:/path/to/config.yaml" + @pytest.mark.regression("17543,23259") def test_add_config_path_with_enumerated_type(mutable_config): -- cgit v1.2.3-70-g09d2