""")
assert_python_ok("-c", source)
+ def test_do_not_cleanup_type_subclasses_before_finalization(self):
+ # See 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
+ # attribute. 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")
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 when finalizers are called:
+ # 1. weakrefs with callbacks have been cleared
+ # 2. weakrefs without callbacks have not been cleared
+ errors = []
+ def test():
+ class Class:
+ def __init__(self):
+ self._self = self
+ self.wr1 = weakref.ref(Class, lambda x: None)
+ self.wr2 = weakref.ref(Class)
+
+ def __del__(self):
+ # we can't use assert* here, because gc will swallow
+ # exceptions
+ if self.wr1() is not None:
+ errors.append("weakref with callback as cleared")
+ if self.wr2() is not Class:
+ errors.append("weakref without callback was cleared")
+
+ Class()
+
+ test()
+ gc.collect()
+ self.assertEqual(errors, [])
+
class SubclassableWeakrefTestCase(TestBase):