Skip to content

Commit 43fa766

Browse files
authored
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.
1 parent 444ac0b commit 43fa766

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

Lib/test/test_weakref.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import threading
1111
import time
1212
import random
13+
import textwrap
1314

1415
from test import support
1516
from test.support import script_helper, ALWAYS_EQ
@@ -1009,6 +1010,31 @@ def __del__(self): pass
10091010
del x
10101011
support.gc_collect()
10111012

1013+
@support.cpython_only
1014+
def test_no_memory_when_clearing(self):
1015+
# gh-118331: Make sure we do not raise an exception from the destructor
1016+
# when clearing weakrefs if allocating the intermediate tuple fails.
1017+
code = textwrap.dedent("""
1018+
import _testcapi
1019+
import weakref
1020+
1021+
class TestObj:
1022+
pass
1023+
1024+
def callback(obj):
1025+
pass
1026+
1027+
obj = TestObj()
1028+
# The choice of 50 is arbitrary, but must be large enough to ensure
1029+
# the allocation won't be serviced by the free list.
1030+
wrs = [weakref.ref(obj, callback) for _ in range(50)]
1031+
_testcapi.set_nomemory(0)
1032+
del obj
1033+
""").strip()
1034+
res, _ = script_helper.run_python_until_end("-c", code)
1035+
stderr = res.err.decode("ascii", "backslashreplace")
1036+
self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'")
1037+
10121038

10131039
class SubclassableWeakrefTestCase(TestBase):
10141040

Objects/weakrefobject.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,9 @@ PyObject_ClearWeakRefs(PyObject *object)
10161016
PyObject *exc = PyErr_GetRaisedException();
10171017
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
10181018
if (tuple == NULL) {
1019-
_PyErr_ChainExceptions1(exc);
1019+
_PyWeakref_ClearWeakRefsExceptCallbacks(object);
1020+
PyErr_WriteUnraisable(NULL);
1021+
PyErr_SetRaisedException(exc);
10201022
return;
10211023
}
10221024

0 commit comments

Comments
 (0)