Updated backport of singledispatchmethod (supports classmethod).
@@ -3,13 +3,37 @@ try:
except ImportError:
from functools import singledispatch, update_wrapper
- # noinspection SpellCheckingInspection
- def singledispatchmethod(func):
- dispatcher = singledispatch(func)
+ class singledispatchmethod:
+ """Single-dispatch generic method descriptor.
- def wrapper(*args, **kwargs):
- return dispatcher.dispatch(args[1].__class__)(*args, **kwargs)
+ Supports wrapping existing descriptors and handles non-descriptor
+ callables as instance methods.
+ """
- wrapper.register = dispatcher.register
- update_wrapper(wrapper, func)
- return wrapper
+ def __init__(self, func):
+ if not callable(func) and not hasattr(func, "__get__"):
+ raise TypeError(f"{func!r} is not callable or a descriptor")
+
+ self.dispatcher = singledispatch(func)
+ self.func = func
+
+ def register(self, cls, method=None):
+ """generic_method.register(cls, func) -> func
+
+ Registers a new implementation for the given *cls* on a *generic_method*.
+ """
+ return self.dispatcher.register(cls, func=method)
+
+ def __get__(self, obj, cls=None):
+ def _method(*args, **kwargs):
+ method = self.dispatcher.dispatch(args[0].__class__)
+ return method.__get__(obj, cls)(*args, **kwargs)
+
+ _method.__isabstractmethod__ = self.__isabstractmethod__
+ _method.register = self.register
+ update_wrapper(_method, self.func)
+ return _method
+
+ @property
+ def __isabstractmethod__(self):
+ return getattr(self.func, "__isabstractmethod__", False)
