Skip to content

[3.13] gh-127750: Backport some tests for singledispatchmethod (GH-130309) #130340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2758,6 +2758,7 @@ def static_func(arg: int) -> str:
"""My function docstring"""
return str(arg)

prefix = A.__qualname__ + '.'
for meth in (
A.func,
A().func,
Expand All @@ -2767,6 +2768,7 @@ def static_func(arg: int) -> str:
A().static_func
):
with self.subTest(meth=meth):
self.assertEqual(meth.__qualname__, prefix + meth.__name__)
self.assertEqual(meth.__doc__,
('My function docstring'
if support.HAVE_DOCSTRINGS
Expand Down Expand Up @@ -3077,6 +3079,115 @@ def _(arg: typing.List[float] | bytes):
self.assertEqual(f(""), "default")
self.assertEqual(f(b""), "default")

def test_method_equal_instances(self):
# gh-127750: Reference to self was cached
class A:
def __eq__(self, other):
return True
def __hash__(self):
return 1
@functools.singledispatchmethod
def t(self, arg):
return self

a = A()
b = A()
self.assertIs(a.t(1), a)
self.assertIs(b.t(2), b)

def test_method_bad_hash(self):
class A:
def __eq__(self, other):
raise AssertionError
def __hash__(self):
raise AssertionError
@functools.singledispatchmethod
def t(self, arg):
pass

# Should not raise
A().t(1)
hash(A().t)
A().t == A().t

def test_method_no_reference_loops(self):
# gh-127750: Created a strong reference to self
class A:
@functools.singledispatchmethod
def t(self, arg):
return weakref.ref(self)

a = A()
r = a.t(1)
self.assertIsNotNone(r())
del a # delete a after a.t
if not support.check_impl_detail(cpython=True):
support.gc_collect()
self.assertIsNone(r())

a = A()
t = a.t
del a # delete a before a.t
support.gc_collect()
r = t(1)
self.assertIsNotNone(r())
del t
if not support.check_impl_detail(cpython=True):
support.gc_collect()
self.assertIsNone(r())

def test_signatures(self):
@functools.singledispatch
def func(item, arg: int) -> str:
return str(item)
@func.register
def _(item: int, arg: bytes) -> str:
return str(item)

self.assertEqual(str(Signature.from_callable(func)),
'(item, arg: int) -> str')

def test_method_signatures(self):
class A:
def m(self, item, arg: int) -> str:
return str(item)
@classmethod
def cm(cls, item, arg: int) -> str:
return str(item)
@functools.singledispatchmethod
def func(self, item, arg: int) -> str:
return str(item)
@func.register
def _(self, item, arg: bytes) -> str:
return str(item)

@functools.singledispatchmethod
@classmethod
def cls_func(cls, item, arg: int) -> str:
return str(arg)
@func.register
@classmethod
def _(cls, item, arg: bytes) -> str:
return str(item)

@functools.singledispatchmethod
@staticmethod
def static_func(item, arg: int) -> str:
return str(arg)
@func.register
@staticmethod
def _(item, arg: bytes) -> str:
return str(item)

self.assertEqual(str(Signature.from_callable(A.func)),
'(self, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A().func)),
'(self, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A.cls_func)),
'(cls, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A.static_func)),
'(item, arg: int) -> str')


class CachedCostItem:
_cost = 1
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,27 @@ def test_isroutine(self):
# partial
self.assertFalse(inspect.isroutine(functools.partial(mod.spam)))

def test_isroutine_singledispatch(self):
self.assertTrue(inspect.isroutine(functools.singledispatch(mod.spam)))

class A:
@functools.singledispatchmethod
def method(self, arg):
pass
@functools.singledispatchmethod
@classmethod
def class_method(cls, arg):
pass
@functools.singledispatchmethod
@staticmethod
def static_method(arg):
pass

self.assertTrue(inspect.isroutine(A.method))
self.assertTrue(inspect.isroutine(A().method))
self.assertTrue(inspect.isroutine(A.static_method))
self.assertTrue(inspect.isroutine(A.class_method))

def test_isclass(self):
self.istest(inspect.isclass, 'mod.StupidGit')
self.assertTrue(inspect.isclass(list))
Expand Down
Loading