Skip to content
Merged
31 changes: 31 additions & 0 deletions Lib/test/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,37 @@ def test_something(self):
""")
assert_python_ok("-c", source)

def test_do_not_cleanup_type_subclasses_before_finalization(self):
# https://github.com/python/cpython/issues/135552
# If we cleanup weakrefs for tp_subclasses before calling
# the finalizer (__del__) then the line `fail = BaseNode.next.next`
# should fail because we are trying to access a subclass
# atribute. But subclass type cache was not properly invalidated.
code = """
class BaseNode:
def __del__(self):
BaseNode.next = BaseNode.next.next
fail = BaseNode.next.next

class Node(BaseNode):
pass

BaseNode.next = Node()
BaseNode.next.next = Node()
"""
# this test checks garbage collection while interp
# finalization
assert_python_ok("-c", textwrap.dedent(code))

code_inside_function = textwrap.dedent(F"""
def test():
{textwrap.indent(code, ' ')}

test()
""")
# this test checks regular garbage collection
assert_python_ok("-c", code_inside_function)


class IncrementalGCTests(unittest.TestCase):
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,38 @@ def callback(obj):
stderr = res.err.decode("ascii", "backslashreplace")
self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'")

def test_clearing_weakrefs_in_gc(self):
# This test checks that:
# 1. weakrefs for types with callbacks are cleared before the
# finalizer is called
# 2. weakrefs for types without callbacks are cleared after the
# finalizer is called
# 3. other weakrefs cleared before the finalizer is called
errors = []
def test():
class Class:
def __init__(self):
self._self = self
self.wr1 = weakref.ref(Class, lambda x: None)
self.wr2 = weakref.ref(Class)
self.wr3 = weakref.ref(self)

def __del__(self):
# we can't use assert* here, because gc will swallow
# exceptions
if self.wr1() is not None:
errors.append("Type weakref with callback is not None")
if self.wr2() is not Class:
errors.append("Type weakref is not Class")
if self.wr3() is not None:
errors.append("Instance weakref is not None")

Class()

test()
gc.collect()
self.assertEqual(errors, [])


class SubclassableWeakrefTestCase(TestBase):

Expand Down
Loading