gh-140873: Add support of non-descriptor callables in functools.singledispatchmethod() (GH-140884)
This commit is contained in:
@@ -672,7 +672,7 @@ The :mod:`functools` module defines the following functions:
|
||||
dispatch>` :term:`generic function`.
|
||||
|
||||
To define a generic method, decorate it with the ``@singledispatchmethod``
|
||||
decorator. When defining a function using ``@singledispatchmethod``, note
|
||||
decorator. When defining a method using ``@singledispatchmethod``, note
|
||||
that the dispatch happens on the type of the first non-*self* or non-*cls*
|
||||
argument::
|
||||
|
||||
@@ -716,6 +716,9 @@ The :mod:`functools` module defines the following functions:
|
||||
|
||||
.. versionadded:: 3.8
|
||||
|
||||
.. versionchanged:: next
|
||||
Added support of non-:term:`descriptor` callables.
|
||||
|
||||
|
||||
.. function:: update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
|
||||
|
||||
|
||||
@@ -498,6 +498,14 @@ difflib
|
||||
(Contributed by Jiahao Li in :gh:`134580`.)
|
||||
|
||||
|
||||
functools
|
||||
---------
|
||||
|
||||
* :func:`~functools.singledispatchmethod` now supports non-:term:`descriptor`
|
||||
callables.
|
||||
(Contributed by Serhiy Storchaka in :gh:`140873`.)
|
||||
|
||||
|
||||
hashlib
|
||||
-------
|
||||
|
||||
|
||||
@@ -1083,7 +1083,10 @@ class _singledispatchmethod_get:
|
||||
'singledispatchmethod method')
|
||||
raise TypeError(f'{funcname} requires at least '
|
||||
'1 positional argument')
|
||||
return self._dispatch(args[0].__class__).__get__(self._obj, self._cls)(*args, **kwargs)
|
||||
method = self._dispatch(args[0].__class__)
|
||||
if hasattr(method, "__get__"):
|
||||
method = method.__get__(self._obj, self._cls)
|
||||
return method(*args, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Resolve these attributes lazily to speed up creation of
|
||||
|
||||
@@ -2785,7 +2785,7 @@ class TestSingleDispatch(unittest.TestCase):
|
||||
@functools.singledispatchmethod
|
||||
@classmethod
|
||||
def go(cls, item, arg):
|
||||
pass
|
||||
return item - arg
|
||||
|
||||
@go.register
|
||||
@classmethod
|
||||
@@ -2794,7 +2794,9 @@ class TestSingleDispatch(unittest.TestCase):
|
||||
|
||||
s = Slot()
|
||||
self.assertEqual(s.go(1, 1), 2)
|
||||
self.assertEqual(s.go(1.5, 1), 0.5)
|
||||
self.assertEqual(Slot.go(1, 1), 2)
|
||||
self.assertEqual(Slot.go(1.5, 1), 0.5)
|
||||
|
||||
def test_staticmethod_slotted_class(self):
|
||||
class A:
|
||||
@@ -3485,6 +3487,37 @@ class TestSingleDispatch(unittest.TestCase):
|
||||
self.assertEqual(str(Signature.from_callable(A.static_func)),
|
||||
'(item, arg: int) -> str')
|
||||
|
||||
def test_method_non_descriptor(self):
|
||||
class Callable:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __call__(self, arg):
|
||||
return self.value, arg
|
||||
|
||||
class A:
|
||||
t = functools.singledispatchmethod(Callable('general'))
|
||||
t.register(int, Callable('special'))
|
||||
|
||||
@functools.singledispatchmethod
|
||||
def u(self, arg):
|
||||
return 'general', arg
|
||||
u.register(int, Callable('special'))
|
||||
|
||||
v = functools.singledispatchmethod(Callable('general'))
|
||||
@v.register(int)
|
||||
def _(self, arg):
|
||||
return 'special', arg
|
||||
|
||||
a = A()
|
||||
self.assertEqual(a.t(0), ('special', 0))
|
||||
self.assertEqual(a.t(2.5), ('general', 2.5))
|
||||
self.assertEqual(A.t(0), ('special', 0))
|
||||
self.assertEqual(A.t(2.5), ('general', 2.5))
|
||||
self.assertEqual(a.u(0), ('special', 0))
|
||||
self.assertEqual(a.u(2.5), ('general', 2.5))
|
||||
self.assertEqual(a.v(0), ('special', 0))
|
||||
self.assertEqual(a.v(2.5), ('general', 2.5))
|
||||
|
||||
|
||||
class CachedCostItem:
|
||||
_cost = 1
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Add support of non-:term:`descriptor` callables in
|
||||
:func:`functools.singledispatchmethod`.
|
||||
Reference in New Issue
Block a user