Skip to content

Commit f5480f1

Browse files
committed
Cache is_satisfied_by
1 parent 98a0593 commit f5480f1

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import collections
22
import math
3+
from functools import lru_cache
34
from typing import (
45
TYPE_CHECKING,
56
Dict,
@@ -236,6 +237,7 @@ def _eligible_for_upgrade(identifier: str) -> bool:
236237
incompatibilities=incompatibilities,
237238
)
238239

240+
@lru_cache(maxsize=None)
239241
def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool:
240242
return requirement.is_satisfied_by(candidate)
241243

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
from typing import Any
2+
13
from pip._vendor.packaging.specifiers import SpecifierSet
24
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
35

46
from pip._internal.req.constructors import install_req_drop_extras
57
from pip._internal.req.req_install import InstallRequirement
8+
from pip._internal.utils.models import KeyBasedCompareMixin
69

710
from .base import Candidate, CandidateLookup, Requirement, format_name
811

@@ -17,6 +20,14 @@ def __str__(self) -> str:
1720
def __repr__(self) -> str:
1821
return f"{self.__class__.__name__}({self.candidate!r})"
1922

23+
def __hash__(self) -> int:
24+
return hash(self.candidate)
25+
26+
def __eq__(self, other: Any) -> bool:
27+
if not isinstance(other, ExplicitRequirement):
28+
return False
29+
return self.candidate == other.candidate
30+
2031
@property
2132
def project_name(self) -> NormalizedName:
2233
# No need to canonicalize - the candidate did this
@@ -37,11 +48,14 @@ def is_satisfied_by(self, candidate: Candidate) -> bool:
3748
return candidate == self.candidate
3849

3950

40-
class SpecifierRequirement(Requirement):
51+
class SpecifierRequirement(Requirement, KeyBasedCompareMixin):
4152
def __init__(self, ireq: InstallRequirement) -> None:
4253
assert ireq.link is None, "This is a link, not a specifier"
4354
self._ireq = ireq
4455
self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
56+
KeyBasedCompareMixin.__init__(
57+
self, key=str(ireq), defining_class=SpecifierRequirement
58+
)
4559

4660
def __str__(self) -> str:
4761
return str(self._ireq.req)
@@ -97,13 +111,17 @@ def __init__(self, ireq: InstallRequirement) -> None:
97111
assert ireq.link is None, "This is a link, not a specifier"
98112
self._ireq = install_req_drop_extras(ireq)
99113
self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
114+
KeyBasedCompareMixin.__init__(
115+
self, key=str(ireq), defining_class=SpecifierRequirement
116+
)
100117

101118

102119
class RequiresPythonRequirement(Requirement):
103120
"""A requirement representing Requires-Python metadata."""
104121

105122
def __init__(self, specifier: SpecifierSet, match: Candidate) -> None:
106123
self.specifier = specifier
124+
self._specifier_string = str(specifier) # for faster __eq__
107125
self._candidate = match
108126

109127
def __str__(self) -> str:
@@ -112,6 +130,17 @@ def __str__(self) -> str:
112130
def __repr__(self) -> str:
113131
return f"{self.__class__.__name__}({str(self.specifier)!r})"
114132

133+
def __hash__(self) -> int:
134+
return hash((self._specifier_string, self._candidate))
135+
136+
def __eq__(self, other: Any) -> bool:
137+
if not isinstance(other, RequiresPythonRequirement):
138+
return False
139+
return (
140+
self._specifier_string == other._specifier_string
141+
and self._candidate == other._candidate
142+
)
143+
115144
@property
116145
def project_name(self) -> NormalizedName:
117146
return self._candidate.project_name
@@ -136,11 +165,14 @@ def is_satisfied_by(self, candidate: Candidate) -> bool:
136165
return self.specifier.contains(candidate.version, prereleases=True)
137166

138167

139-
class UnsatisfiableRequirement(Requirement):
168+
class UnsatisfiableRequirement(Requirement, KeyBasedCompareMixin):
140169
"""A requirement that cannot be satisfied."""
141170

142171
def __init__(self, name: NormalizedName) -> None:
143172
self._name = name
173+
KeyBasedCompareMixin.__init__(
174+
self, key=str(name), defining_class=UnsatisfiableRequirement
175+
)
144176

145177
def __str__(self) -> str:
146178
return f"{self._name} (unavailable)"

0 commit comments

Comments
 (0)