Skip to content

Commit 68c57d6

Browse files
[3.13] gh-127750: Backport some tests for singledispatchmethod (GH-130309) (GH-130340)
(cherry picked from commit 395335d) (cherry picked from commit 10b3205)
1 parent 270ec26 commit 68c57d6

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
@@ -2758,6 +2758,7 @@ def static_func(arg: int) -> str:
27582758
"""My function docstring"""
27592759
return str(arg)
27602760

2761+
prefix = A.__qualname__ + '.'
27612762
for meth in (
27622763
A.func,
27632764
A().func,
@@ -2767,6 +2768,7 @@ def static_func(arg: int) -> str:
27672768
A().static_func
27682769
):
27692770
with self.subTest(meth=meth):
2771+
self.assertEqual(meth.__qualname__, prefix + meth.__name__)
27702772
self.assertEqual(meth.__doc__,
27712773
('My function docstring'
27722774
if support.HAVE_DOCSTRINGS
@@ -3077,6 +3079,115 @@ def _(arg: typing.List[float] | bytes):
30773079
self.assertEqual(f(""), "default")
30783080
self.assertEqual(f(b""), "default")
30793081

3082+
def test_method_equal_instances(self):
3083+
# gh-127750: Reference to self was cached
3084+
class A:
3085+
def __eq__(self, other):
3086+
return True
3087+
def __hash__(self):
3088+
return 1
3089+
@functools.singledispatchmethod
3090+
def t(self, arg):
3091+
return self
3092+
3093+
a = A()
3094+
b = A()
3095+
self.assertIs(a.t(1), a)
3096+
self.assertIs(b.t(2), b)
3097+
3098+
def test_method_bad_hash(self):
3099+
class A:
3100+
def __eq__(self, other):
3101+
raise AssertionError
3102+
def __hash__(self):
3103+
raise AssertionError
3104+
@functools.singledispatchmethod
3105+
def t(self, arg):
3106+
pass
3107+
3108+
# Should not raise
3109+
A().t(1)
3110+
hash(A().t)
3111+
A().t == A().t
3112+
3113+
def test_method_no_reference_loops(self):
3114+
# gh-127750: Created a strong reference to self
3115+
class A:
3116+
@functools.singledispatchmethod
3117+
def t(self, arg):
3118+
return weakref.ref(self)
3119+
3120+
a = A()
3121+
r = a.t(1)
3122+
self.assertIsNotNone(r())
3123+
del a # delete a after a.t
3124+
if not support.check_impl_detail(cpython=True):
3125+
support.gc_collect()
3126+
self.assertIsNone(r())
3127+
3128+
a = A()
3129+
t = a.t
3130+
del a # delete a before a.t
3131+
support.gc_collect()
3132+
r = t(1)
3133+
self.assertIsNotNone(r())
3134+
del t
3135+
if not support.check_impl_detail(cpython=True):
3136+
support.gc_collect()
3137+
self.assertIsNone(r())
3138+
3139+
def test_signatures(self):
3140+
@functools.singledispatch
3141+
def func(item, arg: int) -> str:
3142+
return str(item)
3143+
@func.register
3144+
def _(item: int, arg: bytes) -> str:
3145+
return str(item)
3146+
3147+
self.assertEqual(str(Signature.from_callable(func)),
3148+
'(item, arg: int) -> str')
3149+
3150+
def test_method_signatures(self):
3151+
class A:
3152+
def m(self, item, arg: int) -> str:
3153+
return str(item)
3154+
@classmethod
3155+
def cm(cls, item, arg: int) -> str:
3156+
return str(item)
3157+
@functools.singledispatchmethod
3158+
def func(self, item, arg: int) -> str:
3159+
return str(item)
3160+
@func.register
3161+
def _(self, item, arg: bytes) -> str:
3162+
return str(item)
3163+
3164+
@functools.singledispatchmethod
3165+
@classmethod
3166+
def cls_func(cls, item, arg: int) -> str:
3167+
return str(arg)
3168+
@func.register
3169+
@classmethod
3170+
def _(cls, item, arg: bytes) -> str:
3171+
return str(item)
3172+
3173+
@functools.singledispatchmethod
3174+
@staticmethod
3175+
def static_func(item, arg: int) -> str:
3176+
return str(arg)
3177+
@func.register
3178+
@staticmethod
3179+
def _(item, arg: bytes) -> str:
3180+
return str(item)
3181+
3182+
self.assertEqual(str(Signature.from_callable(A.func)),
3183+
'(self, item, arg: int) -> str')
3184+
self.assertEqual(str(Signature.from_callable(A().func)),
3185+
'(self, item, arg: int) -> str')
3186+
self.assertEqual(str(Signature.from_callable(A.cls_func)),
3187+
'(cls, item, arg: int) -> str')
3188+
self.assertEqual(str(Signature.from_callable(A.static_func)),
3189+
'(item, arg: int) -> str')
3190+
30803191

30813192
class CachedCostItem:
30823193
_cost = 1

Lib/test/test_inspect/test_inspect.py

+21
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,27 @@ def test_isroutine(self):
408408
# partial
409409
self.assertFalse(inspect.isroutine(functools.partial(mod.spam)))
410410

411+
def test_isroutine_singledispatch(self):
412+
self.assertTrue(inspect.isroutine(functools.singledispatch(mod.spam)))
413+
414+
class A:
415+
@functools.singledispatchmethod
416+
def method(self, arg):
417+
pass
418+
@functools.singledispatchmethod
419+
@classmethod
420+
def class_method(cls, arg):
421+
pass
422+
@functools.singledispatchmethod
423+
@staticmethod
424+
def static_method(arg):
425+
pass
426+
427+
self.assertTrue(inspect.isroutine(A.method))
428+
self.assertTrue(inspect.isroutine(A().method))
429+
self.assertTrue(inspect.isroutine(A.static_method))
430+
self.assertTrue(inspect.isroutine(A.class_method))
431+
411432
def test_isclass(self):
412433
self.istest(inspect.isclass, 'mod.StupidGit')
413434
self.assertTrue(inspect.isclass(list))

0 commit comments

Comments
 (0)