Skip to content

Commit

Permalink
Removed positional distance resolution.
Browse files Browse the repository at this point in the history
  • Loading branch information
coady committed Aug 3, 2024
1 parent d84dd00 commit 6ca918c
Show file tree
Hide file tree
Showing 4 changed files with 7 additions and 38 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## Unreleased
### Removed
* Resolving ambiguity using positional distance

## [1.12](https://pypi.org/project/multimethod/1.12/) - 2024-07-04
### Changed
Expand Down
26 changes: 1 addition & 25 deletions multimethod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def __subclasscheck__(self, subclass):

def __instancecheck__(self, instance):
if self.__origin__ is Literal:
return any(type(arg) == type(instance) and arg == instance for arg in self.__args__)
return any(type(arg) is type(instance) and arg == instance for arg in self.__args__)
if self.__origin__ is Union:
return isinstance(instance, self.__args__)
if hasattr(instance, '__orig_class__'): # user-defined generic type
Expand Down Expand Up @@ -186,14 +186,6 @@ def __and__(self, other):
subtype.origins.register(parametric, lambda cls: cls.__bases__)


def distance(cls, subclass: type) -> int:
"""Return estimated distance between classes for tie-breaking."""
if get_origin(cls) is Union:
return min(distance(arg, subclass) for arg in cls.__args__)
mro = get_mro(subclass)
return mro.index(cls if cls in mro else object)


class signature(tuple):
"""A tuple of types that supports partial ordering."""

Expand Down Expand Up @@ -229,14 +221,6 @@ def __le__(self, other: tuple) -> bool:
def __lt__(self, other: tuple) -> bool:
return self != other and self <= other

def __sub__(self, other: tuple) -> tuple:
"""Return relative distances, assuming self >= other."""
return tuple(map(distance, other, self))

def __rsub__(self, other: tuple) -> tuple:
"""Return relative distances, assuming self <= other."""
return tuple(map(distance, self, other))

def callable(self, *types) -> bool:
"""Check positional arity of associated function signature."""
try:
Expand Down Expand Up @@ -339,14 +323,6 @@ def __delitem__(self, types: tuple):
def select(self, types: tuple, keys: set[signature]) -> Callable:
keys = {key for key in keys if key.callable(*types)}
funcs = {self[key] for key in keys}
if len(funcs) > 1:
groups = collections.defaultdict(set)
for key in keys:
groups[types - key].add(key)
keys = groups[min(groups)]
funcs = {self[key] for key in keys}
if len(funcs) == 1:
warnings.warn("positional distance tie-breaking is deprecated", DeprecationWarning)
if len(funcs) == 1:
return funcs.pop()
raise DispatchError(f"{self.__name__}: {len(keys)} methods found", types, keys)
Expand Down
14 changes: 3 additions & 11 deletions tests/test_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ def test_join():
assert join(seq, sep) == '0<>[1]<>2'
assert join(tree(seq), sep) == '0<>1<>2'
assert join(seq, bracket(*sep)) == '<0><[1]><2>'
with pytest.warns(DeprecationWarning):
with pytest.raises(DispatchError):
assert join(tree(seq), bracket(*sep)) == '<0><1><2>'
join[tree, bracket] = join[tree, object]
assert join(tree(seq), bracket(*sep)) == '<0><1><2>'


def subclass(*bases, **kwds):
Expand Down Expand Up @@ -83,19 +85,9 @@ def test_signature():
assert signature([Any, list, NewType('', int)]) == (object, list, int)
assert signature([AnyStr]) == signature([Union[bytes, str]])
assert signature([TypeVar('T')]) == signature([object])
assert signature([int]) - signature([Union[int, float]]) == (0,)
assert signature([list]) <= (list,)
assert signature([list]) <= signature([list])
assert signature([list]) <= signature([list[int]])
assert signature([list[int]]) - signature([list])
assert signature([list]) - signature([list[int]]) == (1,)

# with metaclasses:
assert signature([type]) - (type,) == (0,)
assert (type,) - signature([object]) == (1,)
# using EnumMeta because it is a standard, stable, metaclass
assert signature([enum.EnumMeta]) - signature([object]) == (2,)
assert signature([Union[type, enum.EnumMeta]]) - signature([object]) == (1,)


class namespace:
Expand Down
3 changes: 1 addition & 2 deletions tests/test_subscripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from array import array
from collections.abc import Callable, Iterable, Mapping, Sequence
from typing import Generic, Literal, Type, TypeVar, Union
from multimethod import distance, multimethod, parametric, subtype, DispatchError
from multimethod import multimethod, parametric, subtype, DispatchError


def test_literals():
Expand Down Expand Up @@ -65,7 +65,6 @@ class cls(Generic[TypeVar('T')]):
def func(x: cls[int]):
pass

assert distance(object, cls[int])
obj = cls[int]()
assert isinstance(obj, subtype(cls[int]))
assert func(obj) is None
Expand Down

0 comments on commit 6ca918c

Please sign in to comment.