diff --git a/lib/spack/spack/cmd/config.py b/lib/spack/spack/cmd/config.py index c4446b475afcbe..b3bc2862c1f52a 100644 --- a/lib/spack/spack/cmd/config.py +++ b/lib/spack/spack/cmd/config.py @@ -71,6 +71,21 @@ def setup_parser(subparser): sp.add_parser("list", help="list configuration sections") + list_scopes_parser = sp.add_parser("list-scopes", help="list defined scopes") + stype = list_scopes_parser.add_mutually_exclusive_group(required=False) + stype.add_argument( + "--file", + action="store_true", + default=False, + help="list only writable scopes with an associated file" + ) + stype.add_argument( + "--non-platform", + action="store_true", + default=False, + help="list only non-platform scopes" + ) + add_parser = sp.add_parser("add", help="add configuration parameters") add_parser.add_argument( "path", @@ -196,6 +211,11 @@ def config_list(args): print(" ".join(list(spack.config.SECTION_SCHEMAS))) +def config_list_scopes(args): + scopes = reversed(spack.config.CONFIG.file_scopes) if args.file else spack.config.CONFIG._non_platform_scopes if args.non_platform else reversed(spack.config.CONFIG.scopes.values()) + print(" ".join([s.name for s in scopes])) + + def config_add(args): """Add the given configuration to the specified config scope @@ -467,6 +487,7 @@ def config(parser, args): "blame": config_blame, "edit": config_edit, "list": config_list, + "list-scopes": config_list_scopes, "add": config_add, "rm": config_remove, "remove": config_remove, diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py index 9c6fb6366e0460..99ff6e11f59984 100644 --- a/lib/spack/spack/config.py +++ b/lib/spack/spack/config.py @@ -446,11 +446,8 @@ def highest_precedence_non_platform_scope(self) -> ConfigScope: """Non-internal non-platform scope with highest precedence Platform-specific scopes are of the form scope/platform""" - generator = reversed(self.file_scopes) - highest = next(generator) - while highest and highest.is_platform_dependent: - highest = next(generator) - return highest + generator = self._non_platform_scopes + return next(generator) def matching_scopes(self, reg_expr) -> List[ConfigScope]: """ @@ -700,6 +697,15 @@ def print_section(self, section, blame=False): except (syaml.SpackYAMLError, IOError) as e: raise ConfigError(f"cannot read '{section}' configuration") from e + @property + def _non_platform_scopes(self): + """Returns non-platform scopes, highest priority first.""" + for s in reversed(self.file_scopes): + if s.is_platform_dependent: + continue + else: + yield s + @contextmanager def override(path_or_scope, value=None): diff --git a/lib/spack/spack/test/cmd/config.py b/lib/spack/spack/test/cmd/config.py index 4f3d5afe770e0c..3a409003acc378 100644 --- a/lib/spack/spack/test/cmd/config.py +++ b/lib/spack/spack/test/cmd/config.py @@ -665,3 +665,20 @@ def update_config(data): with ev.Environment(str(tmpdir)) as e: assert not e.manifest.pristine_yaml_content["spack"]["config"]["ccache"] + +def test_config_list_scopes(): + output = config("list-scopes") + assert "command_line" in output + assert "_builtin" in output + assert any("/" in s for s in output) + +def test_config_list_scopes_file(): + output = config("list-scopes", "--file") + assert "site" in output + assert "_builtin" not in output + assert any("/" in s for s in output) + +def test_config_list_scopes_non_platform(): + output = config("list-scopes", "--non-platform") + assert "site" in output + assert "default" in output