Skip to content
This repository was archived by the owner on Apr 27, 2022. It is now read-only.

Commit d9aef7a

Browse files
committed
Attempt to implement logic required for pypa#8253
1 parent cc948a6 commit d9aef7a

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

src/pip/_internal/req/req_install.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -840,8 +840,8 @@ def check_invalid_constraint_type(req):
840840
problem = ""
841841
if not req.name:
842842
problem = "Unnamed requirements are not allowed as constraints"
843-
elif req.link:
844-
problem = "Links are not allowed as constraints"
843+
elif req.editable:
844+
problem = "Editable requirements are not allowed as constraints"
845845
elif req.extras:
846846
problem = "Constraints cannot have extras"
847847

src/pip/_internal/resolution/resolvelib/base.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
from pip._internal.models.link import Link
88
from pip._internal.req.req_install import InstallRequirement
9+
from pip._internal.utils.direct_url_helpers import (
10+
direct_url_from_link,
11+
dist_get_direct_url,
12+
)
913
from pip._internal.utils.hashes import Hashes
1014

1115
CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]]
@@ -20,24 +24,26 @@ def format_name(project, extras):
2024

2125

2226
class Constraint:
23-
def __init__(self, specifier, hashes):
24-
# type: (SpecifierSet, Hashes) -> None
27+
def __init__(self, specifier, hashes, links):
28+
# type: (SpecifierSet, Hashes, Tuple[Link, ...]) -> None
2529
self.specifier = specifier
2630
self.hashes = hashes
31+
self.links = links
2732

2833
@classmethod
2934
def empty(cls):
3035
# type: () -> Constraint
31-
return Constraint(SpecifierSet(), Hashes())
36+
return Constraint(SpecifierSet(), Hashes(), ())
3237

3338
@classmethod
3439
def from_ireq(cls, ireq):
3540
# type: (InstallRequirement) -> Constraint
36-
return Constraint(ireq.specifier, ireq.hashes(trust_internet=False))
41+
links = (ireq.link,) if ireq.link else ()
42+
return Constraint(ireq.specifier, ireq.hashes(trust_internet=False), links)
3743

3844
def __nonzero__(self):
3945
# type: () -> bool
40-
return bool(self.specifier) or bool(self.hashes)
46+
return bool(self.specifier) or bool(self.hashes) or bool(self.links)
4147

4248
def __bool__(self):
4349
# type: () -> bool
@@ -49,14 +55,19 @@ def __and__(self, other):
4955
return NotImplemented
5056
specifier = self.specifier & other.specifier
5157
hashes = self.hashes & other.hashes(trust_internet=False)
52-
return Constraint(specifier, hashes)
58+
links = self.links
59+
if other.link and other.link not in links:
60+
links += (other.link,)
61+
return Constraint(specifier, hashes, links)
5362

5463
def is_satisfied_by(self, candidate):
5564
# type: (Candidate) -> bool
5665
# We can safely always allow prereleases here since PackageFinder
5766
# already implements the prerelease logic, and would have filtered out
5867
# prerelease candidates if the user does not expect them.
59-
return self.specifier.contains(candidate.version, prereleases=True)
68+
return self.specifier.contains(candidate.version, prereleases=True) and all(
69+
_match_link(link, candidate) for link in self.links
70+
)
6071

6172

6273
class Requirement:
@@ -94,6 +105,21 @@ def format_for_error(self):
94105
raise NotImplementedError("Subclass should override")
95106

96107

108+
def _match_link(link, candidate):
109+
# type: (Link, Candidate) -> bool
110+
if candidate.source_link:
111+
return link == candidate.source_link
112+
# The purpose of this is to support AlreadyInstalledCandidates.
113+
# It doesn't handle ExtrasCandidates, which *shouldn't* be necessary.
114+
dist = getattr(candidate, "dist", None)
115+
if dist:
116+
dist_url = dist_get_direct_url(dist)
117+
if dist_url:
118+
# This is missing some optional arguments that *hopefully* aren't needed.
119+
return direct_url_from_link(link).to_dict() == dist_url.to_dict()
120+
return False
121+
122+
97123
class Candidate:
98124
@property
99125
def project_name(self):

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,22 @@ def find_candidates(
264264
if ireq is not None:
265265
ireqs.append(ireq)
266266

267+
for link in constraint.links:
268+
# This part only has to convert an ireq from the requirements to a link.
269+
# If there's only links, either it satisfies, or it doesn't.
270+
if not ireqs:
271+
break
272+
ireq = ireqs[0]
273+
candidate = self._make_candidate_from_link(
274+
link,
275+
extras=frozenset(),
276+
template=ireq,
277+
name=canonicalize_name(ireq.name) if ireq.name else None,
278+
version=None,
279+
)
280+
if candidate is not None:
281+
explicit_candidates.add(candidate)
282+
267283
# If none of the requirements want an explicit candidate, we can ask
268284
# the finder for candidates.
269285
if not explicit_candidates:

0 commit comments

Comments
 (0)