mirror of https://github.com/pypa/pip
Decouple candidate and requirement modules
This introduces a new module "factory" that contains all methods dealing with producing candidates/requirements from an input requirement/candidate. This allows both models to know nothing about each other, and simply rely on the intermediate to produce the other. I *believe* this also helps us reduce merge conflicts due to adding arguments to those producer functions, since now we only need to modify the factory, and exactly one of candidate/requirement. This is only part of a big scheme--the plan is to also move Candidate.get_dependencies() and Requirement.find_matches() into the factory class, so they can avoid holding and passing around finder, preparer, and make_install_req. This is also necessary to change the return type of Candidate.get_dependencies() to Requirement without hard-coupling.
This commit is contained in:
parent
c3d86200b0
commit
07563847b0
|
@ -9,7 +9,7 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||||
from .base import Candidate, format_name
|
from .base import Candidate, format_name
|
||||||
|
|
||||||
if MYPY_CHECK_RUNNING:
|
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.models.link import Link
|
||||||
from pip._internal.operations.prepare import RequirementPreparer
|
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.packaging.version import _BaseVersion
|
||||||
from pip._vendor.pkg_resources import Distribution
|
from pip._vendor.pkg_resources import Distribution
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def make_install_req_from_link(link, parent):
|
||||||
# type: (Link, InstallRequirement) -> InstallRequirement
|
# type: (Link, InstallRequirement) -> InstallRequirement
|
||||||
# TODO: Do we need to support editables?
|
# TODO: Do we need to support editables?
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
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,
|
||||||
|
preparer=self._preparer,
|
||||||
|
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 pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||||
|
|
||||||
from .requirements import make_requirement
|
|
||||||
|
|
||||||
if MYPY_CHECK_RUNNING:
|
if MYPY_CHECK_RUNNING:
|
||||||
from typing import Any, Optional, Sequence, Tuple, Union
|
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.req.req_install import InstallRequirement
|
||||||
from pip._internal.resolution.base import InstallRequirementProvider
|
|
||||||
|
|
||||||
from .base import Requirement, Candidate
|
from .base import Requirement, Candidate
|
||||||
|
from .factory import Factory
|
||||||
|
|
||||||
|
|
||||||
class PipProvider(AbstractProvider):
|
class PipProvider(AbstractProvider):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
finder, # type: PackageFinder
|
factory, # type: Factory
|
||||||
preparer, # type: RequirementPreparer
|
|
||||||
ignore_dependencies, # type: bool
|
ignore_dependencies, # type: bool
|
||||||
make_install_req # type: InstallRequirementProvider
|
|
||||||
):
|
):
|
||||||
# type: (...) -> None
|
# type: (...) -> None
|
||||||
self._finder = finder
|
self._factory = factory
|
||||||
self._preparer = preparer
|
|
||||||
self._ignore_dependencies = ignore_dependencies
|
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):
|
def get_install_requirement(self, c):
|
||||||
# type: (Candidate) -> Optional[InstallRequirement]
|
# type: (Candidate) -> Optional[InstallRequirement]
|
||||||
|
@ -69,11 +52,6 @@ class PipProvider(AbstractProvider):
|
||||||
if self._ignore_dependencies:
|
if self._ignore_dependencies:
|
||||||
return []
|
return []
|
||||||
return [
|
return [
|
||||||
make_requirement(
|
self._factory.make_requirement(r)
|
||||||
r,
|
|
||||||
self._finder,
|
|
||||||
self._preparer,
|
|
||||||
self._make_install_req
|
|
||||||
)
|
|
||||||
for r in candidate.get_dependencies()
|
for r in candidate.get_dependencies()
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,7 +3,6 @@ from pip._vendor.packaging.utils import canonicalize_name
|
||||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||||
|
|
||||||
from .base import Requirement, format_name
|
from .base import Requirement, format_name
|
||||||
from .candidates import make_candidate
|
|
||||||
|
|
||||||
if MYPY_CHECK_RUNNING:
|
if MYPY_CHECK_RUNNING:
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
@ -14,31 +13,7 @@ if MYPY_CHECK_RUNNING:
|
||||||
from pip._internal.resolution.base import InstallRequirementProvider
|
from pip._internal.resolution.base import InstallRequirementProvider
|
||||||
|
|
||||||
from .base import Candidate
|
from .base import Candidate
|
||||||
|
from .factory import Factory
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ExplicitRequirement(Requirement):
|
class ExplicitRequirement(Requirement):
|
||||||
|
@ -66,12 +41,14 @@ class SpecifierRequirement(Requirement):
|
||||||
self,
|
self,
|
||||||
ireq, # type: InstallRequirement
|
ireq, # type: InstallRequirement
|
||||||
finder, # type: PackageFinder
|
finder, # type: PackageFinder
|
||||||
preparer, # type:RequirementPreparer
|
preparer, # type: RequirementPreparer
|
||||||
|
factory, # type: Factory
|
||||||
make_install_req # type: InstallRequirementProvider
|
make_install_req # type: InstallRequirementProvider
|
||||||
):
|
):
|
||||||
# type: (...) -> None
|
# type: (...) -> None
|
||||||
assert ireq.link is None, "This is a link, not a specifier"
|
assert ireq.link is None, "This is a link, not a specifier"
|
||||||
self._ireq = ireq
|
self._ireq = ireq
|
||||||
|
self._factory = factory
|
||||||
self._finder = finder
|
self._finder = finder
|
||||||
self._preparer = preparer
|
self._preparer = preparer
|
||||||
self._make_install_req = make_install_req
|
self._make_install_req = make_install_req
|
||||||
|
@ -91,12 +68,10 @@ class SpecifierRequirement(Requirement):
|
||||||
hashes=self._ireq.hashes(trust_internet=False),
|
hashes=self._ireq.hashes(trust_internet=False),
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
make_candidate(
|
self._factory.make_candidate(
|
||||||
ican.link,
|
link=ican.link,
|
||||||
self._preparer,
|
extras=self.extras,
|
||||||
self._ireq,
|
parent=self._ireq,
|
||||||
self._make_install_req,
|
|
||||||
self.extras
|
|
||||||
)
|
)
|
||||||
for ican in found.iter_applicable()
|
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.resolution.resolvelib.provider import PipProvider
|
||||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||||
|
|
||||||
|
from .factory import Factory
|
||||||
|
|
||||||
if MYPY_CHECK_RUNNING:
|
if MYPY_CHECK_RUNNING:
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
@ -35,24 +37,24 @@ class Resolver(BaseResolver):
|
||||||
py_version_info=None, # type: Optional[Tuple[int, ...]]
|
py_version_info=None, # type: Optional[Tuple[int, ...]]
|
||||||
):
|
):
|
||||||
super(Resolver, self).__init__()
|
super(Resolver, self).__init__()
|
||||||
self.finder = finder
|
self.factory = Factory(
|
||||||
self.preparer = preparer
|
finder=finder,
|
||||||
|
preparer=preparer,
|
||||||
|
make_install_req=make_install_req,
|
||||||
|
)
|
||||||
self.ignore_dependencies = ignore_dependencies
|
self.ignore_dependencies = ignore_dependencies
|
||||||
self.make_install_req = make_install_req
|
|
||||||
self._result = None # type: Optional[Result]
|
self._result = None # type: Optional[Result]
|
||||||
|
|
||||||
def resolve(self, root_reqs, check_supported_wheels):
|
def resolve(self, root_reqs, check_supported_wheels):
|
||||||
# type: (List[InstallRequirement], bool) -> RequirementSet
|
# type: (List[InstallRequirement], bool) -> RequirementSet
|
||||||
provider = PipProvider(
|
provider = PipProvider(
|
||||||
finder=self.finder,
|
factory=self.factory,
|
||||||
preparer=self.preparer,
|
|
||||||
ignore_dependencies=self.ignore_dependencies,
|
ignore_dependencies=self.ignore_dependencies,
|
||||||
make_install_req=self.make_install_req,
|
|
||||||
)
|
)
|
||||||
reporter = BaseReporter()
|
reporter = BaseReporter()
|
||||||
resolver = RLResolver(provider, reporter)
|
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)
|
self._result = resolver.resolve(requirements)
|
||||||
|
|
||||||
req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
|
req_set = RequirementSet(check_supported_wheels=check_supported_wheels)
|
||||||
|
|
Loading…
Reference in New Issue