[Python-checkins] [3.9] bpo-39679: Fix `singledispatchmethod` `classmethod`/`staticmethod` bug (GH-29087)
ambv
webhook-mailer at python.org
Thu Oct 28 12:02:12 EDT 2021
https://github.com/python/cpython/commit/97388c204b557f30e48a2b2ef826868702204cf2
commit: 97388c204b557f30e48a2b2ef826868702204cf2
branch: 3.9
author: Alex Waygood <Alex.Waygood at Gmail.com>
committer: ambv <lukasz at langa.pl>
date: 2021-10-28T18:02:04+02:00
summary:
[3.9] bpo-39679: Fix `singledispatchmethod` `classmethod`/`staticmethod` bug (GH-29087)
This commit fixes a bug in the 3.9 branch where stacking
`@functools.singledispatchmethod` on top of `@classmethod` or `@staticmethod`
caused an exception to be raised if the method was registered using
type-annotations rather than `@method.register(int)`. Tests for this scenario
were added to the 3.11 and 3.10 branches in #29034 and #29072; this commit
also backports those tests to the 3.9 branch.
Co-authored-by: Yurii Karabas <1998uriyyo at gmail.com>
Co-authored-by: Łukasz Langa <lukasz at langa.pl>
files:
A Misc/NEWS.d/next/Library/2021-10-20-10-07-44.bpo-39679.nVYJJ3.rst
M Lib/functools.py
M Lib/test/test_functools.py
diff --git a/Lib/functools.py b/Lib/functools.py
index 97744a869563d..5054e281ad281 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -906,6 +906,12 @@ def register(self, cls, method=None):
Registers a new implementation for the given *cls* on a *generic_method*.
"""
+ # bpo-39679: in Python <= 3.9, classmethods and staticmethods don't
+ # inherit __annotations__ of the wrapped function (fixed in 3.10+ as
+ # a side-effect of bpo-43682) but we need that for annotation-derived
+ # singledispatches. So we add that just-in-time here.
+ if isinstance(cls, (staticmethod, classmethod)):
+ cls.__annotations__ = getattr(cls.__func__, '__annotations__', {})
return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls=None):
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index 987020ea007fa..96e93ed8eab34 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -2427,6 +2427,48 @@ def _(self, arg: str):
self.assertEqual(a.t(''), "str")
self.assertEqual(a.t(0.0), "base")
+ def test_staticmethod_type_ann_register(self):
+ class A:
+ @functools.singledispatchmethod
+ @staticmethod
+ def t(arg):
+ return arg
+ @t.register
+ @staticmethod
+ def _(arg: int):
+ return isinstance(arg, int)
+ @t.register
+ @staticmethod
+ def _(arg: str):
+ return isinstance(arg, str)
+ a = A()
+
+ self.assertTrue(A.t(0))
+ self.assertTrue(A.t(''))
+ self.assertEqual(A.t(0.0), 0.0)
+
+ def test_classmethod_type_ann_register(self):
+ class A:
+ def __init__(self, arg):
+ self.arg = arg
+
+ @functools.singledispatchmethod
+ @classmethod
+ def t(cls, arg):
+ return cls("base")
+ @t.register
+ @classmethod
+ def _(cls, arg: int):
+ return cls("int")
+ @t.register
+ @classmethod
+ def _(cls, arg: str):
+ return cls("str")
+
+ self.assertEqual(A.t(0).arg, "int")
+ self.assertEqual(A.t('').arg, "str")
+ self.assertEqual(A.t(0.0).arg, "base")
+
def test_invalid_registrations(self):
msg_prefix = "Invalid first argument to `register()`: "
msg_suffix = (
diff --git a/Misc/NEWS.d/next/Library/2021-10-20-10-07-44.bpo-39679.nVYJJ3.rst b/Misc/NEWS.d/next/Library/2021-10-20-10-07-44.bpo-39679.nVYJJ3.rst
new file mode 100644
index 0000000000000..b0656aac51677
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-10-20-10-07-44.bpo-39679.nVYJJ3.rst
@@ -0,0 +1,3 @@
+Fix bug in :class:`functools.singledispatchmethod` that caused it to fail
+when attempting to register a :func:`classmethod` or :func:`staticmethod`
+using type annotations. Patch contributed by Alex Waygood.
More information about the Python-checkins
mailing list