summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2021-02-01 17:27:00 +0100
committerMassimiliano Culpo <massimiliano.culpo@gmail.com>2021-05-21 18:00:05 +0200
commitf30fc6cd33c8c201c80dd5d424d923d579f5e39b (patch)
tree8b91d7e6d403fb1b1243d184bb93986f70ff6ec2
parent4e5e1e8f35c3a01fa530a21a39fa7274b54a3989 (diff)
downloadspack-f30fc6cd33c8c201c80dd5d424d923d579f5e39b.tar.gz
spack-f30fc6cd33c8c201c80dd5d424d923d579f5e39b.tar.bz2
spack-f30fc6cd33c8c201c80dd5d424d923d579f5e39b.tar.xz
spack-f30fc6cd33c8c201c80dd5d424d923d579f5e39b.zip
Move context manager to swap the current configuration into spack.config
The context manager can be used to swap the current configuration temporarily, for any use case that may need it. (cherry picked from commit 553d37a6d62b05f15986a702394f67486fa44e0e)
-rw-r--r--lib/spack/spack/config.py67
-rw-r--r--lib/spack/spack/test/cmd/module.py7
-rw-r--r--lib/spack/spack/test/conftest.py68
3 files changed, 78 insertions, 64 deletions
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
index 84d8e8ca3f..6067d65fff 100644
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -29,6 +29,7 @@ schemas are in submodules of :py:mod:`spack.schema`.
"""
import collections
+import contextlib
import copy
import functools
import os
@@ -48,6 +49,7 @@ from llnl.util.filesystem import mkdirp
import spack.paths
import spack.architecture
+import spack.compilers
import spack.schema
import spack.schema.compilers
import spack.schema.mirrors
@@ -803,22 +805,6 @@ def _config():
config = llnl.util.lang.Singleton(_config)
-def replace_config(configuration):
- """Replace the current global configuration with the instance passed as
- argument.
-
- Args:
- configuration (Configuration): the new configuration to be used.
-
- Returns:
- The old configuration that has been removed
- """
- global config
- config.clear_caches(), configuration.clear_caches()
- old_config, config = config, configuration
- return old_config
-
-
def get(path, default=None, scope=None):
"""Module-level wrapper for ``Configuration.get()``."""
return config.get(path, default, scope)
@@ -1133,6 +1119,55 @@ def ensure_latest_format_fn(section):
return update_fn
+@contextlib.contextmanager
+def use_configuration(*scopes_or_paths):
+ """Use the configuration scopes passed as arguments within the
+ context manager.
+
+ Args:
+ *scopes_or_paths: scope objects or paths to be used
+
+ Returns:
+ Configuration object associated with the scopes passed as arguments
+ """
+ global config
+
+ # Normalize input and construct a Configuration object
+ configuration = _config_from(scopes_or_paths)
+ config.clear_caches(), configuration.clear_caches()
+
+ # Save and clear the current compiler cache
+ saved_compiler_cache = spack.compilers._cache_config_file
+ spack.compilers._cache_config_file = []
+
+ saved_config, config = config, configuration
+
+ yield configuration
+
+ # Restore previous config files
+ spack.compilers._cache_config_file = saved_compiler_cache
+ config = saved_config
+
+
+@llnl.util.lang.memoized
+def _config_from(scopes_or_paths):
+ scopes = []
+ for scope_or_path in scopes_or_paths:
+ # If we have a config scope we are already done
+ if isinstance(scope_or_path, ConfigScope):
+ scopes.append(scope_or_path)
+ continue
+
+ # Otherwise we need to construct it
+ path = os.path.normpath(scope_or_path)
+ assert os.path.isdir(path), '"{0}" must be a directory'.format(path)
+ name = os.path.basename(path)
+ scopes.append(ConfigScope(name, path))
+
+ configuration = Configuration(*scopes)
+ return configuration
+
+
class ConfigError(SpackError):
"""Superclass for all Spack config related errors."""
diff --git a/lib/spack/spack/test/cmd/module.py b/lib/spack/spack/test/cmd/module.py
index a488c47d1c..8be6c129c7 100644
--- a/lib/spack/spack/test/cmd/module.py
+++ b/lib/spack/spack/test/cmd/module.py
@@ -8,10 +8,10 @@ import re
import pytest
+import spack.config
import spack.main
import spack.modules
import spack.store
-from spack.test.conftest import use_configuration
module = spack.main.SpackCommand('module')
@@ -19,11 +19,12 @@ module = spack.main.SpackCommand('module')
#: make sure module files are generated for all the tests here
@pytest.fixture(scope='module', autouse=True)
def ensure_module_files_are_there(
- mock_repo_path, mock_store, mock_configuration):
+ mock_repo_path, mock_store, mock_configuration_scopes
+):
"""Generate module files for module tests."""
module = spack.main.SpackCommand('module')
with spack.store.use_store(mock_store):
- with use_configuration(mock_configuration):
+ with spack.config.use_configuration(*mock_configuration_scopes):
with spack.repo.use_repositories(mock_repo_path):
module('tcl', 'refresh', '-y')
diff --git a/lib/spack/spack/test/conftest.py b/lib/spack/spack/test/conftest.py
index 153bee128f..5b1e7b83e6 100644
--- a/lib/spack/spack/test/conftest.py
+++ b/lib/spack/spack/test/conftest.py
@@ -4,7 +4,6 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import collections
-import contextlib
import errno
import inspect
import itertools
@@ -336,7 +335,7 @@ spack.config.config = spack.config._config()
#
-# Context managers used by fixtures
+# Note on context managers used by fixtures
#
# Because these context managers modify global state, they should really
# ONLY be used persistently (i.e., around yield statements) in
@@ -357,23 +356,6 @@ spack.config.config = spack.config._config()
# *USE*, or things can get really confusing.
#
-@contextlib.contextmanager
-def use_configuration(config):
- """Context manager to swap out the global Spack configuration."""
- saved = spack.config.replace_config(config)
-
- # Avoid using real spack configuration that has been cached by other
- # tests, and avoid polluting the cache with spack test configuration
- # (including modified configuration)
- saved_compiler_cache = spack.compilers._cache_config_file
- spack.compilers._cache_config_file = []
-
- yield
-
- spack.config.replace_config(saved)
- spack.compilers._cache_config_file = saved_compiler_cache
-
-
#
# Test-specific fixtures
#
@@ -430,9 +412,7 @@ def default_config():
This ensures we can test the real default configuration without having
tests fail when the user overrides the defaults that we test against."""
defaults_path = os.path.join(spack.paths.etc_path, 'spack', 'defaults')
- defaults_scope = spack.config.ConfigScope('defaults', defaults_path)
- defaults_config = spack.config.Configuration(defaults_scope)
- with use_configuration(defaults_config):
+ with spack.config.use_configuration(defaults_path) as defaults_config:
yield defaults_config
@@ -508,7 +488,7 @@ def configuration_dir(tmpdir_factory, linux_os):
@pytest.fixture(scope='session')
-def mock_configuration(configuration_dir):
+def mock_configuration_scopes(configuration_dir):
"""Create a persistent Configuration object from the configuration_dir."""
defaults = spack.config.InternalConfigScope(
'_builtin', spack.config.config_defaults
@@ -519,14 +499,14 @@ def mock_configuration(configuration_dir):
for name in ['site', 'system', 'user']]
test_scopes.append(spack.config.InternalConfigScope('command_line'))
- yield spack.config.Configuration(*test_scopes)
+ yield test_scopes
@pytest.fixture(scope='function')
-def config(mock_configuration):
+def config(mock_configuration_scopes):
"""This fixture activates/deactivates the mock configuration."""
- with use_configuration(mock_configuration):
- yield mock_configuration
+ with spack.config.use_configuration(*mock_configuration_scopes) as config:
+ yield config
@pytest.fixture(scope='function')
@@ -535,11 +515,10 @@ def mutable_config(tmpdir_factory, configuration_dir):
mutable_dir = tmpdir_factory.mktemp('mutable_config').join('tmp')
configuration_dir.copy(mutable_dir)
- cfg = spack.config.Configuration(
- *[spack.config.ConfigScope(name, str(mutable_dir.join(name)))
- for name in ['site', 'system', 'user']])
+ scopes = [spack.config.ConfigScope(name, str(mutable_dir.join(name)))
+ for name in ['site', 'system', 'user']]
- with use_configuration(cfg):
+ with spack.config.use_configuration(*scopes) as cfg:
yield cfg
@@ -547,23 +526,20 @@ def mutable_config(tmpdir_factory, configuration_dir):
def mutable_empty_config(tmpdir_factory, configuration_dir):
"""Empty configuration that can be modified by the tests."""
mutable_dir = tmpdir_factory.mktemp('mutable_config').join('tmp')
+ scopes = [spack.config.ConfigScope(name, str(mutable_dir.join(name)))
+ for name in ['site', 'system', 'user']]
- cfg = spack.config.Configuration(
- *[spack.config.ConfigScope(name, str(mutable_dir.join(name)))
- for name in ['site', 'system', 'user']])
-
- with use_configuration(cfg):
+ with spack.config.use_configuration(*scopes) as cfg:
yield cfg
@pytest.fixture()
def mock_low_high_config(tmpdir):
"""Mocks two configuration scopes: 'low' and 'high'."""
- config = spack.config.Configuration(
- *[spack.config.ConfigScope(name, str(tmpdir.join(name)))
- for name in ['low', 'high']])
+ scopes = [spack.config.ConfigScope(name, str(tmpdir.join(name)))
+ for name in ['low', 'high']]
- with use_configuration(config):
+ with spack.config.use_configuration(*scopes) as config:
yield config
@@ -612,7 +588,7 @@ def _store_dir_and_cache(tmpdir_factory):
@pytest.fixture(scope='session')
-def mock_store(tmpdir_factory, mock_repo_path, mock_configuration,
+def mock_store(tmpdir_factory, mock_repo_path, mock_configuration_scopes,
_store_dir_and_cache):
"""Creates a read-only mock database with some packages installed note
that the ref count for dyninst here will be 3, as it's recycled
@@ -626,7 +602,7 @@ def mock_store(tmpdir_factory, mock_repo_path, mock_configuration,
# If the cache does not exist populate the store and create it
if not os.path.exists(str(store_cache.join('.spack-db'))):
- with use_configuration(mock_configuration):
+ with spack.config.use_configuration(*mock_configuration_scopes):
with spack.store.use_store(str(store_path)) as store:
with spack.repo.use_repositories(mock_repo_path):
_populate(store.db)
@@ -641,8 +617,10 @@ def mock_store(tmpdir_factory, mock_repo_path, mock_configuration,
@pytest.fixture(scope='function')
-def mutable_mock_store(tmpdir_factory, mock_repo_path, mock_configuration,
- _store_dir_and_cache):
+def mutable_mock_store(
+ tmpdir_factory, mock_repo_path, mock_configuration_scopes,
+ _store_dir_and_cache
+):
"""Creates a read-only mock database with some packages installed note
that the ref count for dyninst here will be 3, as it's recycled
across each install.
@@ -655,7 +633,7 @@ def mutable_mock_store(tmpdir_factory, mock_repo_path, mock_configuration,
# If the cache does not exist populate the store and create it
if not os.path.exists(str(store_cache.join('.spack-db'))):
- with use_configuration(mock_configuration):
+ with spack.config.use_configuration(*mock_configuration_scopes):
with spack.store.use_store(str(store_path)) as store:
with spack.repo.use_repositories(mock_repo_path):
_populate(store.db)