Skip to content

Commit 8610508

Browse files
authored
Merge pull request #13444 from ichard26/vendoring-bumps-25.2-b
Upgrade resolvelib to 1.2.0
2 parents f68e7c9 + 88c90c2 commit 8610508

File tree

6 files changed

+101
-19
lines changed

6 files changed

+101
-19
lines changed

news/resolvelib.vendor.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade resolvelib to 1.2.0

src/pip/_vendor/resolvelib/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
__all__ = [
2-
"__version__",
32
"AbstractProvider",
43
"AbstractResolver",
54
"BaseReporter",
65
"InconsistentCandidate",
7-
"Resolver",
86
"RequirementsConflicted",
97
"ResolutionError",
108
"ResolutionImpossible",
119
"ResolutionTooDeep",
10+
"Resolver",
11+
"__version__",
1212
]
1313

14-
__version__ = "1.1.0"
14+
__version__ = "1.2.0"
1515

1616

1717
from .providers import AbstractProvider

src/pip/_vendor/resolvelib/reporters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
class BaseReporter(Generic[RT, CT, KT]):
12-
"""Delegate class to provider progress reporting for the resolver."""
12+
"""Delegate class to provide progress reporting for the resolver."""
1313

1414
def starting(self) -> None:
1515
"""Called before the resolution actually starts."""

src/pip/_vendor/resolvelib/resolvers/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313

1414
__all__ = [
1515
"AbstractResolver",
16+
"Criterion",
1617
"InconsistentCandidate",
17-
"Resolver",
18-
"Resolution",
18+
"RequirementInformation",
1919
"RequirementsConflicted",
20+
"Resolution",
2021
"ResolutionError",
2122
"ResolutionImpossible",
2223
"ResolutionTooDeep",
23-
"RequirementInformation",
24+
"Resolver",
2425
"ResolverException",
2526
"Result",
26-
"Criterion",
2727
]

src/pip/_vendor/resolvelib/resolvers/resolution.py

Lines changed: 91 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import collections
44
import itertools
55
import operator
6-
from typing import TYPE_CHECKING, Collection, Generic, Iterable, Mapping
6+
from typing import TYPE_CHECKING, Generic
77

88
from ..structs import (
99
CT,
@@ -27,9 +27,13 @@
2727
)
2828

2929
if TYPE_CHECKING:
30+
from collections.abc import Collection, Iterable, Mapping
31+
3032
from ..providers import AbstractProvider, Preference
3133
from ..reporters import BaseReporter
3234

35+
_OPTIMISTIC_BACKJUMPING_RATIO: float = 0.1
36+
3337

