Skip to content

Commit

Permalink
Use the project name to look up constraints
Browse files Browse the repository at this point in the history
User-specified constraints only contain the project name without extras,
but a constraint a project A would also apply to A's extra variants.
This introduces a new project_name property on Requirement and Candidate
classes for this lookup.
  • Loading branch information
uranusjr committed Dec 7, 2020
1 parent ab7ff0a commit 87adddb
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 7 deletions.
2 changes: 2 additions & 0 deletions news/9232.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
New resolver: Make constraints also apply to package variants with extras, so
the resolver correctly avoids baktracking on them.
32 changes: 32 additions & 0 deletions src/pip/_internal/resolution/resolvelib/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,25 @@ def is_satisfied_by(self, candidate):


class Requirement(object):
@property
def project_name(self):
# type: () -> str
"""The "project name" of a requirement.
This is different from ``name`` if this requirement contains extras,
in which case ``name`` would contain the ``[...]`` part, while this
refers to the name of the project.
"""
raise NotImplementedError("Subclass should override")

@property
def name(self):
# type: () -> str
"""The name identifying this requirement in the resolver.
This is different from ``project_name`` if this requirement contains
extras, where ``project_name`` would not contain the ``[...]`` part.
"""
raise NotImplementedError("Subclass should override")

def is_satisfied_by(self, candidate):
Expand All @@ -86,9 +102,25 @@ def format_for_error(self):


class Candidate(object):
@property
def project_name(self):
# type: () -> str
"""The "project name" of the candidate.
This is different from ``name`` if this candidate contains extras,
in which case ``name`` would contain the ``[...]`` part, while this
refers to the name of the project.
"""
raise NotImplementedError("Override in subclass")

@property
def name(self):
# type: () -> str
"""The name identifying this candidate in the resolver.
This is different from ``project_name`` if this candidate contains
extras, where ``project_name`` would not contain the ``[...]`` part.
"""
raise NotImplementedError("Override in subclass")

@property
Expand Down
28 changes: 24 additions & 4 deletions src/pip/_internal/resolution/resolvelib/candidates.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,18 @@ def source_link(self):
return self._source_link

@property
def name(self):
def project_name(self):
# type: () -> str
"""The normalised name of the project the candidate refers to"""
if self._name is None:
self._name = canonicalize_name(self.dist.project_name)
return self._name

@property
def name(self):
# type: () -> str
return self.project_name

@property
def version(self):
# type: () -> _BaseVersion
Expand Down Expand Up @@ -390,10 +395,15 @@ def __ne__(self, other):
return not self.__eq__(other)

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

@property
def name(self):
# type: () -> str
return self.project_name

@property
def version(self):
# type: () -> _BaseVersion
Expand Down Expand Up @@ -481,11 +491,16 @@ def __ne__(self, other):
# type: (Any) -> bool
return not self.__eq__(other)

@property
def project_name(self):
# type: () -> str
return self.base.project_name

@property
def name(self):
# type: () -> str
"""The normalised name of the project the candidate refers to"""
return format_name(self.base.name, self.extras)
return format_name(self.base.project_name, self.extras)

@property
def version(self):
Expand Down Expand Up @@ -572,11 +587,16 @@ def __str__(self):
return "Python {}".format(self._version)

@property
def name(self):
def project_name(self):
# type: () -> str
# Avoid conflicting with the PyPI package "Python".
return "<Python from Requires-Python>"

@property
def name(self):
# type: () -> str
return self.project_name

@property
def version(self):
# type: () -> _BaseVersion
Expand Down
12 changes: 11 additions & 1 deletion src/pip/_internal/resolution/resolvelib/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@


class PipProvider(AbstractProvider):
"""Pip's provider implementation for resolvelib.
:params constraints: A mapping of constraints specified by the user. Keys
are canonicalized project names.
:params ignore_dependencies: Whether the user specified ``--no-deps``.
:params upgrade_strategy: The user-specified upgrade strategy.
:params user_requested: A set of canonicalized package names that the user
supplied for pip to install/upgrade.
"""

def __init__(
self,
factory, # type: Factory
Expand Down Expand Up @@ -113,7 +123,7 @@ def find_matches(self, requirements):
# type: (Sequence[Requirement]) -> Iterable[Candidate]
if not requirements:
return []
name = requirements[0].name
name = requirements[0].project_name

def _eligible_for_upgrade(name):
# type: (str) -> bool
Expand Down
19 changes: 17 additions & 2 deletions src/pip/_internal/resolution/resolvelib/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ def __repr__(self):
candidate=self.candidate,
)

@property
def project_name(self):
# type: () -> str
# No need to canonicalise - the candidate did this
return self.candidate.project_name

@property
def name(self):
# type: () -> str
Expand Down Expand Up @@ -65,11 +71,15 @@ def __repr__(self):
requirement=str(self._ireq.req),
)

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

@property
def name(self):
# type: () -> str
canonical_name = canonicalize_name(self._ireq.req.name)
return format_name(canonical_name, self._extras)
return format_name(self.project_name, self._extras)

def format_for_error(self):
# type: () -> str
Expand Down Expand Up @@ -121,6 +131,11 @@ def __repr__(self):
specifier=str(self.specifier),
)

@property
def project_name(self):
# type: () -> str
return self._candidate.project_name

@property
def name(self):
# type: () -> str
Expand Down

0 comments on commit 87adddb

Please sign in to comment.