spack.concretize: add type-hints, remove kwargs (#47382)

Also remove find_spec, which was used by the old concretizer.
Currently, it seems to be used only in tests.
This commit is contained in:
Massimiliano Culpo 2024-11-05 07:46:49 +01:00 committed by GitHub
parent e42e541605
commit 14bc900e9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 50 additions and 122 deletions

View File

@ -214,6 +214,7 @@ def setup(sphinx):
# Spack classes that intersphinx is unable to resolve # Spack classes that intersphinx is unable to resolve
("py:class", "spack.version.StandardVersion"), ("py:class", "spack.version.StandardVersion"),
("py:class", "spack.spec.DependencySpec"), ("py:class", "spack.spec.DependencySpec"),
("py:class", "spack.spec.ArchSpec"),
("py:class", "spack.spec.InstallStatus"), ("py:class", "spack.spec.InstallStatus"),
("py:class", "spack.spec.SpecfileReaderBase"), ("py:class", "spack.spec.SpecfileReaderBase"),
("py:class", "spack.install_test.Pb"), ("py:class", "spack.install_test.Pb"),

View File

@ -194,7 +194,7 @@ def _concretize_spec_pairs(to_concretize, tests=False):
elif unify == "when_possible": elif unify == "when_possible":
concretize_method = spack.concretize.concretize_together_when_possible concretize_method = spack.concretize.concretize_together_when_possible
concretized = concretize_method(*to_concretize, tests=tests) concretized = concretize_method(to_concretize, tests=tests)
return [concrete for _, concrete in concretized] return [concrete for _, concrete in concretized]

View File

@ -2,20 +2,17 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
""" """High-level functions to concretize list of specs"""
(DEPRECATED) Used to contain the code for the original concretizer
"""
import sys import sys
import time import time
from contextlib import contextmanager from contextlib import contextmanager
from itertools import chain from typing import Iterable, Optional, Sequence, Tuple, Union
from typing import Tuple
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.config import spack.config
import spack.error import spack.error
from spack.spec import Spec from spack.spec import ArchSpec, CompilerSpec, Spec
CHECK_COMPILER_EXISTENCE = True CHECK_COMPILER_EXISTENCE = True
@ -36,91 +33,59 @@ def enable_compiler_existence_check():
CHECK_COMPILER_EXISTENCE = saved CHECK_COMPILER_EXISTENCE = saved
def find_spec(spec, condition, default=None): SpecPair = Tuple[Spec, Spec]
"""Searches the dag from spec in an intelligent order and looks SpecLike = Union[Spec, str]
for a spec that matches a condition""" TestsType = Union[bool, Iterable[str]]
# First search parents, then search children
deptype = ("build", "link")
dagiter = chain(
spec.traverse(direction="parents", deptype=deptype, root=False),
spec.traverse(direction="children", deptype=deptype, root=False),
)
visited = set()
for relative in dagiter:
if condition(relative):
return relative
visited.add(id(relative))
# Then search all other relatives in the DAG *except* spec
for relative in spec.root.traverse(deptype="all"):
if relative is spec:
continue
if id(relative) in visited:
continue
if condition(relative):
return relative
# Finally search spec itself.
if condition(spec):
return spec
return default # Nothing matched the condition; return default.
def concretize_specs_together(*abstract_specs, **kwargs): def concretize_specs_together(
abstract_specs: Sequence[SpecLike], tests: TestsType = False
) -> Sequence[Spec]:
"""Given a number of specs as input, tries to concretize them together. """Given a number of specs as input, tries to concretize them together.
Args: Args:
tests (bool or list or set): False to run no tests, True to test abstract_specs: abstract specs to be concretized
all packages, or a list of package names to run tests for some tests: list of package names for which to consider tests dependencies. If True, all nodes
*abstract_specs: abstract specs to be concretized, given either will have test dependencies. If False, test dependencies will be disregarded.
as Specs or strings
Returns:
List of concretized specs
""" """
import spack.solver.asp import spack.solver.asp
allow_deprecated = spack.config.get("config:deprecated", False) allow_deprecated = spack.config.get("config:deprecated", False)
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
result = solver.solve( result = solver.solve(abstract_specs, tests=tests, allow_deprecated=allow_deprecated)
abstract_specs, tests=kwargs.get("tests", False), allow_deprecated=allow_deprecated
)
return [s.copy() for s in result.specs] return [s.copy() for s in result.specs]
def concretize_together(*spec_list, **kwargs): def concretize_together(
spec_list: Sequence[SpecPair], tests: TestsType = False
) -> Sequence[SpecPair]:
"""Given a number of specs as input, tries to concretize them together. """Given a number of specs as input, tries to concretize them together.
Args: Args:
tests (bool or list or set): False to run no tests, True to test spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
all packages, or a list of package names to run tests for some
*spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
already concrete spec or None if not yet concretized already concrete spec or None if not yet concretized
tests: list of package names for which to consider tests dependencies. If True, all nodes
Returns: will have test dependencies. If False, test dependencies will be disregarded.
List of tuples of abstract and concretized specs
""" """
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list] to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
abstract_specs = [abstract for abstract, _ in spec_list] abstract_specs = [abstract for abstract, _ in spec_list]
concrete_specs = concretize_specs_together(*to_concretize, **kwargs) concrete_specs = concretize_specs_together(to_concretize, tests=tests)
return list(zip(abstract_specs, concrete_specs)) return list(zip(abstract_specs, concrete_specs))
def concretize_together_when_possible(*spec_list, **kwargs): def concretize_together_when_possible(
spec_list: Sequence[SpecPair], tests: TestsType = False
) -> Sequence[SpecPair]:
"""Given a number of specs as input, tries to concretize them together to the extent possible. """Given a number of specs as input, tries to concretize them together to the extent possible.
See documentation for ``unify: when_possible`` concretization for the precise definition of See documentation for ``unify: when_possible`` concretization for the precise definition of
"to the extent possible". "to the extent possible".
Args: Args:
tests (bool or list or set): False to run no tests, True to test spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
all packages, or a list of package names to run tests for some
*spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
already concrete spec or None if not yet concretized already concrete spec or None if not yet concretized
tests: list of package names for which to consider tests dependencies. If True, all nodes
Returns: will have test dependencies. If False, test dependencies will be disregarded.
List of tuples of abstract and concretized specs
""" """
to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list] to_concretize = [concrete if concrete else abstract for abstract, concrete in spec_list]
old_concrete_to_abstract = { old_concrete_to_abstract = {
@ -131,7 +96,7 @@ def concretize_together_when_possible(*spec_list, **kwargs):
solver = spack.solver.asp.Solver() solver = spack.solver.asp.Solver()
allow_deprecated = spack.config.get("config:deprecated", False) allow_deprecated = spack.config.get("config:deprecated", False)
for result in solver.solve_in_rounds( for result in solver.solve_in_rounds(
to_concretize, tests=kwargs.get("tests", False), allow_deprecated=allow_deprecated to_concretize, tests=tests, allow_deprecated=allow_deprecated
): ):
result_by_user_spec.update(result.specs_by_input) result_by_user_spec.update(result.specs_by_input)
@ -143,19 +108,17 @@ def concretize_together_when_possible(*spec_list, **kwargs):
] ]
def concretize_separately(*spec_list, **kwargs): def concretize_separately(
"""Given a number of specs as input, tries to concretize them together. spec_list: Sequence[SpecPair], tests: TestsType = False
) -> Sequence[SpecPair]:
"""Concretizes the input specs separately from each other.
Args: Args:
tests (bool or list or set): False to run no tests, True to test spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
all packages, or a list of package names to run tests for some
*spec_list: list of tuples to concretize. First entry is abstract spec, second entry is
already concrete spec or None if not yet concretized already concrete spec or None if not yet concretized
tests: list of package names for which to consider tests dependencies. If True, all nodes
Returns: will have test dependencies. If False, test dependencies will be disregarded.
List of tuples of abstract and concretized specs
""" """
tests = kwargs.get("tests", False)
to_concretize = [abstract for abstract, concrete in spec_list if not concrete] to_concretize = [abstract for abstract, concrete in spec_list if not concrete]
args = [ args = [
(i, str(abstract), tests) (i, str(abstract), tests)
@ -215,7 +178,7 @@ def concretize_separately(*spec_list, **kwargs):
] ]
def _concretize_task(packed_arguments) -> Tuple[int, Spec, float]: def _concretize_task(packed_arguments: Tuple[int, str, TestsType]) -> Tuple[int, Spec, float]:
index, spec_str, tests = packed_arguments index, spec_str, tests = packed_arguments
with tty.SuppressOutput(msg_enabled=False): with tty.SuppressOutput(msg_enabled=False):
start = time.time() start = time.time()
@ -227,10 +190,10 @@ class UnavailableCompilerVersionError(spack.error.SpackError):
"""Raised when there is no available compiler that satisfies a """Raised when there is no available compiler that satisfies a
compiler spec.""" compiler spec."""
def __init__(self, compiler_spec, arch=None): def __init__(self, compiler_spec: CompilerSpec, arch: Optional[ArchSpec] = None) -> None:
err_msg = "No compilers with spec {0} found".format(compiler_spec) err_msg = f"No compilers with spec {compiler_spec} found"
if arch: if arch:
err_msg += " for operating system {0} and target {1}.".format(arch.os, arch.target) err_msg += f" for operating system {arch.os} and target {arch.target}."
super().__init__( super().__init__(
err_msg, err_msg,

View File

@ -14,7 +14,7 @@
import urllib.parse import urllib.parse
import urllib.request import urllib.request
import warnings import warnings
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
import llnl.util.tty as tty import llnl.util.tty as tty
@ -55,7 +55,7 @@
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
SpecPair = Tuple[spack.spec.Spec, spack.spec.Spec] SpecPair = spack.concretize.SpecPair
#: environment variable used to indicate the active environment #: environment variable used to indicate the active environment
spack_env_var = "SPACK_ENV" spack_env_var = "SPACK_ENV"
@ -1533,9 +1533,7 @@ def _get_specs_to_concretize(
] ]
return new_user_specs, kept_user_specs, specs_to_concretize return new_user_specs, kept_user_specs, specs_to_concretize
def _concretize_together_where_possible( def _concretize_together_where_possible(self, tests: bool = False) -> Sequence[SpecPair]:
self, tests: bool = False
) -> List[Tuple[spack.spec.Spec, spack.spec.Spec]]:
# Avoid cyclic dependency # Avoid cyclic dependency
import spack.solver.asp import spack.solver.asp
@ -1550,7 +1548,7 @@ def _concretize_together_where_possible(
ret = [] ret = []
result = spack.concretize.concretize_together_when_possible( result = spack.concretize.concretize_together_when_possible(
*specs_to_concretize, tests=tests specs_to_concretize, tests=tests
) )
for abstract, concrete in result: for abstract, concrete in result:
# Only add to the environment if it's from this environment (not included in) # Only add to the environment if it's from this environment (not included in)
@ -1563,7 +1561,7 @@ def _concretize_together_where_possible(
return ret return ret
def _concretize_together(self, tests: bool = False) -> List[SpecPair]: def _concretize_together(self, tests: bool = False) -> Sequence[SpecPair]:
"""Concretization strategy that concretizes all the specs """Concretization strategy that concretizes all the specs
in the same DAG. in the same DAG.
""" """
@ -1577,8 +1575,8 @@ def _concretize_together(self, tests: bool = False) -> List[SpecPair]:
self.specs_by_hash = {} self.specs_by_hash = {}
try: try:
concretized_specs: List[SpecPair] = spack.concretize.concretize_together( concretized_specs = spack.concretize.concretize_together(
*specs_to_concretize, tests=tests specs_to_concretize, tests=tests
) )
except spack.error.UnsatisfiableSpecError as e: except spack.error.UnsatisfiableSpecError as e:
# "Enhance" the error message for multiple root specs, suggest a less strict # "Enhance" the error message for multiple root specs, suggest a less strict
@ -1627,7 +1625,7 @@ def _concretize_separately(self, tests=False):
to_concretize = [ to_concretize = [
(root, None) for root in self.user_specs if root not in old_concretized_user_specs (root, None) for root in self.user_specs if root not in old_concretized_user_specs
] ]
concretized_specs = spack.concretize.concretize_separately(*to_concretize, tests=tests) concretized_specs = spack.concretize.concretize_separately(to_concretize, tests=tests)
by_hash = {} by_hash = {}
for abstract, concrete in concretized_specs: for abstract, concrete in concretized_specs:

View File

@ -33,7 +33,6 @@
import spack.store import spack.store
import spack.util.file_cache import spack.util.file_cache
import spack.variant as vt import spack.variant as vt
from spack.concretize import find_spec
from spack.installer import PackageInstaller from spack.installer import PackageInstaller
from spack.spec import CompilerSpec, Spec from spack.spec import CompilerSpec, Spec
from spack.version import Version, VersionList, ver from spack.version import Version, VersionList, ver
@ -674,39 +673,6 @@ def test_external_and_virtual(self, mutable_config):
assert spec["externaltool"].compiler.satisfies("gcc") assert spec["externaltool"].compiler.satisfies("gcc")
assert spec["stuff"].compiler.satisfies("gcc") assert spec["stuff"].compiler.satisfies("gcc")
def test_find_spec_parents(self):
"""Tests the spec finding logic used by concretization."""
s = Spec.from_literal({"a +foo": {"b +foo": {"c": None, "d+foo": None}, "e +foo": None}})
assert "a" == find_spec(s["b"], lambda s: "+foo" in s).name
def test_find_spec_children(self):
s = Spec.from_literal({"a": {"b +foo": {"c": None, "d+foo": None}, "e +foo": None}})
assert "d" == find_spec(s["b"], lambda s: "+foo" in s).name
s = Spec.from_literal({"a": {"b +foo": {"c+foo": None, "d": None}, "e +foo": None}})
assert "c" == find_spec(s["b"], lambda s: "+foo" in s).name
def test_find_spec_sibling(self):
s = Spec.from_literal({"a": {"b +foo": {"c": None, "d": None}, "e +foo": None}})
assert "e" == find_spec(s["b"], lambda s: "+foo" in s).name
assert "b" == find_spec(s["e"], lambda s: "+foo" in s).name
s = Spec.from_literal({"a": {"b +foo": {"c": None, "d": None}, "e": {"f +foo": None}}})
assert "f" == find_spec(s["b"], lambda s: "+foo" in s).name
def test_find_spec_self(self):
s = Spec.from_literal({"a": {"b +foo": {"c": None, "d": None}, "e": None}})
assert "b" == find_spec(s["b"], lambda s: "+foo" in s).name
def test_find_spec_none(self):
s = Spec.from_literal({"a": {"b": {"c": None, "d": None}, "e": None}})
assert find_spec(s["b"], lambda s: "+foo" in s) is None
def test_compiler_child(self): def test_compiler_child(self):
s = Spec("mpileaks%clang target=x86_64 ^dyninst%gcc") s = Spec("mpileaks%clang target=x86_64 ^dyninst%gcc")
s.concretize() s.concretize()
@ -815,7 +781,7 @@ def test_regression_issue_7941(self):
) )
def test_simultaneous_concretization_of_specs(self, abstract_specs): def test_simultaneous_concretization_of_specs(self, abstract_specs):
abstract_specs = [Spec(x) for x in abstract_specs] abstract_specs = [Spec(x) for x in abstract_specs]
concrete_specs = spack.concretize.concretize_specs_together(*abstract_specs) concrete_specs = spack.concretize.concretize_specs_together(abstract_specs)
# Check there's only one configuration of each package in the DAG # Check there's only one configuration of each package in the DAG
names = set(dep.name for spec in concrete_specs for dep in spec.traverse()) names = set(dep.name for spec in concrete_specs for dep in spec.traverse())
@ -2137,7 +2103,7 @@ def test_external_python_extension_find_unified_python(self):
spack.config.set("packages", external_conf) spack.config.set("packages", external_conf)
abstract_specs = [Spec(s) for s in ["py-extension1", "python"]] abstract_specs = [Spec(s) for s in ["py-extension1", "python"]]
specs = spack.concretize.concretize_specs_together(*abstract_specs) specs = spack.concretize.concretize_specs_together(abstract_specs)
assert specs[0]["python"] == specs[1]["python"] assert specs[0]["python"] == specs[1]["python"]
@pytest.mark.regression("36190") @pytest.mark.regression("36190")