summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/config.py26
-rw-r--r--lib/spack/spack/test/config.py48
2 files changed, 72 insertions, 2 deletions
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
index c73b5047f5..4a684986eb 100644
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -32,6 +32,7 @@ schemas are in submodules of :py:mod:`spack.schema`.
import copy
import os
+import re
import sys
import multiprocessing
from contextlib import contextmanager
@@ -354,6 +355,15 @@ class Configuration(object):
"""Non-internal scope with highest precedence."""
return next(reversed(self.file_scopes), None)
+ def matching_scopes(self, reg_expr):
+ """
+ List of all scopes whose names match the provided regular expression.
+
+ For example, matching_scopes(r'^command') will return all scopes
+ whose names begin with `command`.
+ """
+ return [s for s in self.scopes.values() if re.search(reg_expr, s.name)]
+
def _validate_scope(self, scope):
"""Ensure that scope is valid in this configuration.
@@ -539,13 +549,25 @@ def override(path_or_scope, value=None):
an internal config scope for it and push/pop that scope.
"""
+ base_name = 'overrides-'
if isinstance(path_or_scope, ConfigScope):
overrides = path_or_scope
config.push_scope(path_or_scope)
else:
- overrides = InternalConfigScope('overrides')
+ # Ensure the new override gets a unique scope name
+ current_overrides = [s.name for s in
+ config.matching_scopes(r'^{0}'.format(base_name))]
+ num_overrides = len(current_overrides)
+ while True:
+ scope_name = '{0}{1}'.format(base_name, num_overrides)
+ if scope_name in current_overrides:
+ num_overrides += 1
+ else:
+ break
+
+ overrides = InternalConfigScope(scope_name)
config.push_scope(overrides)
- config.set(path_or_scope, value, scope='overrides')
+ config.set(path_or_scope, value, scope=scope_name)
yield config
diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py
index 163fc392d3..99e1d7499c 100644
--- a/lib/spack/spack/test/config.py
+++ b/lib/spack/spack/test/config.py
@@ -627,6 +627,54 @@ config:
spack.config._add_command_line_scopes(mutable_config, [str(tmpdir)])
+@pytest.mark.nomockstage
+def test_nested_override():
+ """Ensure proper scope naming of nested overrides."""
+ # WARNING: Base name must match that used in `config.py`s `override()`.
+ base_name = 'overrides-'
+
+ def _check_scopes(num_expected, debug_values):
+ scope_names = [s.name for s in spack.config.config.scopes.values()]
+
+ for i in range(num_expected):
+ name = '{0}{1}'.format(base_name, i)
+ assert name in scope_names
+
+ data = spack.config.config.get_config('config', name)
+ assert data['debug'] == debug_values[i]
+
+ # Check results from single and nested override
+ with spack.config.override('config:debug', True):
+ with spack.config.override('config:debug', False):
+ _check_scopes(2, [True, False])
+
+ _check_scopes(1, [True])
+
+
+@pytest.mark.nomockstage
+def test_alternate_override(monkeypatch):
+ """Ensure proper scope naming of override when conflict present."""
+ # WARNING: Base name must match that used in `config.py`s `override()`.
+ base_name = 'overrides-'
+
+ def _matching_scopes(regexpr):
+ return [spack.config.InternalConfigScope('{0}1'.format(base_name))]
+
+ # Check that the alternate naming works
+ monkeypatch.setattr(spack.config.config, 'matching_scopes',
+ _matching_scopes)
+
+ with spack.config.override('config:debug', False):
+ name = '{0}2'.format(base_name)
+
+ scope_names = [s.name for s in spack.config.config.scopes.values() if
+ s.name.startswith(base_name)]
+ assert name in scope_names
+
+ data = spack.config.config.get_config('config', name)
+ assert data['debug'] is False
+
+
def test_immutable_scope(tmpdir):
config_yaml = str(tmpdir.join('config.yaml'))
with open(config_yaml, 'w') as f: