未验证 提交 06a3406a 编写于 作者: P Pradyun Gedam 提交者: GitHub

Merge pull request #7861 from uranusjr/resolver-implement

Implement resolver methods on provider interface
import functools
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter
from pip._vendor.resolvelib import Resolver as RLResolver
from pip._internal.resolution.base import BaseResolver
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import List, Optional, Tuple
from typing import Dict, List, Optional, Tuple
from pip._vendor.resolvelib.resolvers import Result
from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
......@@ -10,6 +18,29 @@ if MYPY_CHECK_RUNNING:
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import InstallRequirementProvider
from .base import Candidate, Requirement
# FIXME: Import the actual implementation.
# This is a stub to pass typing checks.
class PipProvider(object):
def __init__(
self,
finder, # type: PackageFinder
preparer, # type: RequirementPreparer
make_install_req, # type: InstallRequirementProvider
):
# type: (...) -> None
super(PipProvider, self).__init__()
def make_requirement(self, r):
# type: (InstallRequirement) -> Requirement
raise NotImplementedError()
def get_install_requirement(self, c):
# type: (Candidate) -> InstallRequirement
raise NotImplementedError()
class Resolver(BaseResolver):
def __init__(
......@@ -26,11 +57,84 @@ class Resolver(BaseResolver):
py_version_info=None, # type: Optional[Tuple[int, ...]]
):
super(Resolver, self).__init__()
self.finder = finder
self.preparer = preparer
self.make_install_req = make_install_req
self._result = None # type: Optional[Result]
def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
raise NotImplementedError()
provider = PipProvider(
self.finder,
self.preparer,
self.make_install_req,
)
reporter = BaseReporter()
resolver = RLResolver(provider, reporter)
requirements = [provider.make_requirement(r) for r in root_reqs]
self._result = resolver.resolve(requirements)
req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
for candidate in self._result.mapping.values():
ireq = provider.get_install_requirement(candidate)
req_set.add_named_requirement(ireq)
return req_set
def get_installation_order(self, req_set):
# type: (RequirementSet) -> List[InstallRequirement]
raise NotImplementedError()
"""Create a list that orders given requirements for installation.
The returned list should contain all requirements in ``req_set``,
so the caller can loop through it and have a requirement installed
before the requiring thing.
The current implementation walks the resolved dependency graph, and
make sure every node has a greater "weight" than all its parents.
"""
assert self._result is not None, "must call resolve() first"
weights = {} # type: Dict[Optional[str], int]
graph = self._result.graph
key_count = len(self._result.mapping) + 1 # Packages plus sentinal.
while len(weights) < key_count:
progressed = False
for key in graph:
if key in weights:
continue
parents = list(graph.iter_parents(key))
if not all(p in weights for p in parents):
continue
if parents:
weight = max(weights[p] for p in parents) + 1
else:
weight = 0
weights[key] = weight
progressed = True
# FIXME: This check will fail if there are unbreakable cycles.
# Implement something to forcifully break them up to continue.
assert progressed, "Order calculation stuck in dependency loop."
sorted_items = sorted(
req_set.requirements.items(),
key=functools.partial(_req_set_item_sorter, weights=weights),
reverse=True,
)
return [ireq for _, ireq in sorted_items]
def _req_set_item_sorter(
item, # type: Tuple[str, InstallRequirement]
weights, # type: Dict[Optional[str], int]
):
# type: (...) -> Tuple[int, str]
"""Key function used to sort install requirements for installation.
Based on the "weight" mapping calculated in ``get_installation_order()``.
The canonical package name is returned as the second member as a tie-
breaker to ensure the result is predictable, which is useful in tests.
"""
name = canonicalize_name(item[0])
return weights[name], name
import mock
import pytest
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib.resolvers import Result
from pip._vendor.resolvelib.structs import DirectedGraph
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.resolvelib.resolver import Resolver
@pytest.fixture()
def resolver(preparer, finder):
resolver = Resolver(
preparer=preparer,
finder=finder,
make_install_req=mock.Mock(),
use_user_site="not-used",
ignore_dependencies="not-used",
ignore_installed="not-used",
ignore_requires_python="not-used",
force_reinstall="not-used",
upgrade_strategy="not-used",
)
return resolver
@pytest.mark.parametrize(
"edges, ordered_reqs",
[
(
[(None, "require-simple"), ("require-simple", "simple")],
["simple==3.0", "require-simple==1.0"],
),
(
[(None, "meta"), ("meta", "simple"), ("meta", "simple2")],
["simple2==3.0", "simple==3.0", "meta==1.0"],
),
(
[
(None, "toporequires"),
(None, "toporequire2"),
(None, "toporequire3"),
(None, "toporequire4"),
("toporequires2", "toporequires"),
("toporequires3", "toporequires"),
("toporequires4", "toporequires"),
("toporequires4", "toporequires2"),
("toporequires4", "toporequires3"),
],
[
"toporequires==0.0.1",
"toporequires3==0.0.1",
"toporequires2==0.0.1",
"toporequires4==0.0.1",
],
),
],
)
def test_rlr_resolver_get_installation_order(resolver, edges, ordered_reqs):
# Build graph from edge declarations.
graph = DirectedGraph()
for parent, child in edges:
parent = canonicalize_name(parent) if parent else None
child = canonicalize_name(child) if child else None
for v in (parent, child):
if v not in graph:
graph.add(v)
graph.connect(parent, child)
# Mapping values and criteria are not used in test, so we stub them out.
mapping = {vertex: None for vertex in graph if vertex is not None}
resolver._result = Result(mapping, graph, criteria=None)
reqset = RequirementSet()
for r in ordered_reqs:
reqset.add_named_requirement(install_req_from_line(r))
ireqs = resolver.get_installation_order(reqset)
req_strs = [str(r.req) for r in ireqs]
assert req_strs == ordered_reqs
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册