3438
def _build_result(state: State[RT, CT, KT]) -> Result[RT, CT, KT]:
3539
mapping = state.mapping
@@ -77,6 +81,11 @@ def __init__(
7781
self._r = reporter
7882
self._states: list[State[RT, CT, KT]] = []
7983

84+
# Optimistic backjumping variables
85+
self._optimistic_backjumping_ratio = _OPTIMISTIC_BACKJUMPING_RATIO
86+
self._save_states: list[State[RT, CT, KT]] | None = None
87+
self._optimistic_start_round: int | None = None
88+
8089
@property
8190
def state(self) -> State[RT, CT, KT]:
8291
try:
@@ -274,6 +283,25 @@ def _patch_criteria(
274283
)
275284
return True
276285

286+
def _save_state(self) -> None:
287+
"""Save states for potential rollback if optimistic backjumping fails."""
288+
if self._save_states is None:
289+
self._save_states = [
290+
State(
291+
mapping=s.mapping.copy(),
292+
criteria=s.criteria.copy(),
293+
backtrack_causes=s.backtrack_causes[:],
294+
)
295+
for s in self._states
296+
]
297+
298+
def _rollback_states(self) -> None:
299+
"""Rollback states and disable optimistic backjumping."""
300+
self._optimistic_backjumping_ratio = 0.0
301+
if self._save_states:
302+
self._states = self._save_states
303+
self._save_states = None
304+
277305
def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool:
278306
"""Perform backjumping.
279307
@@ -324,13 +352,26 @@ def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool:
324352
except (IndexError, KeyError):
325353
raise ResolutionImpossible(causes) from None
326354

327-
# Only backjump if the current broken state is
328-
# an incompatible dependency
329-
if name not in incompatible_deps:
355+
if (
356+
not self._optimistic_backjumping_ratio
357+
and name not in incompatible_deps
358+
):
359+
# For safe backjumping only backjump if the current dependency
360+
# is not the same as the incompatible dependency
330361
break
331362

363+
# On the first time a non-safe backjump is done the state
364+
# is saved so we can restore it later if the resolution fails
365+
if (
366+
self._optimistic_backjumping_ratio
367+
and self._save_states is None
368+
and name not in incompatible_deps
369+
):
370+
self._save_state()
371+
332372
# If the current dependencies and the incompatible dependencies
333-
# are overlapping then we have found a cause of the incompatibility
373+
# are overlapping then we have likely found a cause of the
374+
# incompatibility
334375
current_dependencies = {
335376
self._p.identify(d) for d in self._p.get_dependencies(candidate)
336377
}
@@ -394,9 +435,32 @@ def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT,
394435
# pinning the virtual "root" package in the graph.
395436
self._push_new_state()
396437

438+
# Variables for optimistic backjumping
439+
optimistic_rounds_cutoff: int | None = None
440+
optimistic_backjumping_start_round: int | None = None
441+
397442
for round_index in range(max_rounds):
398443
self._r.starting_round(index=round_index)
399444

445+
# Handle if optimistic backjumping has been running for too long
446+
if self._optimistic_backjumping_ratio and self._save_states is not None:
447+
if optimistic_backjumping_start_round is None:
448+
optimistic_backjumping_start_round = round_index
449+
optimistic_rounds_cutoff = int(
450+
(max_rounds - round_index) * self._optimistic_backjumping_ratio
451+
)
452+
453+
if optimistic_rounds_cutoff <= 0:
454+
self._rollback_states()
455+
continue
456+
elif optimistic_rounds_cutoff is not None:
457+
if (
458+
round_index - optimistic_backjumping_start_round
459+
>= optimistic_rounds_cutoff
460+
):
461+
self._rollback_states()
462+
continue
463+
400464
unsatisfied_names = [
401465
key
402466
for key, criterion in self.state.criteria.items()
@@ -448,12 +512,29 @@ def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT,
448512
# Backjump if pinning fails. The backjump process puts us in
449513
# an unpinned state, so we can work on it in the next round.
450514
self._r.resolving_conflicts(causes=causes)
451-
success = self._backjump(causes)
452-
self.state.backtrack_causes[:] = causes
453515

454-
# Dead ends everywhere. Give up.
455-
if not success:
456-
raise ResolutionImpossible(self.state.backtrack_causes)
516+
try:
517+
success = self._backjump(causes)
518+
except ResolutionImpossible:
519+
if self._optimistic_backjumping_ratio and self._save_states:
520+
failed_optimistic_backjumping = True
521+
else:
522+
raise
523+
else:
524+
failed_optimistic_backjumping = bool(
525+
not success
526+
and self._optimistic_backjumping_ratio
527+
and self._save_states
528+
)
529+
530+
if failed_optimistic_backjumping and self._save_states:
531+
self._rollback_states()
532+
else:
533+
self.state.backtrack_causes[:] = causes
534+
535+
# Dead ends everywhere. Give up.
536+
if not success:
537+
raise ResolutionImpossible(self.state.backtrack_causes)
457538
else:
458539
# discard as information sources any invalidated names
459540
# (unsatisfied names that were previously satisfied)

src/pip/_vendor/vendor.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ requests==2.32.4
1212
rich==14.0.0
1313
pygments==2.19.2
1414
typing_extensions==4.14.0
15-
resolvelib==1.1.0
15+
resolvelib==1.2.0
1616
setuptools==70.3.0
1717
tomli==2.2.1
1818
tomli-w==1.2.0

0 commit comments

Comments
 (0)