Revert "Assign priorities to configuration scopes (#48420)" (#49185)

All the build jobs in pipelines are apparently relying on the bug that was fixed.

The issue was not caught in the PR because generation jobs were fine, and
there was nothing to rebuild.

Reverting to fix pipelines in a new PR.

This reverts commit 3ad99d75f9.
This commit is contained in:
Massimiliano Culpo 2025-02-25 11:33:41 +01:00 committed by GitHub
parent 2c26c429a7
commit 9e508b0321
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 109 additions and 343 deletions

View File

@ -223,10 +223,6 @@ def setup(sphinx):
("py:class", "spack.compiler.CompilerCache"), ("py:class", "spack.compiler.CompilerCache"),
# TypeVar that is not handled correctly # TypeVar that is not handled correctly
("py:class", "llnl.util.lang.T"), ("py:class", "llnl.util.lang.T"),
("py:class", "llnl.util.lang.KT"),
("py:class", "llnl.util.lang.VT"),
("py:obj", "llnl.util.lang.KT"),
("py:obj", "llnl.util.lang.VT"),
] ]
# The reST default role (used for this markup: `text`) to use for all documents. # The reST default role (used for this markup: `text`) to use for all documents.

View File

@ -14,7 +14,7 @@
import typing import typing
import warnings import warnings
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Callable, Dict, Iterable, List, Mapping, Optional, Tuple, TypeVar from typing import Callable, Dict, Iterable, List, Tuple, TypeVar
# Ignore emacs backups when listing modules # Ignore emacs backups when listing modules
ignore_modules = r"^\.#|~$" ignore_modules = r"^\.#|~$"
@ -1080,88 +1080,3 @@ def __set__(self, instance, value):
def factory(self, instance, owner): def factory(self, instance, owner):
raise NotImplementedError("must be implemented by derived classes") raise NotImplementedError("must be implemented by derived classes")
KT = TypeVar("KT")
VT = TypeVar("VT")
class PriorityOrderedMapping(Mapping[KT, VT]):
"""Mapping that iterates over key according to an integer priority. If the priority is
the same for two keys, insertion order is what matters.
The priority is set when the key/value pair is added. If not set, the highest current priority
is used.
"""
_data: Dict[KT, VT]
_priorities: List[Tuple[int, KT]]
def __init__(self) -> None:
self._data = {}
# Tuple of (priority, key)
self._priorities = []
def __getitem__(self, key: KT) -> VT:
return self._data[key]
def __len__(self) -> int:
return len(self._data)
def __iter__(self):
yield from (key for _, key in self._priorities)
def __reversed__(self):
yield from (key for _, key in reversed(self._priorities))
def reversed_keys(self):
"""Iterates over keys from the highest priority, to the lowest."""
return reversed(self)
def reversed_values(self):
"""Iterates over values from the highest priority, to the lowest."""
yield from (self._data[key] for _, key in reversed(self._priorities))
def _highest_priority(self) -> int:
if not self._priorities:
return 0
result, _ = self._priorities[-1]
return result
def add(self, key: KT, *, value: VT, priority: Optional[int] = None) -> None:
"""Adds a key/value pair to the mapping, with a specific priority.
If the priority is None, then it is assumed to be the highest priority value currently
in the container.
Raises:
ValueError: when the same priority is already in the mapping
"""
if priority is None:
priority = self._highest_priority()
if key in self._data:
self.remove(key)
self._priorities.append((priority, key))
# We rely on sort being stable
self._priorities.sort(key=lambda x: x[0])
self._data[key] = value
assert len(self._data) == len(self._priorities)
def remove(self, key: KT) -> VT:
"""Removes a key from the mapping.
Returns:
The value associated with the key being removed
Raises:
KeyError: if the key is not in the mapping
"""
if key not in self._data:
raise KeyError(f"cannot find {key}")
popped_item = self._data.pop(key)
self._priorities = [(p, k) for p, k in self._priorities if k != key]
assert len(self._data) == len(self._priorities)
return popped_item

View File

