From ed69da6432f1ce63d871b643dc1672cca894794f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 18 Oct 2021 16:44:43 +0100 Subject: [PATCH 1/2] bpo-39679: Add tests for classmethod and staticmethod singledispatchmethods In Python 3.8 and 3.9, 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)`. This was not caught by unit tests, however, as the tests only tested the `@method.register(int)` way of registering additional implementations. The bug is no longer present in Python 3.10+, but `test_functools.py` is still lacking regression tests for these cases. This commit adds these test cases. --- Lib/test/test_functools.py | 42 +++++++++++++++++++ .../2021-10-18-16-18-41.bpo-39679.F18qcE.rst | 2 + 2 files changed, 44 insertions(+) create mode 100644 Misc/NEWS.d/next/Tests/2021-10-18-16-18-41.bpo-39679.F18qcE.rst diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index fece8256a3e261..3b8379b43d56b8 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2437,6 +2437,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/Tests/2021-10-18-16-18-41.bpo-39679.F18qcE.rst b/Misc/NEWS.d/next/Tests/2021-10-18-16-18-41.bpo-39679.F18qcE.rst new file mode 100644 index 00000000000000..b0d1b686392ef8 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2021-10-18-16-18-41.bpo-39679.F18qcE.rst @@ -0,0 +1,2 @@ +Add more test cases for `@functools.singledispatchmethod` when combined with +`@classmethod` or `@staticmethod`. From 14e032c41248fa67039a6e5d909241872875c4c8 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 19 Oct 2021 16:01:19 +0100 Subject: [PATCH 2/2] Correct typo --- Lib/test/test_functools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 3b8379b43d56b8..bdb4ddcc60cac2 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2437,7 +2437,7 @@ def _(self, arg: str): self.assertEqual(a.t(''), "str") self.assertEqual(a.t(0.0), "base") - def test_staticmethod__type_ann_register(self): + def test_staticmethod_type_ann_register(self): class A: @functools.singledispatchmethod @staticmethod