mirror of https://github.com/pypa/pip
Merge pull request #7907 from uranusjr/resolver-refactor-factory
Initial refactoring to decouple candidate and requirement modules
This commit is contained in:
commit
904d1c8c0a
|
@ -9,7 +9,7 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
|||
from .base import Candidate, format_name
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any, Dict, Optional, Sequence, Set
|
||||
from typing import Any, Optional, Sequence, Set
|
||||
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
|
@ -18,33 +18,10 @@ if MYPY_CHECK_RUNNING:
|
|||
from pip._vendor.packaging.version import _BaseVersion
|
||||
from pip._vendor.pkg_resources import Distribution
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_CANDIDATE_CACHE = {} # type: Dict[Link, LinkCandidate]
|
||||
|
||||
|
||||
def make_candidate(
|
||||
link, # type: Link
|
||||
preparer, # type: RequirementPreparer
|
||||
parent, # type: InstallRequirement
|
||||
make_install_req, # type: InstallRequirementProvider
|
||||
extras # type: Set[str]
|
||||
):
|
||||
# type: (...) -> Candidate
|
||||
if link not in _CANDIDATE_CACHE:
|
||||
_CANDIDATE_CACHE[link] = LinkCandidate(
|
||||
link,
|
||||
preparer,
|
||||
parent=parent,
|
||||
make_install_req=make_install_req
|
||||
)
|
||||
base = _CANDIDATE_CACHE[link]
|
||||
if extras:
|
||||
return ExtrasCandidate(base, extras)
|
||||
return base
|
||||
|
||||
|
||||
def make_install_req_from_link(link, parent):
|
||||
# type: (Link, InstallRequirement) -> InstallRequirement
|
||||
# TODO: Do we need to support editables?
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
from .candidates import ExtrasCandidate, LinkCandidate
|
||||
from .requirements import ExplicitRequirement, SpecifierRequirement
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Dict, Set
|
||||
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.resolution.base import InstallRequirementProvider
|
||||
|
||||
from .base import Candidate, Requirement
|
||||
|
||||
|
||||
class Factory(object):
|
||||
def __init__(
|
||||
self,
|
||||
finder, # type: PackageFinder
|
||||
preparer, # type: RequirementPreparer
|
||||
make_install_req, # type: InstallRequirementProvider
|
||||
):
|
||||
# type: (...) -> None
|
||||
self._finder = finder
|
||||
self._preparer = preparer
|
||||
self._make_install_req = make_install_req
|
||||
self._candidate_cache = {} # type: Dict[Link, LinkCandidate]
|
||||
|
||||
def make_candidate(
|
||||
self,
|
||||
link, # type: Link
|
||||
extras, # type: Set[str]
|
||||
parent, # type: InstallRequirement
|
||||
):
|
||||
# type: (...) -> Candidate
|
||||
if link not in self._candidate_cache:
|
||||
self._candidate_cache[link] = LinkCandidate(
|
||||
link,
|
||||
self._preparer,
|
||||
parent=parent,
|
||||
make_install_req=self._make_install_req
|
||||
)
|
||||
base = self._candidate_cache[link]
|
||||
if extras:
|
||||
return ExtrasCandidate(base, extras)
|
||||
return base
|
||||
|
||||
def make_requirement(self, ireq):
|
||||
# type: (InstallRequirement) -> Requirement
|
||||
if ireq.link:
|
||||
cand = self.make_candidate(ireq.link, extras=set(), parent=ireq)
|
||||
return ExplicitRequirement(cand)
|
||||
else:
|
||||
return SpecifierRequirement(
|
||||
ireq,
|
||||
finder=self._finder,
|
||||
factory=self,
|
||||
make_install_req=self._make_install_req,
|
||||
)
|
|
@ -2,41 +2,24 @@ from pip._vendor.resolvelib.providers import AbstractProvider
|
|||
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
from .requirements import make_requirement
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any, Optional, Sequence, Tuple, Union
|
||||
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.resolution.base import InstallRequirementProvider
|
||||
|
||||
from .base import Requirement, Candidate
|
||||
from .factory import Factory
|
||||
|
||||
|
||||
class PipProvider(AbstractProvider):
|
||||
def __init__(
|
||||
self,
|
||||
finder, # type: PackageFinder
|
||||
preparer, # type: RequirementPreparer
|
||||
factory, # type: Factory
|
||||
ignore_dependencies, # type: bool
|
||||
make_install_req # type: InstallRequirementProvider
|
||||
):
|
||||
# type: (...) -> None
|
||||
self._finder = finder
|
||||
self._preparer = preparer
|
||||
self._factory = factory
|
||||
self._ignore_dependencies = ignore_dependencies
|
||||
self._make_install_req = make_install_req
|
||||
|
||||
def make_requirement(self, ireq):
|
||||
# type: (InstallRequirement) -> Requirement
|
||||
return make_requirement(
|
||||
ireq,
|
||||
self._finder,
|
||||
self._preparer,
|
||||
self._make_install_req
|
||||
)
|
||||
|
||||
def get_install_requirement(self, c):
|
||||
# type: (Candidate) -> Optional[InstallRequirement]
|
||||
|
@ -69,11 +52,6 @@ class PipProvider(AbstractProvider):
|
|||
if self._ignore_dependencies:
|
||||
return []
|
||||
return [
|
||||
make_requirement(
|
||||
r,
|
||||
self._finder,
|
||||
self._preparer,
|
||||
self._make_install_req
|
||||
)
|
||||
self._factory.make_requirement(r)
|
||||
for r in candidate.get_dependencies()
|
||||
]
|
||||
|
|
|
@ -3,42 +3,16 @@ from pip._vendor.packaging.utils import canonicalize_name
|
|||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
from .base import Requirement, format_name
|
||||
from .candidates import make_candidate
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Sequence
|
||||
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.resolution.base import InstallRequirementProvider
|
||||
|
||||
from .base import Candidate
|
||||
|
||||
|
||||
def make_requirement(
|
||||
ireq, # type: InstallRequirement
|
||||
finder, # type: PackageFinder
|
||||
preparer, # type: RequirementPreparer
|
||||
make_install_req # type: InstallRequirementProvider
|
||||
):
|
||||
# type: (...) -> Requirement
|
||||
if ireq.link:
|
||||
candidate = make_candidate(
|
||||
ireq.link,
|
||||
preparer,
|
||||
ireq,
|
||||
make_install_req,
|
||||
set()
|
||||
)
|
||||
return ExplicitRequirement(candidate)
|
||||
else:
|
||||
return SpecifierRequirement(
|
||||
ireq,
|
||||
finder,
|
||||
preparer,
|
||||
make_install_req
|
||||
)
|
||||
from .factory import Factory
|
||||
|
||||
|
||||
class ExplicitRequirement(Requirement):
|
||||
|
@ -66,14 +40,14 @@ class SpecifierRequirement(Requirement):
|
|||
self,
|
||||
ireq, # type: InstallRequirement
|
||||
finder, # type: PackageFinder
|
||||
preparer, # type:RequirementPreparer
|
||||
factory, # type: Factory
|
||||
make_install_req # type: InstallRequirementProvider
|
||||
):
|
||||
# type: (...) -> None
|
||||
assert ireq.link is None, "This is a link, not a specifier"
|
||||
self._ireq = ireq
|
||||
self._factory = factory
|
||||
self._finder = finder
|
||||
self._preparer = preparer
|
||||
self._make_install_req = make_install_req
|
||||
self.extras = ireq.req.extras
|
||||
|
||||
|
@ -91,12 +65,10 @@ class SpecifierRequirement(Requirement):
|
|||
hashes=self._ireq.hashes(trust_internet=False),
|
||||
)
|
||||
return [
|
||||
make_candidate(
|
||||
ican.link,
|
||||
self._preparer,
|
||||
self._ireq,
|
||||
self._make_install_req,
|
||||
self.extras
|
||||
self._factory.make_candidate(
|
||||
link=ican.link,
|
||||
extras=self.extras,
|
||||
parent=self._ireq,
|
||||
)
|
||||
for ican in found.iter_applicable()
|
||||
]
|
||||
|
|
|
@ -9,6 +9,8 @@ from pip._internal.resolution.base import BaseResolver
|
|||
from pip._internal.resolution.resolvelib.provider import PipProvider
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
from .factory import Factory
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
|
@ -35,24 +37,24 @@ class Resolver(BaseResolver):
|
|||
py_version_info=None, # type: Optional[Tuple[int, ...]]
|
||||
):
|
||||
super(Resolver, self).__init__()
|
||||
self.finder = finder
|
||||
self.preparer = preparer
|
||||
self.factory = Factory(
|
||||
finder=finder,
|
||||
preparer=preparer,
|
||||
make_install_req=make_install_req,
|
||||
)
|
||||
self.ignore_dependencies = ignore_dependencies
|
||||
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
|
||||
provider = PipProvider(
|
||||
finder=self.finder,
|
||||
preparer=self.preparer,
|
||||
factory=self.factory,
|
||||
ignore_dependencies=self.ignore_dependencies,
|
||||
make_install_req=self.make_install_req,
|
||||
)
|
||||
reporter = BaseReporter()
|
||||
resolver = RLResolver(provider, reporter)
|
||||
|
||||
requirements = [provider.make_requirement(r) for r in root_reqs]
|
||||
requirements = [self.factory.make_requirement(r) for r in root_reqs]
|
||||
self._result = resolver.resolve(requirements)
|
||||
|
||||
req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
|
||||
|
|
|
@ -12,6 +12,7 @@ from pip._internal.models.selection_prefs import SelectionPreferences
|
|||
from pip._internal.network.session import PipSession
|
||||
from pip._internal.req.constructors import install_req_from_req_string
|
||||
from pip._internal.req.req_tracker import get_requirement_tracker
|
||||
from pip._internal.resolution.resolvelib.factory import Factory
|
||||
from pip._internal.resolution.resolvelib.provider import PipProvider
|
||||
from pip._internal.utils.temp_dir import TempDirectory, global_tempdir_manager
|
||||
|
||||
|
@ -48,16 +49,23 @@ def preparer(finder):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def provider(finder, preparer):
|
||||
def factory(finder, preparer):
|
||||
make_install_req = partial(
|
||||
install_req_from_req_string,
|
||||
isolated=False,
|
||||
wheel_cache=None,
|
||||
use_pep517=None,
|
||||
)
|
||||
yield PipProvider(
|
||||
yield Factory(
|
||||
finder=finder,
|
||||
preparer=preparer,
|
||||
ignore_dependencies=False,
|
||||
make_install_req=make_install_req,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def provider(factory):
|
||||
yield PipProvider(
|
||||
factory=factory,
|
||||
ignore_dependencies=False,
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ from pip._vendor.resolvelib import BaseReporter, Resolver
|
|||
|
||||
from pip._internal.req.constructors import install_req_from_line
|
||||
from pip._internal.resolution.resolvelib.base import Candidate
|
||||
from pip._internal.resolution.resolvelib.requirements import make_requirement
|
||||
from pip._internal.utils.urls import path_to_url
|
||||
|
||||
# NOTE: All tests are prefixed `test_rlr` (for "test resolvelib resolver").
|
||||
|
@ -49,40 +48,36 @@ def test_cases(data):
|
|||
yield test_cases
|
||||
|
||||
|
||||
def req_from_line(line):
|
||||
return make_requirement(install_req_from_line(line))
|
||||
|
||||
|
||||
def test_rlr_requirement_has_name(test_cases, provider):
|
||||
def test_rlr_requirement_has_name(test_cases, factory):
|
||||
"""All requirements should have a name"""
|
||||
for requirement, name, matches in test_cases:
|
||||
ireq = install_req_from_line(requirement)
|
||||
req = provider.make_requirement(ireq)
|
||||
req = factory.make_requirement(ireq)
|
||||
assert req.name == name
|
||||
|
||||
|
||||
def test_rlr_correct_number_of_matches(test_cases, provider):
|
||||
def test_rlr_correct_number_of_matches(test_cases, factory):
|
||||
"""Requirements should return the correct number of candidates"""
|
||||
for requirement, name, matches in test_cases:
|
||||
ireq = install_req_from_line(requirement)
|
||||
req = provider.make_requirement(ireq)
|
||||
req = factory.make_requirement(ireq)
|
||||
assert len(req.find_matches()) == matches
|
||||
|
||||
|
||||
def test_rlr_candidates_match_requirement(test_cases, provider):
|
||||
def test_rlr_candidates_match_requirement(test_cases, factory):
|
||||
"""Candidates returned from find_matches should satisfy the requirement"""
|
||||
for requirement, name, matches in test_cases:
|
||||
ireq = install_req_from_line(requirement)
|
||||
req = provider.make_requirement(ireq)
|
||||
req = factory.make_requirement(ireq)
|
||||
for c in req.find_matches():
|
||||
assert isinstance(c, Candidate)
|
||||
assert req.is_satisfied_by(c)
|
||||
|
||||
|
||||
def test_rlr_full_resolve(provider):
|
||||
def test_rlr_full_resolve(factory, provider):
|
||||
"""A very basic full resolve"""
|
||||
ireq = install_req_from_line("simplewheel")
|
||||
req = provider.make_requirement(ireq)
|
||||
req = factory.make_requirement(ireq)
|
||||
r = Resolver(provider, BaseReporter())
|
||||
result = r.resolve([req])
|
||||
assert set(result.mapping.keys()) == {'simplewheel'}
|
||||
|
|
Loading…
Reference in New Issue