Skip to content

Commit

Permalink
Removed overload.
Browse files Browse the repository at this point in the history
  • Loading branch information
coady committed Sep 16, 2024
1 parent e85ea7d commit e68aac3
Show file tree
Hide file tree
Showing 4 changed files with 4 additions and 143 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## Unreleased
### Removed
* Resolving ambiguity using positional distance
* `overload` is redundant with instance checking

## [1.12](https://pypi.org/project/multimethod/1.12/) - 2024-07-04
### Changed
Expand Down
12 changes: 0 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,6 @@ Coroutine = parametric(Callable, asyncio.iscoroutinefunction)
IntArray = parametric(array, typecode='i')
```

`overload` used to dispatch on annotated predicate functions. It is deprecated because a custom instance check - including using `parametric` - offers the same functionality. Any predicate function can be wrapped with the closest matching base class, including `object` if necessary.

```python
Cls = parametric(object, predicate)
Digits = parametric(str, str.isdigit)
assert isinstance('0', Digits)
assert not isinstance('a', Digits)

@meth.register
def _(arg: Digits): ...
```

### classes
`classmethod` and `staticmethod` may be used with a multimethod, but must be applied _last_, i.e., wrapping the final multimethod definition after all functions are registered. For class and instance methods, `cls` and `self` participate in the dispatch as usual. They may be left blank when using annotations, otherwise use `object` as a placeholder.

Expand Down
66 changes: 3 additions & 63 deletions multimethod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import itertools
import types
import typing
import warnings
from collections.abc import Callable, Iterable, Iterator, Mapping
from typing import Any, Literal, Optional, TypeVar, Union, get_type_hints, overload as tp_overload
from typing import Any, Literal, Optional, TypeVar, Union, get_type_hints, overload


class DispatchError(TypeError):
Expand Down Expand Up @@ -259,10 +258,10 @@ def __init__(self, func: Callable):
except (NameError, AttributeError):
self.pending.add(func)

@tp_overload
@overload
def register(self, __func: REGISTERED) -> REGISTERED: ... # pragma: no cover

@tp_overload
@overload
def register(self, *args: type) -> Callable[[REGISTERED], REGISTERED]: ... # pragma: no cover

def register(self, *args) -> Callable:
Expand Down Expand Up @@ -411,65 +410,6 @@ def __call__(self, *args: Any, **kwargs: Any) -> RETURN:
return func(*args, **kwargs)


def isa(*types: type) -> Callable:
"""Partially bound `isinstance`."""
types = tuple(map(subtype, types))
return lambda arg: isinstance(arg, types)


class overload(dict):
"""Ordered functions which dispatch based on their annotated predicates."""

pending: set
__get__ = multimethod.__get__

def __new__(cls, func):
namespace = inspect.currentframe().f_back.f_locals
if func.__name__ not in namespace:
warnings.warn("use `parametric(<base>, <func>)` as a type instead", DeprecationWarning)
self = functools.update_wrapper(super().__new__(cls), func)
self.pending = set()
return namespace.get(func.__name__, self)

def __init__(self, func: Callable):
try:
sig = self.signature(func)
except (NameError, AttributeError):
self.pending.add(func)
else:
self[sig] = func

@classmethod
def signature(cls, func: Callable) -> inspect.Signature:
for name, value in get_type_hints(func).items():
if not callable(value) or isinstance(value, type) or hasattr(value, '__origin__'):
func.__annotations__[name] = isa(value)
return inspect.signature(func)

def __call__(self, *args, **kwargs):
"""Dispatch to first matching function."""
while self.pending:
func = self.pending.pop()
self[self.signature(func)] = func
for sig in reversed(self):
try:
arguments = sig.bind(*args, **kwargs).arguments
except TypeError:
continue
if all(
param.annotation is param.empty or param.annotation(arguments[name])
for name, param in sig.parameters.items()
if name in arguments
):
return self[sig](*args, **kwargs)
raise DispatchError("No matching functions found")

def register(self, func: Callable) -> Callable:
"""Decorator for registering a function."""
self.__init__(func) # type: ignore
return self if self.__name__ == func.__name__ else func # type: ignore


class multimeta(type):
"""Convert all callables in namespace to multimethods."""

Expand Down
68 changes: 0 additions & 68 deletions tests/test_overload.py

This file was deleted.

0 comments on commit e68aac3

Please sign in to comment.