coady / multimethod

Multiple argument dispatching.
https://coady.github.io/multimethod
Other
277 stars 24 forks source link

[Feature Request(Maybe)] Support for Ellipsis #17

Closed hzhangxyz closed 3 years ago

hzhangxyz commented 3 years ago

Currently multimethod cannot recognize type contaning ellipsis such as tuple[int, ...] However it is a standard way to declare variable-length tuple ref: https://docs.python.org/3/library/typing.html#typing.Tuple

Maybe add another check in __init__.py:subtype.__subclasscheck__ would help

hzhangxyz commented 3 years ago

It seems change __init__.py:subtype.__subclasscheck__ to this works, but it is really an ugly solution

    def __subclasscheck__(self, subclass):
        origin = getattr(subclass, '__extra__', getattr(subclass, '__origin__', subclass))
        args = getattr(subclass, '__args__', ())
        if origin is typing.Union:
            return all(issubclass(cls, self) for cls in args)
        if self.__origin__ is typing.Union:
            return issubclass(subclass, self.__args__)
        issubclass_fixed = lambda x, y : x == y == Ellipsis or (x != Ellipsis and y != Ellipsis and issubclass(x, y))
        return (  # check args first to avoid a recursion error in ABCMeta
            len(args) == len(self.__args__)
            and issubclass(origin, self.__origin__)
            and all(map(issubclass_fixed, args, self.__args__))
        )
hzhangxyz commented 3 years ago

The above change cannot solve ellipsis, see

from multimethod import multimethod

@multimethod
def p(a: tuple[tuple[int, int], ...]):
    print(a)

p(((1,2),(3,4)))

it report:

Traceback (most recent call last):
  File "/mnt/d/Home/Downloads/main.py", line 10, in <module>
    p(((1,2),(3,4)))
  File "/usr/lib/python3.9/site-packages/multimethod/__init__.py", line 185, in __call__
    return self[tuple(map(self.get_type, args))](*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/multimethod/__init__.py", line 181, in __missing__
    raise DispatchError(msg, types, keys)
multimethod.DispatchError: ('p: 0 methods found', (<class 'multimethod.<class 'tuple'>'>,), [])
hzhangxyz commented 3 years ago

Maybe it is correct way to update something in class subclass? I don't know about it

coady commented 3 years ago

For this use case, it would be simpler (and faster) to use Iterable or Sequence. But I guess it might as well be supported, to not look like a bug. Thanks.

hzhangxyz commented 3 years ago

In my case, I want tuple[tuple[int, int], ...] to be the key of dict, so it seems Iterable or Sequence not work.

Thanks for solving this.