@ -528,6 +528,7 @@ def __call__(self, parser, namespace, values, option_string):
# the const from the constructor or a value from the CLI. # the const from the constructor or a value from the CLI.
# Note that this is only called if the argument is actually # Note that this is only called if the argument is actually
# specified on the command line. # specified on the command line.
spack.config.CONFIG.ensure_scope_ordering()
spack.config.set(self.config_path, self.const, scope="command_line") spack.config.set(self.config_path, self.const, scope="command_line")

View File

@ -66,8 +66,6 @@
import spack.util.web as web_util import spack.util.web as web_util
from spack.util.cpus import cpus_available from spack.util.cpus import cpus_available
from .enums import ConfigScopePriority
#: Dict from section names -> schema for that section #: Dict from section names -> schema for that section
SECTION_SCHEMAS: Dict[str, Any] = { SECTION_SCHEMAS: Dict[str, Any] = {
"compilers": spack.schema.compilers.schema, "compilers": spack.schema.compilers.schema,
@ -410,18 +408,26 @@ def _method(self, *args, **kwargs):
return _method return _method
ScopeWithOptionalPriority = Union[ConfigScope, Tuple[int, ConfigScope]]
ScopeWithPriority = Tuple[int, ConfigScope]
class Configuration: class Configuration:
"""A hierarchical configuration, merging a number of scopes at different priorities.""" """A full Spack configuration, from a hierarchy of config files.
This class makes it easy to add a new scope on top of an existing one.
"""
# convert to typing.OrderedDict when we drop 3.6, or OrderedDict when we reach 3.9 # convert to typing.OrderedDict when we drop 3.6, or OrderedDict when we reach 3.9
scopes: lang.PriorityOrderedMapping[str, ConfigScope] scopes: Dict[str, ConfigScope]
def __init__(self) -> None: def __init__(self, *scopes: ConfigScope) -> None:
self.scopes = lang.PriorityOrderedMapping() """Initialize a configuration with an initial list of scopes.
Args:
scopes: list of scopes to add to this
Configuration, ordered from lowest to highest precedence
"""
self.scopes = collections.OrderedDict()
for scope in scopes:
self.push_scope(scope)
self.format_updates: Dict[str, List[ConfigScope]] = collections.defaultdict(list) self.format_updates: Dict[str, List[ConfigScope]] = collections.defaultdict(list)
def ensure_unwrapped(self) -> "Configuration": def ensure_unwrapped(self) -> "Configuration":
@ -429,31 +435,36 @@ def ensure_unwrapped(self) -> "Configuration":
return self return self
def highest(self) -> ConfigScope: def highest(self) -> ConfigScope:
"""Scope with the highest precedence""" """Scope with highest precedence"""
return next(self.scopes.reversed_values()) # type: ignore return next(reversed(self.scopes.values())) # type: ignore
@_config_mutator @_config_mutator
def push_scope(self, scope: ConfigScope, priority: Optional[int] = None) -> None: def ensure_scope_ordering(self):
"""Adds a scope to the Configuration, at a given priority. """Ensure that scope order matches documented precedent"""
# FIXME: We also need to consider that custom configurations and other orderings
# may not be preserved correctly
if "command_line" in self.scopes:
# TODO (when dropping python 3.6): self.scopes.move_to_end
self.scopes["command_line"] = self.remove_scope("command_line")
If a priority is not given, it is assumed to be the current highest priority. @_config_mutator
def push_scope(self, scope: ConfigScope) -> None:
"""Add a higher precedence scope to the Configuration."""
tty.debug(f"[CONFIGURATION: PUSH SCOPE]: {str(scope)}", level=2)
self.scopes[scope.name] = scope
Args: @_config_mutator
scope: scope to be added def pop_scope(self) -> ConfigScope:
priority: priority of the scope """Remove the highest precedence scope and return it."""
""" name, scope = self.scopes.popitem(last=True) # type: ignore[call-arg]
tty.debug(f"[CONFIGURATION: PUSH SCOPE]: {str(scope)}, priority={priority}", level=2) tty.debug(f"[CONFIGURATION: POP SCOPE]: {str(scope)}", level=2)
self.scopes.add(scope.name, value=scope, priority=priority) return scope
@_config_mutator @_config_mutator
def remove_scope(self, scope_name: str) -> Optional[ConfigScope]: def remove_scope(self, scope_name: str) -> Optional[ConfigScope]:
"""Removes a scope by name, and returns it. If the scope does not exist, returns None.""" """Remove scope by name; has no effect when ``scope_name`` does not exist"""
try: scope = self.scopes.pop(scope_name, None)
scope = self.scopes.remove(scope_name)
tty.debug(f"[CONFIGURATION: POP SCOPE]: {str(scope)}", level=2) tty.debug(f"[CONFIGURATION: POP SCOPE]: {str(scope)}", level=2)
except KeyError as e:
tty.debug(f"[CONFIGURATION: POP SCOPE]: {e}", level=2)
return None
return scope return scope
@property @property
@ -462,13 +473,15 @@ def writable_scopes(self) -> Generator[ConfigScope, None, None]:
return (s for s in self.scopes.values() if s.writable) return (s for s in self.scopes.values() if s.writable)
def highest_precedence_scope(self) -> ConfigScope: def highest_precedence_scope(self) -> ConfigScope:
"""Writable scope with the highest precedence.""" """Writable scope with highest precedence."""
return next(s for s in self.scopes.reversed_values() if s.writable) return next(s for s in reversed(self.scopes.values()) if s.writable) # type: ignore
def highest_precedence_non_platform_scope(self) -> ConfigScope: def highest_precedence_non_platform_scope(self) -> ConfigScope:
"""Writable non-platform scope with the highest precedence""" """Writable non-platform scope with highest precedence"""
return next( return next(
s for s in self.scopes.reversed_values() if s.writable and not s.is_platform_dependent s
for s in reversed(self.scopes.values()) # type: ignore
if s.writable and not s.is_platform_dependent
) )
def matching_scopes(self, reg_expr) -> List[ConfigScope]: def matching_scopes(self, reg_expr) -> List[ConfigScope]:
@ -735,7 +748,7 @@ def override(
""" """
if isinstance(path_or_scope, ConfigScope): if isinstance(path_or_scope, ConfigScope):
overrides = path_or_scope overrides = path_or_scope
CONFIG.push_scope(path_or_scope, priority=None) CONFIG.push_scope(path_or_scope)
else: else:
base_name = _OVERRIDES_BASE_NAME base_name = _OVERRIDES_BASE_NAME
# Ensure the new override gets a unique scope name # Ensure the new override gets a unique scope name
@ -749,7 +762,7 @@ def override(
break break
overrides = InternalConfigScope(scope_name) overrides = InternalConfigScope(scope_name)
CONFIG.push_scope(overrides, priority=None) CONFIG.push_scope(overrides)
CONFIG.set(path_or_scope, value, scope=scope_name) CONFIG.set(path_or_scope, value, scope=scope_name)
try: try:
@ -759,15 +772,13 @@ def override(
assert scope is overrides assert scope is overrides
def _add_platform_scope( def _add_platform_scope(cfg: Configuration, name: str, path: str, writable: bool = True) -> None:
cfg: Configuration, name: str, path: str, priority: ConfigScopePriority, writable: bool = True
) -> None:
"""Add a platform-specific subdirectory for the current platform.""" """Add a platform-specific subdirectory for the current platform."""
platform = spack.platforms.host().name platform = spack.platforms.host().name
scope = DirectoryConfigScope( scope = DirectoryConfigScope(
f"{name}/{platform}", os.path.join(path, platform), writable=writable f"{name}/{platform}", os.path.join(path, platform), writable=writable
) )
cfg.push_scope(scope, priority=priority) cfg.push_scope(scope)
def config_paths_from_entry_points() -> List[Tuple[str, str]]: def config_paths_from_entry_points() -> List[Tuple[str, str]]:
@ -802,10 +813,11 @@ def create() -> Configuration:
it. It is bundled inside a function so that configuration can be it. It is bundled inside a function so that configuration can be
initialized lazily. initialized lazily.
""" """
cfg = Configuration()
# first do the builtin, hardcoded defaults # first do the builtin, hardcoded defaults
cfg = create_from( builtin = InternalConfigScope("_builtin", CONFIG_DEFAULTS)
(ConfigScopePriority.BUILTIN, InternalConfigScope("_builtin", CONFIG_DEFAULTS)) cfg.push_scope(builtin)
)
# Builtin paths to configuration files in Spack # Builtin paths to configuration files in Spack
configuration_paths = [ configuration_paths = [
@ -835,9 +847,10 @@ def create() -> Configuration:
# add each scope and its platform-specific directory # add each scope and its platform-specific directory
for name, path in configuration_paths: for name, path in configuration_paths:
cfg.push_scope(DirectoryConfigScope(name, path), priority=ConfigScopePriority.CONFIG_FILES) cfg.push_scope(DirectoryConfigScope(name, path))
# Each scope can have per-platform overrides in subdirectories
_add_platform_scope(cfg, name, path, priority=ConfigScopePriority.CONFIG_FILES) # Each scope can have per-platfom overrides in subdirectories
_add_platform_scope(cfg, name, path)
return cfg return cfg
@ -942,7 +955,7 @@ def set(path: str, value: Any, scope: Optional[str] = None) -> None:
return CONFIG.set(path, value, scope) return CONFIG.set(path, value, scope)
def scopes() -> lang.PriorityOrderedMapping[str, ConfigScope]: def scopes() -> Dict[str, ConfigScope]:
"""Convenience function to get list of configuration scopes.""" """Convenience function to get list of configuration scopes."""
return CONFIG.scopes return CONFIG.scopes
@ -1396,7 +1409,7 @@ def ensure_latest_format_fn(section: str) -> Callable[[YamlConfigDict], bool]:
@contextlib.contextmanager @contextlib.contextmanager
def use_configuration( def use_configuration(
*scopes_or_paths: Union[ScopeWithOptionalPriority, str] *scopes_or_paths: Union[ConfigScope, str]
) -> Generator[Configuration, None, None]: ) -> Generator[Configuration, None, None]:
"""Use the configuration scopes passed as arguments within the context manager. """Use the configuration scopes passed as arguments within the context manager.
@ -1411,7 +1424,7 @@ def use_configuration(
global CONFIG global CONFIG
# Normalize input and construct a Configuration object # Normalize input and construct a Configuration object
configuration = create_from(*scopes_or_paths) configuration = _config_from(scopes_or_paths)
CONFIG.clear_caches(), configuration.clear_caches() CONFIG.clear_caches(), configuration.clear_caches()
saved_config, CONFIG = CONFIG, configuration saved_config, CONFIG = CONFIG, configuration
@ -1422,44 +1435,23 @@ def use_configuration(
CONFIG = saved_config CONFIG = saved_config
def _normalize_input(entry: Union[ScopeWithOptionalPriority, str]) -> ScopeWithPriority: @lang.memoized
if isinstance(entry, tuple): def _config_from(scopes_or_paths: List[Union[ConfigScope, str]]) -> Configuration:
return entry scopes = []
for scope_or_path in scopes_or_paths:
default_priority = ConfigScopePriority.CONFIG_FILES # If we have a config scope we are already done
if isinstance(entry, ConfigScope): if isinstance(scope_or_path, ConfigScope):
return default_priority, entry scopes.append(scope_or_path)
continue
# Otherwise we need to construct it # Otherwise we need to construct it
path = os.path.normpath(entry) path = os.path.normpath(scope_or_path)
assert os.path.isdir(path), f'"{path}" must be a directory' assert os.path.isdir(path), f'"{path}" must be a directory'
name = os.path.basename(path) name = os.path.basename(path)
return default_priority, DirectoryConfigScope(name, path) scopes.append(DirectoryConfigScope(name, path))
configuration = Configuration(*scopes)
@lang.memoized return configuration
def create_from(*scopes_or_paths: Union[ScopeWithOptionalPriority, str]) -> Configuration:
"""Creates a configuration object from the scopes passed in input.
Args:
*scopes_or_paths: either a tuple of (priority, ConfigScope), or a ConfigScope, or a string
If priority is not given, it is assumed to be ConfigScopePriority.CONFIG_FILES. If a
string is given, a DirectoryConfigScope is created from it.
Examples:
>>> builtin_scope = InternalConfigScope("_builtin", {"config": {"build_jobs": 1}})
>>> cl_scope = InternalConfigScope("command_line", {"config": {"build_jobs": 10}})
>>> cfg = create_from(
... (ConfigScopePriority.COMMAND_LINE, cl_scope),
... (ConfigScopePriority.BUILTIN, builtin_scope)
... )
"""
scopes_with_priority = [_normalize_input(x) for x in scopes_or_paths]
result = Configuration()
for priority, scope in scopes_with_priority:
result.push_scope(scope, priority=priority)
return result
def raw_github_gitlab_url(url: str) -> str: def raw_github_gitlab_url(url: str) -> str:

View File

@ -12,13 +12,3 @@ class InstallRecordStatus(enum.Flag):
DEPRECATED = enum.auto() DEPRECATED = enum.auto()
MISSING = enum.auto() MISSING = enum.auto()
ANY = INSTALLED | DEPRECATED | MISSING ANY = INSTALLED | DEPRECATED | MISSING
class ConfigScopePriority(enum.IntEnum):
"""Priorities of the different kind of config scopes used by Spack"""
BUILTIN = 0
CONFIG_FILES = 1
ENVIRONMENT = 2
CUSTOM = 3
COMMAND_LINE = 4

View File

@ -51,8 +51,6 @@
from spack.spec_list import SpecList from spack.spec_list import SpecList
from spack.util.path import substitute_path_variables from spack.util.path import substitute_path_variables
from ..enums import ConfigScopePriority
SpecPair = spack.concretize.SpecPair SpecPair = spack.concretize.SpecPair
#: environment variable used to indicate the active environment #: environment variable used to indicate the active environment
@ -3069,12 +3067,14 @@ def env_config_scopes(self) -> List[spack.config.ConfigScope]:
def prepare_config_scope(self) -> None: def prepare_config_scope(self) -> None:
"""Add the manifest's scopes to the global configuration search path.""" """Add the manifest's scopes to the global configuration search path."""
for scope in self.env_config_scopes: for scope in self.env_config_scopes:
spack.config.CONFIG.push_scope(scope, priority=ConfigScopePriority.ENVIRONMENT) spack.config.CONFIG.push_scope(scope)
spack.config.CONFIG.ensure_scope_ordering()
def deactivate_config_scope(self) -> None: def deactivate_config_scope(self) -> None:
"""Remove any of the manifest's scopes from the global config path.""" """Remove any of the manifest's scopes from the global config path."""
for scope in self.env_config_scopes: for scope in self.env_config_scopes:
spack.config.CONFIG.remove_scope(scope.name) spack.config.CONFIG.remove_scope(scope.name)
spack.config.CONFIG.ensure_scope_ordering()
@contextlib.contextmanager @contextlib.contextmanager
def use_config(self): def use_config(self):

View File

@ -47,8 +47,6 @@
import spack.util.environment import spack.util.environment
import spack.util.lock import spack.util.lock
from .enums import ConfigScopePriority
#: names of profile statistics #: names of profile statistics
stat_names = pstats.Stats.sort_arg_dict_default stat_names = pstats.Stats.sort_arg_dict_default
@ -874,19 +872,14 @@ def add_command_line_scopes(
scopes = ev.environment_path_scopes(name, path) scopes = ev.environment_path_scopes(name, path)
if scopes is None: if scopes is None:
if os.path.isdir(path): # directory with config files if os.path.isdir(path): # directory with config files
cfg.push_scope( cfg.push_scope(spack.config.DirectoryConfigScope(name, path, writable=False))
spack.config.DirectoryConfigScope(name, path, writable=False), spack.config._add_platform_scope(cfg, name, path, writable=False)
priority=ConfigScopePriority.CUSTOM,
)
spack.config._add_platform_scope(
cfg, name, path, priority=ConfigScopePriority.CUSTOM, writable=False
)
continue continue
else: else:
raise spack.error.ConfigError(f"Invalid configuration scope: {path}") raise spack.error.ConfigError(f"Invalid configuration scope: {path}")
for scope in scopes: for scope in scopes:
cfg.push_scope(scope, priority=ConfigScopePriority.CUSTOM) cfg.push_scope(scope)
def _main(argv=None): def _main(argv=None):
@ -959,9 +952,7 @@ def _main(argv=None):
# Push scopes from the command line last # Push scopes from the command line last
if args.config_scopes: if args.config_scopes:
add_command_line_scopes(spack.config.CONFIG, args.config_scopes) add_command_line_scopes(spack.config.CONFIG, args.config_scopes)
spack.config.CONFIG.push_scope( spack.config.CONFIG.push_scope(spack.config.InternalConfigScope("command_line"))
spack.config.InternalConfigScope("command_line"), priority=ConfigScopePriority.COMMAND_LINE
)
setup_main_options(args) setup_main_options(args)
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------

View File

@ -542,7 +542,7 @@ def test_build_jobs_sequential_is_sequential():
spack.config.determine_number_of_jobs( spack.config.determine_number_of_jobs(
parallel=False, parallel=False,
max_cpus=8, max_cpus=8,
config=spack.config.create_from( config=spack.config.Configuration(
spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 8}}), spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 8}}),
spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 8}}), spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 8}}),
), ),
@ -556,7 +556,7 @@ def test_build_jobs_command_line_overrides():
spack.config.determine_number_of_jobs( spack.config.determine_number_of_jobs(
parallel=True, parallel=True,
max_cpus=1, max_cpus=1,
config=spack.config.create_from( config=spack.config.Configuration(
spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 10}}), spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 10}}),
spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 1}}), spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 1}}),
), ),
@ -567,7 +567,7 @@ def test_build_jobs_command_line_overrides():
spack.config.determine_number_of_jobs( spack.config.determine_number_of_jobs(
parallel=True, parallel=True,
max_cpus=100, max_cpus=100,
config=spack.config.create_from( config=spack.config.Configuration(
spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 10}}), spack.config.InternalConfigScope("command_line", {"config": {"build_jobs": 10}}),
spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 100}}), spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 100}}),
), ),
@ -581,7 +581,7 @@ def test_build_jobs_defaults():
spack.config.determine_number_of_jobs( spack.config.determine_number_of_jobs(
parallel=True, parallel=True,
max_cpus=10, max_cpus=10,
config=spack.config.create_from( config=spack.config.Configuration(
spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 1}}) spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 1}})
), ),
) )
@ -591,7 +591,7 @@ def test_build_jobs_defaults():
spack.config.determine_number_of_jobs( spack.config.determine_number_of_jobs(
parallel=True, parallel=True,
max_cpus=10, max_cpus=10,
config=spack.config.create_from( config=spack.config.Configuration(
spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 100}}) spack.config.InternalConfigScope("defaults", {"config": {"build_jobs": 100}})
), ),
) )

