Skip to content

Commit

Permalink
Allow provider to narrow backtrack selection
Browse files Browse the repository at this point in the history
  • Loading branch information
notatallshaw committed Mar 30, 2024
1 parent a062162 commit c52395a
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 2 deletions.
57 changes: 57 additions & 0 deletions src/resolvelib/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,60 @@ def get_dependencies(self, candidate: CT) -> Iterable[RT]:
specifies as its dependencies.
"""
raise NotImplementedError

def narrow_requirement_selection(
self,
identifiers: Iterable[KT],
resolutions: Mapping[KT, CT],
candidates: Mapping[KT, Iterator[CT]],
information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]],
backtrack_causes: Sequence[RequirementInformation[RT, CT]],
) -> Iterable[KT]:
"""
An optional method to narrow the selection of requirements being
considered during resolution.
The requirement selection is defined as "The possible requirements
that will be resolved next." If a requirement is not part of the returned
iterable, it will not be considered during the next step of resolution.
:param identifiers: An iterable of `identifiers` as returned by
``identify()``. These identify all requirements currently being
considered.
:param resolutions: A mapping of candidates currently pinned by the
resolver. Each key is an identifier, and the value is a candidate
that may conflict with requirements from ``information``.
:param candidates: A mapping of each dependency's possible candidates.
Each value is an iterator of candidates.
:param information: A mapping of requirement information for each package.
Each value is an iterator of *requirement information*.
:param backtrack_causes: A sequence of *requirement information* that are
the requirements causing the resolver to most recently
backtrack.
A *requirement information* instance is a named tuple with two members:
* ``requirement`` specifies a requirement contributing to the current
list of candidates.
* ``parent`` specifies the candidate that provides (is depended on for)
the requirement, or ``None`` to indicate a root requirement.
Must return a non-empty subset of `identifiers`, with the default
implementation being to return `identifiers` unchanged.
Can be used by the provider to optimize the dependency resolution
process. `get_preference` will only be called for the identifiers
returned. If there is only one identifier returned, then `get_preference`
won't be called at all.
Serving a similar purpose as `get_preference`, this method allows the
provider to guide resolvelib through the resolution process. It should
be used instead of `get_preference` for logic when the provider needs
to consider multiple identifiers simultaneously, or when the provider
wants to skip checking all identifiers, e.g., because the checks are
prohibitively expensive.
Returns:
Iterable[KT]: A non-empty subset of `identifiers`.
"""
return identifiers
30 changes: 28 additions & 2 deletions src/resolvelib/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,34 @@ def resolve(
unsatisfied_names
)

# Choose the most preferred unpinned criterion to try.
name = min(unsatisfied_names, key=self._get_preference)
if len(unsatisfied_names) > 1:
narrowed_unstatisfied_names = list(
self._p.narrow_requirement_selection(
identifiers=unsatisfied_names,
resolutions=self.state.mapping,
candidates=IteratorMapping(
self.state.criteria,
operator.attrgetter("candidates"),
),
information=IteratorMapping(
self.state.criteria,
operator.attrgetter("information"),
),
backtrack_causes=self.state.backtrack_causes,
)
)
else:
narrowed_unstatisfied_names = unsatisfied_names

# If there is only 1 unsatisfied name skip calling self._get_preference
if len(narrowed_unstatisfied_names) > 1:
# Choose the most preferred unpinned criterion to try.
name = min(
narrowed_unstatisfied_names, key=self._get_preference
)
else:
name = narrowed_unstatisfied_names[0]

failure_criterion = self._attempt_to_pin_criterion(name)

if failure_criterion:
Expand Down

0 comments on commit c52395a

Please sign in to comment.