"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.
This commit is contained in:
John W. Parent 2023-09-01 14:05:02 -04:00 committed by GitHub
parent 818195a3bd
commit b72a268bc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 7 deletions

View file

@ -863,6 +863,7 @@ def add(fullpath, scope=None):
has_existing_value = True has_existing_value = True
path = "" path = ""
override = False override = False
value = syaml.load_config(components[-1])
for idx, name in enumerate(components[:-1]): for idx, name in enumerate(components[:-1]):
# First handle double colons in constructing path # First handle double colons in constructing path
colon = "::" if override else ":" if path else "" colon = "::" if override else ":" if path else ""
@ -883,14 +884,14 @@ def add(fullpath, scope=None):
existing = get_valid_type(path) existing = get_valid_type(path)
# construct value from this point down # construct value from this point down
value = syaml.load_config(components[-1])
for component in reversed(components[idx + 1 : -1]): for component in reversed(components[idx + 1 : -1]):
value = {component: value} value = {component: value}
break break
if override:
path += "::"
if has_existing_value: if has_existing_value:
path, _, value = fullpath.rpartition(":")
value = syaml.load_config(value)
existing = get(path, scope=scope) existing = get(path, scope=scope)
# append values to lists # append values to lists
@ -1231,11 +1232,17 @@ def they_are(t):
return copy.copy(source) return copy.copy(source)
#
# Process a path argument to config.set() that may contain overrides ('::' or
# trailing ':')
#
def process_config_path(path): 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 = [] result = []
if path.startswith(":"): if path.startswith(":"):
raise syaml.SpackYAMLError("Illegal leading `:' in path `{0}'".format(path), "") 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 = syaml.syaml_str(front)
front.append = True 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) result.append(front)
return result return result

View file

@ -277,6 +277,12 @@ def test_add_config_path(mutable_config):
compilers = spack.config.get("packages")["all"]["compiler"] compilers = spack.config.get("packages")["all"]["compiler"]
assert "gcc" in compilers 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") @pytest.mark.regression("17543,23259")
def test_add_config_path_with_enumerated_type(mutable_config): def test_add_config_path_with_enumerated_type(mutable_config):