Skip to content

Commit

Permalink
Merge pull request #81 from nadavwe/report-start-backtracking
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr authored Oct 11, 2021
2 parents c911178 + dd6a227 commit 12cfc09
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/resolvelib/reporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def adding_requirement(self, requirement, parent):
requirements passed in from ``Resolver.resolve()``.
"""

def resolving_conflicts(self, causes):
"""Called when starting to attempt requirement conflict resolution.
:param causes: The information on the collision that caused the backtracking.
"""

def backtracking(self, candidate):
"""Called when rejecting a candidate during backtracking."""

Expand Down
1 change: 1 addition & 0 deletions src/resolvelib/reporters.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ class BaseReporter:
def ending(self, state: Any) -> Any: ...
def adding_requirement(self, requirement: Any, parent: Any) -> Any: ...
def backtracking(self, candidate: Any) -> Any: ...
def resolving_conflicts(self, causes: Any) -> Any: ...
def pinning(self, candidate: Any) -> Any: ...
6 changes: 3 additions & 3 deletions src/resolvelib/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,12 @@ def resolve(self, requirements, max_rounds):
failure_causes = self._attempt_to_pin_criterion(name)

if failure_causes:
causes = [i for c in failure_causes for i in c.information]
# Backtrack if pinning fails. The backtrack process puts us in
# an unpinned state, so we can work on it in the next round.
self._r.resolving_conflicts(causes=causes)
success = self._backtrack()
self.state.backtrack_causes[:] = [
i for c in failure_causes for i in c.information
]
self.state.backtrack_causes[:] = causes

# Dead ends everywhere. Give up.
if not success:
Expand Down
52 changes: 52 additions & 0 deletions tests/test_resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
BaseReporter,
InconsistentCandidate,
Resolver,
ResolutionImpossible,
)


Expand Down Expand Up @@ -91,3 +92,54 @@ def is_satisfied_by(self, requirement, candidate):

assert set(result.mapping) == {"parent", "child"}
assert result.mapping["child"] == ("child", "1", [])


def test_resolving_conflicts():
all_candidates = {
"a": [("a", 1, [("q", {1})]), ("a", 2, [("q", {2})])],
"b": [("b", 1, [("q", {1})])],
"q": [("q", 1, []), ("q", 2, [])],
}

class Reporter(BaseReporter):
def __init__(self):
self.backtracking_causes = None

def resolving_conflicts(self, causes):
self.backtracking_causes = causes

class Provider(AbstractProvider):
def identify(self, requirement_or_candidate):
return requirement_or_candidate[0]

def get_preference(self, **_):
return 0

def get_dependencies(self, candidate):
return candidate[2]

def find_matches(self, identifier, requirements, incompatibilities):
bad_versions = {c[1] for c in incompatibilities[identifier]}
candidates = [
c
for c in all_candidates[identifier]
if all(c[1] in r[1] for r in requirements[identifier])
and c[1] not in bad_versions
]
return sorted(candidates, key=lambda c: c[1], reverse=True)

def is_satisfied_by(self, requirement, candidate):
return candidate[1] in requirement[1]

def run_resolver(*args):
reporter = Reporter()
resolver = Resolver(Provider(), reporter)
try:
resolver.resolve(*args)
return reporter.backtracking_causes
except ResolutionImpossible as e:
return e.causes

backtracking_causes = run_resolver([("a", {1, 2}), ("b", {1})])
exception_causes = run_resolver([("a", {2}), ("b", {1})])
assert exception_causes == backtracking_causes

0 comments on commit 12cfc09

Please sign in to comment.