From 02501bc4af92e2485b0362d6b2d08890d2588357 Mon Sep 17 00:00:00 2001 From: Harmen Stoppels Date: Mon, 5 May 2025 10:45:11 +0200 Subject: [PATCH] lang.py: make HashableMap generic, and use in Spec (#50229) --- lib/spack/docs/conf.py | 4 ++++ lib/spack/llnl/util/lang.py | 30 ++++++++++++------------------ lib/spack/spack/solver/asp.py | 2 +- lib/spack/spack/spec.py | 4 ++-- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/spack/docs/conf.py b/lib/spack/docs/conf.py index 277ece75f5b..a695329295d 100644 --- a/lib/spack/docs/conf.py +++ b/lib/spack/docs/conf.py @@ -225,10 +225,14 @@ def setup(sphinx): ("py:class", "llnl.util.lang.T"), ("py:class", "llnl.util.lang.KT"), ("py:class", "llnl.util.lang.VT"), + ("py:class", "llnl.util.lang.K"), + ("py:class", "llnl.util.lang.V"), ("py:class", "llnl.util.lang.ClassPropertyType"), ("py:obj", "llnl.util.lang.KT"), ("py:obj", "llnl.util.lang.VT"), ("py:obj", "llnl.util.lang.ClassPropertyType"), + ("py:obj", "llnl.util.lang.K"), + ("py:obj", "llnl.util.lang.V"), ] # The reST default role (used for this markup: `text`) to use for all documents. diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py index ac44fe74937..584e9de1bc4 100644 --- a/lib/spack/llnl/util/lang.py +++ b/lib/spack/llnl/util/lang.py @@ -21,6 +21,7 @@ Dict, Generic, Iterable, + Iterator, List, Mapping, Optional, @@ -436,46 +437,39 @@ def add_func_to_class(name, func): return cls +K = TypeVar("K") +V = TypeVar("V") + + @lazy_lexicographic_ordering -class HashableMap(collections.abc.MutableMapping): +class HashableMap(typing.MutableMapping[K, V]): """This is a hashable, comparable dictionary. Hash is performed on a tuple of the values in the dictionary.""" __slots__ = ("dict",) def __init__(self): - self.dict = {} + self.dict: Dict[K, V] = {} - def __getitem__(self, key): + def __getitem__(self, key: K) -> V: return self.dict[key] - def __setitem__(self, key, value): + def __setitem__(self, key: K, value: V) -> None: self.dict[key] = value - def __iter__(self): + def __iter__(self) -> Iterator[K]: return iter(self.dict) - def __len__(self): + def __len__(self) -> int: return len(self.dict) - def __delitem__(self, key): + def __delitem__(self, key: K) -> None: del self.dict[key] def _cmp_iter(self): for _, v in sorted(self.items()): yield v - def copy(self): - """Type-agnostic clone method. Preserves subclass type.""" - # Construct a new dict of my type - self_type = type(self) - clone = self_type() - - # Copy everything from this dict into it. - for key in self: - clone[key] = self[key].copy() - return clone - def match_predicate(*args): """Utility function for making string matching predicates. diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index d3db57f3ea4..3a825929307 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -2492,7 +2492,7 @@ def _spec_clauses( # TODO: variant="*" means 'variant is defined to something', which used to # be meaningless in concretization, as all variants had to be defined. But # now that variants can be conditional, it should force a variant to exist. - if variant.value == ("*",): + if not variant.values: continue for value in variant.values: diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index b31f1f1cb51..b68bbea8c76 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -837,7 +837,7 @@ def _shared_subset_pair_iterate(container1, container2): b_idx += 1 -class FlagMap(lang.HashableMap): +class FlagMap(lang.HashableMap[str, List[CompilerFlag]]): __slots__ = ("spec",) def __init__(self, spec): @@ -4490,7 +4490,7 @@ def has_virtual_dependency(self, virtual: str) -> bool: return bool(self.dependencies(virtuals=(virtual,))) -class VariantMap(lang.HashableMap): +class VariantMap(lang.HashableMap[str, vt.VariantValue]): """Map containing variant instances. New values can be added only if the key is not already present."""