From b74ce753d1dad377833a5792154f3fd2cba68b23 Mon Sep 17 00:00:00 2001 From: Damian Shaw Date: Tue, 18 Nov 2025 20:50:40 -0500 Subject: [PATCH 1/2] Pre-compute Python Requirements Candidate Lookup --- .../_internal/resolution/resolvelib/requirements.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/requirements.py b/src/pip/_internal/resolution/resolvelib/requirements.py index 447e36b5ac1..9c5e2337411 100644 --- a/src/pip/_internal/resolution/resolvelib/requirements.py +++ b/src/pip/_internal/resolution/resolvelib/requirements.py @@ -164,6 +164,12 @@ def __init__(self, specifier: SpecifierSet, match: Candidate) -> None: self._hash: int | None = None self._candidate = match + # Pre-compute candidate lookup to avoid repeated specifier checks + if specifier.contains(match.version, prereleases=True): + self._candidate_lookup: CandidateLookup = (match, None) + else: + self._candidate_lookup = (None, None) + def __str__(self) -> str: return f"Python {self.specifier}" @@ -197,9 +203,7 @@ def format_for_error(self) -> str: return str(self) def get_candidate_lookup(self) -> CandidateLookup: - if self.specifier.contains(self._candidate.version, prereleases=True): - return self._candidate, None - return None, None + return self._candidate_lookup def is_satisfied_by(self, candidate: Candidate) -> bool: assert candidate.name == self._candidate.name, "Not Python candidate" From 4d65ba60761acc467dde7a343df62d8accb7b140 Mon Sep 17 00:00:00 2001 From: Damian Shaw Date: Tue, 18 Nov 2025 21:07:23 -0500 Subject: [PATCH 2/2] News Entry. --- news/13656.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/13656.bugfix.rst diff --git a/news/13656.bugfix.rst b/news/13656.bugfix.rst new file mode 100644 index 00000000000..bfaedb398f7 --- /dev/null +++ b/news/13656.bugfix.rst @@ -0,0 +1 @@ +Precompute Python Requirements on each candidate, reducing time of long resolutions.