Skip to content

Commit

Permalink
Callable type checker and metaclass.
Browse files Browse the repository at this point in the history
Kept `get_type` kept because it is useful as a static method. Using `ABCMeta` and inheritance obviates the need for explicit registration.
  • Loading branch information
coady committed Sep 21, 2023
1 parent 2dd2615 commit 41e3eed
Showing 1 changed file with 10 additions and 8 deletions.
18 changes: 10 additions & 8 deletions multimethod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class DispatchError(TypeError):
pass


class subtype(type):
class subtype(abc.ABCMeta):
"""A normalized generic type which checks subscripts.
Transforms a generic alias into a concrete type which supports `issubclass`.
Expand All @@ -42,7 +42,7 @@ def __new__(cls, tp, *args):
args = tuple(map(cls, getattr(tp, '__args__', args)))
if set(args) <= {object} and not (origin is tuple and args):
return origin
bases = (origin,) if type(origin) is type else ()
bases = (origin,) if type(origin) in (type, abc.ABCMeta) else ()
if origin is Literal:
bases = (subtype(Union[tuple(map(type, args))]),)
if origin is Callable.__origin__ and args[:1] == (...,):
Expand All @@ -51,8 +51,7 @@ def __new__(cls, tp, *args):
return type.__new__(cls, str(tp), bases, namespace)

def __init__(self, tp, *args):
if isinstance(self.__origin__, abc.ABCMeta):
self.__origin__.register(self)
...

def key(self) -> tuple:
return self.__origin__, *self.__args__
Expand Down Expand Up @@ -147,6 +146,8 @@ def get_type(self, arg) -> type:
except TypeError: # not an acceptable base type
return subtype(self.__origin__, *subscripts)

__call__ = get_type


def distance(cls, subclass: type) -> int:
"""Return estimated distance between classes for tie-breaking."""
Expand Down Expand Up @@ -286,10 +287,11 @@ def __setitem__(self, types: tuple, func: Callable):
for index, (cls, type_checker) in enumerate(zip(types, self.type_checkers)):
if subtype.subcheck(cls): # switch to slower generic type checker
if type_checker is not type:
tp = type_checker.__self__
args = {cls} | set(tp.__args__) if tp.__origin__ is Union else {cls, tp}
cls = subtype(Union[tuple(args)])
self.type_checkers[index] = cls.get_type
if issubclass(type_checker, cls):
cls = type_checker
else:
cls = subtype(Union[type_checker, cls])
self.type_checkers[index] = cls
super().__setitem__(types, func)
self.__doc__ = self.docstring

Expand Down

0 comments on commit 41e3eed

Please sign in to comment.