Skip to content

Commit e63ca56

Browse files
[3.12] gh-127750: Backport some tests for singledispatchmethod (GH-130309) (GH-130340) (GH-130341)
(cherry picked from commit 68c57d6) (cherry picked from commit 395335d) (cherry picked from commit 10b3205) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 96a914a commit e63ca56

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

Lib/test/test_functools.py

+111
Original file line numberDiff line numberDiff line change
@@ -2642,6 +2642,7 @@ def static_func(arg: int) -> str:
26422642
"""My function docstring"""
26432643
return str(arg)
26442644

2645+
prefix = A.__qualname__ + '.'
26452646
for meth in (
26462647
A.func,
26472648
A().func,
@@ -2651,6 +2652,7 @@ def static_func(arg: int) -> str:
26512652
A().static_func
26522653
):
26532654
with self.subTest(meth=meth):
2655+
self.assertEqual(meth.__qualname__, prefix + meth.__name__)
26542656
self.assertEqual(meth.__doc__,
26552657
('My function docstring'
26562658
if support.HAVE_DOCSTRINGS
@@ -2946,6 +2948,115 @@ def _(arg: typing.List[float] | bytes):
29462948
self.assertEqual(f(""), "default")
29472949
self.assertEqual(f(b""), "default")
29482950

2951+
def test_method_equal_instances(self):
2952+
# gh-127750: Reference to self was cached
2953+
class A:
2954+
def __eq__(self, other):
2955+
return True
2956+
def __hash__(self):
2957+
return 1
2958+
@functools.singledispatchmethod
2959+
def t(self, arg):
2960+
return self
2961+
2962+
a = A()
2963+
b = A()
2964+
self.assertIs(a.t(1), a)
2965+
self.assertIs(b.t(2), b)
2966+
2967+
def test_method_bad_hash(self):
2968+
class A:
2969+
def __eq__(self, other):
2970+
raise AssertionError
2971+
def __hash__(self):
2972+
raise AssertionError
2973+
@functools.singledispatchmethod
2974+
def t(self, arg):
2975+
pass
2976+
2977+
# Should not raise
2978+
A().t(1)
2979+
hash(A().t)
2980+
A().t == A().t
2981+
2982+
def test_method_no_reference_loops(self):
2983+
# gh-127750: Created a strong reference to self
2984+
class A:
2985+
@functools.singledispatchmethod
2986+
def t(self, arg):
2987+
return weakref.ref(self)
2988+
2989+
a = A()
2990+
r = a.t(1)
2991+
self.assertIsNotNone(r())
2992+
del a # delete a after a.t
2993+
if not support.check_impl_detail(cpython=True):
2994+
support.gc_collect()
2995+
self.assertIsNone(r())
2996+
2997+
a = A()
2998+
t = a.t
2999+
del a # delete a before a.t
3000+
support.gc_collect()
3001+
r = t(1)
3002+
self.assertIsNotNone(r())
3003+
del t
3004+
if not support.check_impl_detail(cpython=True):
3005+
support.gc_collect()
3006+
self.assertIsNone(r())
3007+
3008+
def test_signatures(self):
3009+
@functools.singledispatch
3010+
def func(item, arg: int) -> str:
3011+
return str(item)
3012+
@func.register
3013+
def _(item: int, arg: bytes) -> str:
3014+
return str(item)
3015+
3016+
self.assertEqual(str(Signature.from_callable(func)),
3017+
'(item, arg: int) -> str')
3018+
3019+
def test_method_signatures(self):
3020+
class A:
3021+
def m(self, item, arg: int) -> str:
3022+
return str(item)
3023+
@classmethod
3024+
def cm(cls, item, arg: int) -> str:
3025+
return str(item)
3026+
@functools.singledispatchmethod
3027+
def func(self, item, arg: int) -> str:
3028+
return str(item)
3029+
@func.register
3030+
def _(self, item, arg: bytes) -> str:
3031+
return str(item)
3032+
3033+
@functools.singledispatchmethod
3034+
@classmethod
3035+
def cls_func(cls, item, arg: int) -> str:
3036+
return str(arg)
3037+
@func.register
3038+
@classmethod
3039+
def _(cls, item, arg: bytes) -> str:
3040+
return str(item)
3041+
3042+
@functools.singledispatchmethod
3043+
@staticmethod
3044+
def static_func(item, arg: int) -> str:
3045+
return str(arg)
3046+
@func.register
3047+
@staticmethod
3048+
def _(item, arg: bytes) -> str:
3049+
return str(item)
3050+
3051+
self.assertEqual(str(Signature.from_callable(A.func)),
3052+
'(self, item, arg: int) -> str')
3053+
self.assertEqual(str(Signature.from_callable(A().func)),
3054+
'(self, item, arg: int) -> str')
3055+
self.assertEqual(str(Signature.from_callable(A.cls_func)),
3056+
'(cls, item, arg: int) -> str')
3057+
self.assertEqual(str(Signature.from_callable(A.static_func)),
3058+
'(item, arg: int) -> str')
3059+
29493060

29503061
class CachedCostItem:
29513062
_cost = 1

Lib/test/test_inspect/test_inspect.py

+21
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,27 @@ def test_isroutine(self):
362362
self.assertFalse(inspect.isroutine(int))
363363
self.assertFalse(inspect.isroutine(type('some_class', (), {})))
364364

365+
def test_isroutine_singledispatch(self):
366+
self.assertTrue(inspect.isroutine(functools.singledispatch(mod.spam)))
367+
368+
class A:
369+
@functools.singledispatchmethod
370+
def method(self, arg):
371+
pass
372+
@functools.singledispatchmethod
373+
@classmethod
374+
def class_method(cls, arg):
375+
pass
376+
@functools.singledispatchmethod
377+
@staticmethod
378+
def static_method(arg):
379+
pass
380+
381+
self.assertTrue(inspect.isroutine(A.method))
382+
self.assertTrue(inspect.isroutine(A().method))
383+
self.assertTrue(inspect.isroutine(A.static_method))
384+
self.assertTrue(inspect.isroutine(A.class_method))
385+
365386
def test_isclass(self):
366387
self.istest(inspect.isclass, 'mod.StupidGit')
367388
self.assertTrue(inspect.isclass(list))

0 commit comments

Comments
 (0)