Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Lib/test/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,41 @@ def __init__(self):
self.fail("MemoryError was not raised while detaching "
f"the dictionary: {seen}")

@support.nomemtest
def test_detach_materialized_dict_no_memory_keeps_exception(self):
# A MemoryError raised while detaching the dictionary must not
# replace the exception which is propagating when the object is freed.
import _testcapi

class A:
def __init__(self):
self.a = 1
self.b = 2

def f(lst):
# The object is only referenced from the evaluation stack,
# so it is freed while ZeroDivisionError propagates.
[lst.pop(), 1/0]

seen = []
for n in range(20):
lst = [A()]
d = lst[0].__dict__
with support.catch_unraisable_exception() as ex:
with self.assertRaises((ZeroDivisionError, MemoryError)) as cm:
_testcapi.call_with_nomemory(n, n + 1, f, lst)
if ex.unraisable is None:
continue
err_msg = ex.unraisable.err_msg
seen.append((n, type(cm.exception), err_msg))
if (err_msg ==
'Exception ignored while clearing an object managed dict'):
self.assertIs(type(cm.exception), ZeroDivisionError)
break
else:
self.fail("MemoryError was not raised while detaching "
f"the dictionary: {seen}")

class DefinitionOrderTests(unittest.TestCase):
# PEP 520: Preserving Class Attribute Definition Order

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a :exc:`SystemError` (a fatal error in a debug build) when an object
whose :attr:`~object.__dict__` has been accessed is freed while an exception
propagates and detaching the dictionary fails with :exc:`MemoryError`. The
propagating exception is now kept. Patch by Christian Aurich Zanettini Martins.
4 changes: 4 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -8018,6 +8018,9 @@ PyObject_ClearManagedDict(PyObject *obj)
// We have a materialized object which points at the inline
// values. We need to materialize the keys. Nothing can modify
// this object, but we need to lock the dictionary.
// The object can be freed while an exception is propagating:
// don't let a MemoryError from detaching the dict replace it.
PyObject *exc = PyErr_GetRaisedException();
int err;
Py_BEGIN_CRITICAL_SECTION(dict);
err = detach_dict_from_object(dict, obj);
Expand All @@ -8038,6 +8041,7 @@ PyObject_ClearManagedDict(PyObject *obj)
clear_inline_values(_PyObject_InlineValues(obj));
Py_END_CRITICAL_SECTION();
}
PyErr_SetRaisedException(exc);
}
}
Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict);
Expand Down
Loading