Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: modern typing #135

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ['setuptools>=36.2.2', 'wheel>=0.28.0']
requires = ['setuptools>=62', 'wheel']

[tool.black]
line-length = 79
Expand Down
3 changes: 2 additions & 1 deletion src/resolvelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
__version__ = "1.0.2.dev0"


from .providers import AbstractProvider, AbstractResolver
from .providers import AbstractProvider
from .reporters import BaseReporter
from .resolvers import (
AbstractResolver,
InconsistentCandidate,
RequirementsConflicted,
ResolutionError,
Expand Down
11 changes: 0 additions & 11 deletions src/resolvelib/__init__.pyi

This file was deleted.

74 changes: 38 additions & 36 deletions src/resolvelib/providers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
class AbstractProvider(object):
from __future__ import annotations

from typing import (
TYPE_CHECKING,
Generic,
Iterable,
Iterator,
Mapping,
Sequence,
)

from .structs import CT, KT, RT, Matches, RequirementInformation

if TYPE_CHECKING:
from typing import Any, Protocol

class Preference(Protocol):
def __lt__(self, __other: Any) -> bool:
...


class AbstractProvider(Generic[RT, CT, KT]):
"""Delegate class to provide the required interface for the resolver."""

def identify(self, requirement_or_candidate):
def identify(self, requirement_or_candidate: RT | CT) -> KT:
"""Given a requirement or candidate, return an identifier for it.

This is used to identify, e.g. whether two requirements
Expand All @@ -12,13 +33,13 @@ def identify(self, requirement_or_candidate):

def get_preference(
self,
identifier,
resolutions,
candidates,
information,
backtrack_causes,
):
"""Produce a sort key for the given requirement based on preference.
identifier: KT,
resolutions: Mapping[KT, CT],
candidates: Mapping[KT, Iterator[CT]],
information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]],
backtrack_causes: Sequence[RequirementInformation[RT, CT]],
) -> Preference:
"""Produce a sort key for given requirement based on preference.

The preference is defined as "I think this requirement should be
resolved first". The lower the return value is, the more preferred
Expand Down Expand Up @@ -62,7 +83,12 @@ def get_preference(
"""
raise NotImplementedError

def find_matches(self, identifier, requirements, incompatibilities):
def find_matches(
self,
identifier: KT,
requirements: Mapping[KT, Iterator[RT]],
incompatibilities: Mapping[KT, Iterator[CT]],
) -> Matches:
"""Find all possible candidates that satisfy the given constraints.

:param identifier: An identifier as returned by ``identify()``. All
Expand Down Expand Up @@ -92,7 +118,7 @@ def find_matches(self, identifier, requirements, incompatibilities):
"""
raise NotImplementedError

def is_satisfied_by(self, requirement, candidate):
def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool:
"""Whether the given requirement can be satisfied by a candidate.

The candidate is guaranteed to have been generated from the
Expand All @@ -103,34 +129,10 @@ def is_satisfied_by(self, requirement, candidate):
"""
raise NotImplementedError

def get_dependencies(self, candidate):
def get_dependencies(self, candidate: CT) -> Iterable[RT]:
"""Get dependencies of a candidate.

This should return a collection of requirements that `candidate`
specifies as its dependencies.
"""
raise NotImplementedError


class AbstractResolver(object):
"""The thing that performs the actual resolution work."""

base_exception = Exception

def __init__(self, provider, reporter):
self.provider = provider
self.reporter = reporter

def resolve(self, requirements, **kwargs):
"""Take a collection of constraints, spit out the resolution result.

This returns a representation of the final resolution state, with one
guaranteed attribute ``mapping`` that contains resolved candidates as
values. The keys are their respective identifiers.

:param requirements: A collection of constraints.
:param kwargs: Additional keyword arguments that subclasses may accept.

:raises: ``self.base_exception`` or its subclass.
"""
raise NotImplementedError
44 changes: 0 additions & 44 deletions src/resolvelib/providers.pyi

This file was deleted.

37 changes: 28 additions & 9 deletions src/resolvelib/reporters.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
class BaseReporter(object):
from __future__ import annotations

from typing import Generic

from .structs import (
CT,
KT,
RT,
Collection,
Criterion,
RequirementInformation,
State,
)


class BaseReporter(Generic[RT, CT, KT]):
"""Delegate class to provider progress reporting for the resolver."""

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

def starting_round(self, index):
def starting_round(self, index: int) -> None:
"""Called before each round of resolution starts.

The index is zero-based.
"""

def ending_round(self, index, state):
def ending_round(self, index: int, state: State[RT, CT, KT]) -> None:
"""Called before each round of resolution ends.

This is NOT called if the resolution ends at this round. Use `ending`
if you want to report finalization. The index is zero-based.
"""

def ending(self, state):
def ending(self, state: State[RT, CT, KT]) -> None:
"""Called before the resolution ends successfully."""

def adding_requirement(self, requirement, parent):
def adding_requirement(self, requirement: RT, parent: CT | None) -> None:
"""Called when adding a new requirement into the resolve criteria.

:param requirement: The additional requirement to be applied to filter
Expand All @@ -30,14 +45,18 @@ def adding_requirement(self, requirement, parent):
requirements passed in from ``Resolver.resolve()``.
"""

def resolving_conflicts(self, causes):
def resolving_conflicts(
self, causes: Collection[RequirementInformation[RT, CT]]
) -> None:
"""Called when starting to attempt requirement conflict resolution.

:param causes: The information on the collision that caused the backtracking.
"""

def rejecting_candidate(self, criterion, candidate):
def rejecting_candidate(
self, criterion: Criterion[RT, CT], candidate: CT
) -> None:
"""Called when rejecting a candidate during backtracking."""

def pinning(self, candidate):
def pinning(self, candidate: CT) -> None:
"""Called when adding a candidate to the potential solution."""
11 changes: 0 additions & 11 deletions src/resolvelib/reporters.pyi

This file was deleted.

Loading
Loading