https://github.com/python/cpython/commit/25518f51dc9fd3ffe4f8ae5d53baa3728936be2b
commit: 25518f51dc9fd3ffe4f8ae5d53baa3728936be2b
branch: main
author: Sergey Miryanov <sergey.mirya...@gmail.com>
committer: nascheme <nas-git...@arctrix.com>
date: 2025-08-07T17:45:33-07:00
summary:

GH-135552: Add tests to check weakref clearing (GH-136304)

These are tests to ensure behaviour introduced by GH-136189 is working as 
expected.

Co-authored-by: Mikhail Borisov <43937008+fxeqxmu...@users.noreply.github.com>
Co-authored-by: Kumar Aditya <kumaradi...@python.org>
Co-authored-by: Neil Schemenauer <nas-git...@arctrix.com>

files:
M Lib/test/test_gc.py
M Lib/test/test_weakref.py

diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index 3ec211531c4c70..7c9adf3049a131 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1155,6 +1155,37 @@ def test_something(self):
         """)
         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")
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 4c7c900eb56ae1..47f6b46061ac30 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -1044,6 +1044,32 @@ 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 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):
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to