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.
This commit is contained in:
parent
9f035ca030
commit
8a9c501030
2 changed files with 30 additions and 18 deletions
|
@ -4164,29 +4164,21 @@ def __getitem__(self, name: str):
|
||||||
csv = query_parameters.pop().strip()
|
csv = query_parameters.pop().strip()
|
||||||
query_parameters = re.split(r"\s*,\s*", csv)
|
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(
|
order = lambda: itertools.chain(
|
||||||
self.traverse(deptype="link"),
|
self.traverse_edges(deptype=dt.LINK, order="breadth", cover="edges"),
|
||||||
self.dependencies(deptype=dt.BUILD | dt.RUN | dt.TEST),
|
self.edges_to_dependencies(depflag=dt.BUILD | dt.RUN | dt.TEST),
|
||||||
self.traverse(), # fall back to a full search
|
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:
|
try:
|
||||||
child: Spec = next(
|
child: Spec = next(
|
||||||
itertools.chain(
|
e.spec
|
||||||
# Regular specs
|
for e in itertools.chain(
|
||||||
(x for x in order() if x.name == name),
|
(e for e in order() if e.spec.name == name or name in e.virtuals),
|
||||||
(
|
# for historical reasons
|
||||||
x
|
(e for e in order() if e.spec.concrete and e.spec.package.provides(name)),
|
||||||
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)),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
|
|
|
@ -1105,3 +1105,23 @@ def test_indexing_prefers_direct_or_transitive_link_deps():
|
||||||
|
|
||||||
# Ensure that the full DAG is still searched
|
# Ensure that the full DAG is still searched
|
||||||
assert root["a2"]
|
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"
|
||||||
|
|
Loading…
Reference in a new issue