Skip to content

First stab at implementing the resolver provider using pip internals #7799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6e06123
WIP provider implementation
uranusjr Feb 26, 2020
18c83dc
Format, not join
uranusjr Feb 27, 2020
f270056
Rely on the evaluator to sort matches
uranusjr Feb 27, 2020
f478bc0
Bring editable into picture
uranusjr Feb 27, 2020
a1a23ec
Testing framework for the new resolvelib provider
pfmoore Feb 27, 2020
6e7cf0f
Reuse find_best_candidate() for candidate sorting
uranusjr Feb 28, 2020
307c0b9
Add todo to handle the link attribute better later
uranusjr Feb 28, 2020
d0ad73a
Fix the types!
uranusjr Feb 28, 2020
e7e3cdc
Move resolver test helpers into test file
uranusjr Feb 28, 2020
0197d13
There are tests using finder and preparer fixtures
uranusjr Feb 28, 2020
8b86746
Remove confusing? stub args
uranusjr Mar 3, 2020
e31f2f9
Tidy up requirement and candidate models
uranusjr Mar 3, 2020
4517ef4
Implement candidate discovery via PackageFinder
uranusjr Mar 3, 2020
816b3ef
Merge branch 'provider-integration' of https://github.com/pypa/pip in…
uranusjr Mar 4, 2020
e5d492d
Merge branch 'provider-integration' of github.com:pypa/pip into provi…
uranusjr Mar 4, 2020
34430ec
Assert IReq state in VersionedRequirement
uranusjr Mar 4, 2020
89a677e
Use ireq.hash
uranusjr Mar 4, 2020
aad12ec
Fix hashes signature
uranusjr Mar 5, 2020
fab4ab9
Make candidate backed by ireq
uranusjr Mar 5, 2020
1325d68
A reminder to implement wheel cache
uranusjr Mar 5, 2020
7650661
Implement get_dependencies()
uranusjr Mar 5, 2020
eb50451
isort does not like comments in import block
uranusjr Mar 5, 2020
64ccaaa
InstallRequirementProvider is typing-only
uranusjr Mar 5, 2020
f741e3a
Revert "Remove confusing? stub args"
uranusjr Mar 5, 2020
1f5bf21
Get the tests closer to working again
pfmoore Mar 5, 2020
dc63d24
Sigh, sorry linters...
pfmoore Mar 5, 2020
5265557
Remove req.url check in candidate construction
uranusjr Mar 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
147 changes: 147 additions & 0 deletions src/pip/_internal/resolution/resolvelib/candidates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
from pip._vendor.packaging.utils import canonicalize_name

# TODO: Re-implement me.
from pip._internal.resolution.legacy.resolver import (
UnsupportedPythonVersion,
_check_dist_requires_python,
)
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

if MYPY_CHECK_RUNNING:
from typing import Callable, Optional, Sequence, Set

from pip._vendor.packaging.version import _BaseVersion
from pip._vendor.pkg_resources import Distribution
from pip._internal.distributions import AbstractDistribution
from pip._internal.models.link import Link
from pip._internal.req.req_install import InstallRequirement

from .requirements import Requirement, ResolveOptions

RequirementProvider = Callable[[str, InstallRequirement], Requirement]


def format_name(project, extras):
# type: (str, Set[str]) -> str
if not extras:
return project
canonical_extras = sorted(canonicalize_name(e) for e in extras)
return "{}[{}]".format(project, ",".join(canonical_extras))


class Candidate(object):
@property
def name(self):
# type: () -> str
raise NotImplementedError("Override in subclass")

@property
def version(self):
# type: () -> _BaseVersion
raise NotImplementedError("Override in subclass")

@property
def link(self):
# type: () -> Link
raise NotImplementedError("Override in subclass")

def get_dependencies(self, make_req):
# type: (RequirementProvider) -> Sequence[Requirement]
raise NotImplementedError("Override in subclass")


class ExtrasCandidate(Candidate):
def __init__(self, candidate, extras):
# type: (ConcreteCandidate, Set[str]) -> None
self._candidate = candidate
self._extras = extras

