diff options
author | Harmen Stoppels <me@harmenstoppels.nl> | 2024-07-08 14:53:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-08 14:53:51 +0200 |
commit | 297874bfed27caedf32d93bba550206729ba353e (patch) | |
tree | 450e58aac00f460a1e71216284f9da2eb59bb4e4 /lib | |
parent | 74398d74ace4b09ec9aabc9ce243b98ea4d7fada (diff) | |
download | spack-297874bfed27caedf32d93bba550206729ba353e.tar.gz spack-297874bfed27caedf32d93bba550206729ba353e.tar.bz2 spack-297874bfed27caedf32d93bba550206729ba353e.tar.xz spack-297874bfed27caedf32d93bba550206729ba353e.zip |
spec.py: fix __getitem__ looking outside of dag (#45090)
`Spec.__getitem__` queries dependent edges, which almost always point to
nodes outside the sub-dag considered. It should only ever look at edges
being traversed.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/spec.py | 28 | ||||
-rw-r--r-- | lib/spack/spack/test/spec_dag.py | 20 |
2 files changed, 30 insertions, 18 deletions
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 5bdb00173d..0e00e03e98 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -4258,29 +4258,21 @@ class Spec: csv = query_parameters.pop().strip() query_parameters = re.split(r"\s*,\s*", csv) - # In some cases a package appears multiple times in the same DAG for *distinct* - # specs. For example, a build-type dependency may itself depend on a package - # the current spec depends on, but their specs may differ. Therefore we iterate - # in an order here that prioritizes the build, test and runtime dependencies; - # only when we don't find the package do we consider the full DAG. order = lambda: itertools.chain( - self.traverse(deptype="link"), - self.dependencies(deptype=dt.BUILD | dt.RUN | dt.TEST), - self.traverse(), # fall back to a full search + self.traverse_edges(deptype=dt.LINK, order="breadth", cover="edges"), + self.edges_to_dependencies(depflag=dt.BUILD | dt.RUN | dt.TEST), + self.traverse_edges(deptype=dt.ALL, order="breadth", cover="edges"), ) + # Consider runtime dependencies and direct build/test deps before transitive dependencies, + # and prefer matches closest to the root. try: child: Spec = next( - itertools.chain( - # Regular specs - (x for x in order() if x.name == name), - ( - x - for x in order() - if (not x.virtual) - and any(name in edge.virtuals for edge in x.edges_from_dependents()) - ), - (x for x in order() if (not x.virtual) and x.package.provides(name)), + e.spec + for e in itertools.chain( + (e for e in order() if e.spec.name == name or name in e.virtuals), + # for historical reasons + (e for e in order() if e.spec.concrete and e.spec.package.provides(name)), ) ) except StopIteration: diff --git a/lib/spack/spack/test/spec_dag.py b/lib/spack/spack/test/spec_dag.py index da85be6d1c..39e9ff66a1 100644 --- a/lib/spack/spack/test/spec_dag.py +++ b/lib/spack/spack/test/spec_dag.py @@ -1105,3 +1105,23 @@ def test_indexing_prefers_direct_or_transitive_link_deps(): # Ensure that the full DAG is still searched assert root["a2"] + + +def test_getitem_sticks_to_subdag(): + """Test that indexing on Spec by virtual does not traverse outside the dag, which happens in + the unlikely case someone would rewrite __getitem__ in terms of edges_from_dependents instead + of edges_to_dependencies.""" + x, y, z = Spec("x"), Spec("y"), Spec("z") + x.add_dependency_edge(z, depflag=dt.LINK, virtuals=("virtual",)) + y.add_dependency_edge(z, depflag=dt.LINK, virtuals=()) + assert x["virtual"].name == "z" + with pytest.raises(KeyError): + y["virtual"] + + +def test_getitem_finds_transitive_virtual(): + x, y, z = Spec("x"), Spec("y"), Spec("z") + x.add_dependency_edge(z, depflag=dt.LINK, virtuals=()) + x.add_dependency_edge(y, depflag=dt.LINK, virtuals=()) + y.add_dependency_edge(z, depflag=dt.LINK, virtuals=("virtual",)) + assert x["virtual"].name == "z" |