1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Fix assertion error when determining installation order.

Fixes https://github.com/pypa/pip/issues/10851
This commit is contained in:
Maurits van Rees 2022-01-31 20:05:54 +01:00
parent 193259d3dc
commit 63b19afac2
No known key found for this signature in database
GPG key ID: AAEC1E5318E6FCCE
3 changed files with 51 additions and 20 deletions

1
news/10851.bugfix.rst Normal file
View file

@ -0,0 +1 @@
Fixed assertion error when determining installation order.

View file

@ -186,8 +186,7 @@ class Resolver(BaseResolver):
graph = self._result.graph
weights = get_topological_weights(
graph,
expected_node_count=len(self._result.mapping) + 1,
graph, requirement_keys=set(req_set.requirements.keys())
)
sorted_items = sorted(
@ -199,7 +198,7 @@ class Resolver(BaseResolver):
def get_topological_weights(
graph: "DirectedGraph[Optional[str]]", expected_node_count: int
graph: "DirectedGraph[Optional[str]]", requirement_keys: Set[str]
) -> Dict[Optional[str], int]:
"""Assign weights to each node based on how "deep" they are.
@ -222,6 +221,9 @@ def get_topological_weights(
don't get stuck in a cycle.
When assigning weight, the longer path (i.e. larger length) is preferred.
We are only interested in the weights of packages that are in the
requirement_keys.
"""
path: Set[Optional[str]] = set()
weights: Dict[Optional[str], int] = {}
@ -237,6 +239,9 @@ def get_topological_weights(
visit(child)
path.remove(node)
if node not in requirement_keys:
return
last_known_parent_count = weights.get(node, 0)
weights[node] = max(last_known_parent_count, len(path))
@ -262,6 +267,8 @@ def get_topological_weights(
# Calculate the weight for the leaves.
weight = len(graph) - 1
for leaf in leaves:
if leaf not in requirement_keys:
continue
weights[leaf] = weight
# Remove the leaves from the graph, making it simpler.
for leaf in leaves:
@ -271,9 +278,8 @@ def get_topological_weights(
# `None` is guaranteed to be the root node by resolvelib.
visit(None)
# Sanity checks
assert weights[None] == 0
assert len(weights) == expected_node_count
# Sanity check
assert len(weights) == len(requirement_keys)
return weights

View file

@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Tuple, cast
from typing import Dict, List, Optional, Set, Tuple, cast
from unittest import mock
import pytest
@ -103,7 +103,7 @@ def test_new_resolver_get_installation_order(
@pytest.mark.parametrize(
"name, edges, expected_weights",
"name, edges, requirement_keys, expected_weights",
[
(
# From https://github.com/pypa/pip/pull/8127#discussion_r414564664
@ -116,7 +116,8 @@ def test_new_resolver_get_installation_order(
("three", "four"),
("four", "five"),
],
{None: 0, "five": 5, "four": 4, "one": 4, "three": 2, "two": 1},
{"one", "two", "three", "four", "five"},
{"five": 5, "four": 4, "one": 4, "three": 2, "two": 1},
),
(
"linear",
@ -127,7 +128,20 @@ def test_new_resolver_get_installation_order(
("three", "four"),
("four", "five"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND restricted",
[
(None, "one"),
("one", "two"),
("two", "three"),
("three", "four"),
("four", "five"),
],
{"one", "three", "five"},
{"one": 1, "three": 3, "five": 5},
),
(
"linear AND root -> two",
@ -139,7 +153,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
(None, "two"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND root -> three",
@ -151,7 +166,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
(None, "three"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND root -> four",
@ -163,7 +179,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
(None, "four"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND root -> five",
@ -175,7 +192,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
(None, "five"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND one -> four",
@ -187,7 +205,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
("one", "four"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND two -> four",
@ -199,7 +218,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
("two", "four"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND four -> one (cycle)",
@ -211,7 +231,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
("four", "one"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND four -> two (cycle)",
@ -223,7 +244,8 @@ def test_new_resolver_get_installation_order(
("four", "five"),
("four", "two"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
(
"linear AND four -> three (cycle)",
@ -235,16 +257,18 @@ def test_new_resolver_get_installation_order(
("four", "five"),
("four", "three"),
],
{None: 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
{"one", "two", "three", "four", "five"},
{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5},
),
],
)
def test_new_resolver_topological_weights(
name: str,
edges: List[Tuple[Optional[str], Optional[str]]],
requirement_keys: Set[str],
expected_weights: Dict[Optional[str], int],
) -> None:
graph = _make_graph(edges)
weights = get_topological_weights(graph, len(expected_weights))
weights = get_topological_weights(graph, requirement_keys)
assert weights == expected_weights