View File

@ -33,8 +33,6 @@
import spack.util.path as spack_path import spack.util.path as spack_path
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
from ..enums import ConfigScopePriority
# sample config data # sample config data
config_low = { config_low = {
"config": { "config": {
@ -712,7 +710,10 @@ def assert_marked(obj):
def test_internal_config_from_data(): def test_internal_config_from_data():
config = spack.config.create_from( config = spack.config.Configuration()
# add an internal config initialized from an inline dict
config.push_scope(
spack.config.InternalConfigScope( spack.config.InternalConfigScope(
"_builtin", {"config": {"verify_ssl": False, "build_jobs": 6}} "_builtin", {"config": {"verify_ssl": False, "build_jobs": 6}}
) )
@ -1444,7 +1445,7 @@ def test_config_path_dsl(path, it_should_work, expected_parsed):
@pytest.mark.regression("48254") @pytest.mark.regression("48254")
def test_env_activation_preserves_command_line_scope(mutable_mock_env_path): def test_env_activation_preserves_config_scopes(mutable_mock_env_path):
"""Check that the "command_line" scope remains the highest priority scope, when we activate, """Check that the "command_line" scope remains the highest priority scope, when we activate,
or deactivate, environments. or deactivate, environments.
""" """
@ -1468,33 +1469,3 @@ def test_env_activation_preserves_command_line_scope(mutable_mock_env_path):
assert spack.config.CONFIG.highest() == expected_cl_scope assert spack.config.CONFIG.highest() == expected_cl_scope
assert spack.config.CONFIG.highest() == expected_cl_scope assert spack.config.CONFIG.highest() == expected_cl_scope
@pytest.mark.regression("48414")
def test_env_activation_preserves_config_scopes(mutable_mock_env_path):
"""Check that the priority of scopes is respected when merging configuration files."""
custom_scope = spack.config.InternalConfigScope("custom_scope")
spack.config.CONFIG.push_scope(custom_scope, priority=ConfigScopePriority.CUSTOM)
expected_scopes = ["custom_scope", "command_line"]
def highest_priority_scopes(config):
return list(config.scopes)[-2:]
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
# Creating an environment pushes a new scope
ev.create("test")
with ev.read("test"):
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
# No active environment pops the scope
with ev.no_active_environment():
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
# Switch the environment to another one
ev.create("test-2")
with ev.read("test-2"):
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes
assert highest_priority_scopes(spack.config.CONFIG) == expected_scopes

View File

@ -66,8 +66,6 @@
from spack.main import SpackCommand from spack.main import SpackCommand
from spack.util.pattern import Bunch from spack.util.pattern import Bunch
from ..enums import ConfigScopePriority
mirror_cmd = SpackCommand("mirror") mirror_cmd = SpackCommand("mirror")
@ -725,23 +723,11 @@ def configuration_dir(tmpdir_factory, linux_os):
def _create_mock_configuration_scopes(configuration_dir): def _create_mock_configuration_scopes(configuration_dir):
"""Create the configuration scopes used in `config` and `mutable_config`.""" """Create the configuration scopes used in `config` and `mutable_config`."""
return [ return [
(
ConfigScopePriority.BUILTIN,
spack.config.InternalConfigScope("_builtin", spack.config.CONFIG_DEFAULTS), spack.config.InternalConfigScope("_builtin", spack.config.CONFIG_DEFAULTS),
),
(
ConfigScopePriority.CONFIG_FILES,
spack.config.DirectoryConfigScope("site", str(configuration_dir.join("site"))), spack.config.DirectoryConfigScope("site", str(configuration_dir.join("site"))),
),
(
ConfigScopePriority.CONFIG_FILES,
spack.config.DirectoryConfigScope("system", str(configuration_dir.join("system"))), spack.config.DirectoryConfigScope("system", str(configuration_dir.join("system"))),
),
(
ConfigScopePriority.CONFIG_FILES,
spack.config.DirectoryConfigScope("user", str(configuration_dir.join("user"))), spack.config.DirectoryConfigScope("user", str(configuration_dir.join("user"))),
), spack.config.InternalConfigScope("command_line"),
(ConfigScopePriority.COMMAND_LINE, spack.config.InternalConfigScope("command_line")),
] ]
@ -808,11 +794,13 @@ def mock_wsdk_externals(monkeypatch_session):
def concretize_scope(mutable_config, tmpdir): def concretize_scope(mutable_config, tmpdir):
"""Adds a scope for concretization preferences""" """Adds a scope for concretization preferences"""
tmpdir.ensure_dir("concretize") tmpdir.ensure_dir("concretize")
with spack.config.override( mutable_config.push_scope(
spack.config.DirectoryConfigScope("concretize", str(tmpdir.join("concretize"))) spack.config.DirectoryConfigScope("concretize", str(tmpdir.join("concretize")))
): )
yield str(tmpdir.join("concretize")) yield str(tmpdir.join("concretize"))
mutable_config.pop_scope()
spack.repo.PATH._provider_index = None spack.repo.PATH._provider_index = None

View File

@ -519,9 +519,7 @@ def test_error_message_when_using_too_new_lockfile(tmp_path):
("when_possible", True), ("when_possible", True),
], ],
) )
def test_environment_concretizer_scheme_used( def test_environment_concretizer_scheme_used(tmp_path, unify_in_lower_scope, unify_in_spack_yaml):
tmp_path, mutable_config, unify_in_lower_scope, unify_in_spack_yaml
):
"""Tests that "unify" settings in spack.yaml always take precedence over settings in lower """Tests that "unify" settings in spack.yaml always take precedence over settings in lower
configuration scopes. configuration scopes.
""" """
@ -535,10 +533,9 @@ def test_environment_concretizer_scheme_used(
unify: {str(unify_in_spack_yaml).lower()} unify: {str(unify_in_spack_yaml).lower()}
""" """
) )
mutable_config.set("concretizer:unify", unify_in_lower_scope)
assert mutable_config.get("concretizer:unify") == unify_in_lower_scope with spack.config.override("concretizer:unify", unify_in_lower_scope):
with ev.Environment(manifest.parent) as e: with ev.Environment(manifest.parent) as e:
assert mutable_config.get("concretizer:unify") == unify_in_spack_yaml
assert e.unify == unify_in_spack_yaml assert e.unify == unify_in_spack_yaml

View File

@ -364,44 +364,3 @@ def test_fnmatch_multiple():
assert not regex.match("libbar.so.1") assert not regex.match("libbar.so.1")
assert not regex.match("libfoo.solibbar.so") assert not regex.match("libfoo.solibbar.so")
assert not regex.match("libbaz.so") assert not regex.match("libbaz.so")
class TestPriorityOrderedMapping:
@pytest.mark.parametrize(
"elements,expected",
[
# Push out-of-order with explicit, and different, priorities
([("b", 2), ("a", 1), ("d", 4), ("c", 3)], ["a", "b", "c", "d"]),
# Push in-order with priority=None
([("a", None), ("b", None), ("c", None), ("d", None)], ["a", "b", "c", "d"]),
# Mix explicit and implicit priorities
([("b", 2), ("c", None), ("a", 1), ("d", None)], ["a", "b", "c", "d"]),
([("b", 10), ("c", None), ("a", -20), ("d", None)], ["a", "b", "c", "d"]),
([("b", 10), ("c", None), ("a", 20), ("d", None)], ["b", "c", "a", "d"]),
# Adding the same key twice with different priorities
([("b", 10), ("c", None), ("a", 20), ("d", None), ("a", -20)], ["a", "b", "c", "d"]),
# Adding the same key twice, no priorities
([("b", None), ("a", None), ("b", None)], ["a", "b"]),
],
)
def test_iteration_order(self, elements, expected):
"""Tests that the iteration order respects priorities, no matter the insertion order."""
m = llnl.util.lang.PriorityOrderedMapping()
for key, priority in elements:
m.add(key, value=None, priority=priority)
assert list(m) == expected
def test_reverse_iteration(self):
"""Tests that we can conveniently use reverse iteration"""
m = llnl.util.lang.PriorityOrderedMapping()
for key, value in [("a", 1), ("b", 2), ("c", 3)]:
m.add(key, value=value)
assert list(m) == ["a", "b", "c"]
assert list(reversed(m)) == ["c", "b", "a"]
assert list(m.keys()) == ["a", "b", "c"]
assert list(m.reversed_keys()) == ["c", "b", "a"]
assert list(m.values()) == [1, 2, 3]
assert list(m.reversed_values()) == [3, 2, 1]

View File

@ -1,2 +1,3 @@
concretizer: concretizer:
reuse: false reuse: false
unify: false

View File

@ -1,8 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
definitions: definitions:
- apps: - apps:
- gromacs - gromacs

View File

@ -1,8 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
definitions: definitions:
- apps: - apps:
- gromacs %oneapi - gromacs %oneapi

View File

@ -1,9 +1,6 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: target=aarch64 require: target=aarch64

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: target=x86_64_v3 require: target=x86_64_v3

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: require:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: require:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: require:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: require:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: require:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
providers: providers:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
providers: providers:

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: target=x86_64_v3 require: target=x86_64_v3

View File

@ -1,7 +1,5 @@
spack: spack:
view: false view: false
concretizer:
unify: false
packages: packages:
all: all:
require: target=x86_64_v3 require: target=x86_64_v3

View File

@ -5,8 +5,6 @@
spack: spack:
view: false view: false
concretizer:
unify: false
specs: specs:
- vtk~mpi - vtk~mpi