@property
def name(self):
# type: () -> str
return format_name(self._candidate.name, self._extras)

@property
def version(self):
# type: () -> _BaseVersion
return self._candidate.version

@property
def link(self):
# type: () -> Link
return self._candidate.link

def get_dependencies(self, make_req):
# type: (RequirementProvider) -> Sequence[Requirement]
return [
make_req(str(r), self._candidate._ireq)
for r in self._candidate._dist.requires(self._extras)
]


class ConcreteCandidate(Candidate):
def __init__(self, ireq, dist):
# type: (InstallRequirement, Distribution) -> None
assert ireq.link is not None, "Candidate should be pinned"
assert ireq.req is not None, "Un-specified requirement not allowed"
self._ireq = ireq
self._dist = dist

@classmethod
def from_abstract_dist(
cls,
adist, # type: AbstractDistribution
ireq, # type: InstallRequirement
options, # type: ResolveOptions
):
# type: (...) -> Optional[ConcreteCandidate]
dist = adist.get_pkg_resources_distribution()
try:
_check_dist_requires_python(
dist,
options.python_version_info,
options.ignore_requires_python,
)
except UnsupportedPythonVersion:
return None
return cls(ireq, dist)

@property
def name(self):
# type: () -> str
return canonicalize_name(self._ireq.req.name)

@property
def version(self):
# type: () -> _BaseVersion
return self._dist.parsed_version

@property
def link(self):
# type: () -> Link
assert self._ireq.link is not None, "Candidate should be pinned"
return self._ireq.link

def get_dependencies(self, make_req):
# type: (RequirementProvider) -> Sequence[Requirement]
return [
make_req(str(r), self._ireq)
for r in self._dist.requires()
]


class RemoteCandidate(ConcreteCandidate):
"""Candidate pointing to a remote location.

A remote location can be on-disk, but not installed into the environment.
"""


class EditableCandidate(ConcreteCandidate):
"""Candidate pointing to an editable source.
"""


class InstalledCandidate(ConcreteCandidate):
"""Candidate pointing to an installed dist/egg.
"""
69 changes: 69 additions & 0 deletions src/pip/_internal/resolution/resolvelib/providers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

from .candidates import RemoteCandidate
from .requirements import DirectRequirement, VersionedRequirement

if MYPY_CHECK_RUNNING:
from typing import Any, List, Sequence, Union

from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.req_install import InstallRequirement
# TODO: Re-implement me.
from pip._internal.resolution.legacy.resolver import (
InstallRequirementProvider,
)

from .candidates import Candidate
from .requirements import Requirement, ResolveOptions

Dependency = Union[Requirement, Candidate]


class Provider(object):
def __init__(
self,
finder, # type: PackageFinder
preparer, # type: RequirementPreparer
make_ireq, # type: InstallRequirementProvider
options, # type: ResolveOptions
):
# type: (...) -> None
super(Provider, self).__init__()
self.finder = finder
self.preparer = preparer
self.make_ireq = make_ireq
self.options = options

def identify(self, dependency):
# type: (Dependency) -> str
return dependency.name

def get_preference(self, resolution, candidates, information):
# type: (Any, List[Candidate], Any) -> int
return len(candidates)

def find_matches(self, req):
# type: (Requirement) -> Sequence[Candidate]
return req.find_matches(
self.finder,
self.preparer,
self.options,
)

def is_satisfied_by(self, requirement, candidate):
# type: (Requirement, Candidate) -> bool
return requirement.is_satisfied_by(candidate)

def _requirement_from_spec(self, spec, parent):
# type: (str, InstallRequirement) -> Requirement
ireq = self.make_ireq(spec, parent)
if ireq.link is None:
return VersionedRequirement(ireq)
dist = self.preparer.prepare_linked_requirement(ireq)
cand = RemoteCandidate(ireq, dist.get_pkg_resources_distribution())
return DirectRequirement(cand)

def get_dependencies(self, candidate):
# type: (Candidate) -> Sequence[Requirement]
return candidate.get_dependencies(self._requirement_from_spec)
Loading