https://github.com/python/cpython/commit/43fa76638fc75958b592096b6830c15f0afa1a73
commit: 43fa76638fc75958b592096b6830c15f0afa1a73
branch: main
author: mpage <[email protected]>
committer: colesbury <[email protected]>
date: 2024-04-29T16:56:51Z
summary:

gh-118331: Don't raise an error if tuple allocation fails when clearing 
weakrefs (#118338)

It's not safe to raise an exception in `PyObject_ClearWeakRefs()` if one
is not already set, since it may be called by `_Py_Dealloc()`, which
requires that the active exception does not change.

Additionally, make sure we clear the weakrefs even when tuple allocation
fails.

files:
M Lib/test/test_weakref.py
M Objects/weakrefobject.c

diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index 499ba77fd19542..a2f5b9b2902d36 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -10,6 +10,7 @@
 import threading
 import time
 import random
+import textwrap
 
 from test import support
 from test.support import script_helper, ALWAYS_EQ
@@ -1009,6 +1010,31 @@ def __del__(self): pass
         del x
         support.gc_collect()
 
+    @support.cpython_only
+    def test_no_memory_when_clearing(self):
+        # gh-118331: Make sure we do not raise an exception from the destructor
+        # when clearing weakrefs if allocating the intermediate tuple fails.
+        code = textwrap.dedent("""
+        import _testcapi
+        import weakref
+
+        class TestObj:
+            pass
+
+        def callback(obj):
+            pass
+
+        obj = TestObj()
+        # The choice of 50 is arbitrary, but must be large enough to ensure
+        # the allocation won't be serviced by the free list.
+        wrs = [weakref.ref(obj, callback) for _ in range(50)]
+        _testcapi.set_nomemory(0)
+        del obj
+        """).strip()
+        res, _ = script_helper.run_python_until_end("-c", code)
+        stderr = res.err.decode("ascii", "backslashreplace")
+        self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 
'TestObj'")
+
 
 class SubclassableWeakrefTestCase(TestBase):
 
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 206107e8505dc7..93c5fe3aacecfd 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -1016,7 +1016,9 @@ PyObject_ClearWeakRefs(PyObject *object)
     PyObject *exc = PyErr_GetRaisedException();
     PyObject *tuple = PyTuple_New(num_weakrefs * 2);
     if (tuple == NULL) {
-        _PyErr_ChainExceptions1(exc);
+        _PyWeakref_ClearWeakRefsExceptCallbacks(object);
+        PyErr_WriteUnraisable(NULL);
+        PyErr_SetRaisedException(exc);
         return;
     }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to