[3.13] GH-124547: Clear instance dictionary if memory error occurs du… · python/cpython@80de976 (original) (raw)

File tree

3 files changed

lines changed

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
1 1 "Test the functionality of Python classes implementing operators."
2 2
3 3 import unittest
4 +import test.support
4 5
5 6 testmeths = [
6 7
@@ -919,6 +920,20 @@ class C:
919 920 C.a = X()
920 921 C.a = X()
921 922
923 +def test_detach_materialized_dict_no_memory(self):
924 +import _testcapi
925 +class A:
926 +def __init__(self):
927 +self.a = 1
928 +self.b = 2
929 +a = A()
930 +d = a.__dict__
931 +with test.support.catch_unraisable_exception() as ex:
932 +_testcapi.set_nomemory(0, 1)
933 +del a
934 +self.assertEqual(ex.unraisable.exc_type, MemoryError)
935 +with self.assertRaises(KeyError):
936 +d["a"]
922 937
923 938 if __name__ == '__main__':
924 939 unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 +When deallocating an object with inline values whose ``__dict__`` is still
2 +live: if memory allocation for the inline values fails, clear the
3 +dictionary. Prevents an interpreter crash.
Original file line number Diff line number Diff line change
@@ -3918,13 +3918,13 @@ dict_copy_impl(PyDictObject *self)
3918 3918 }
3919 3919
3920 3920 /* Copies the values, but does not change the reference
3921 - * counts of the objects in the array. */
3921 + * counts of the objects in the array.
3922 + * Return NULL, but does *not* set an exception on failure */
3922 3923 static PyDictValues *
3923 3924 copy_values(PyDictValues *values)
3924 3925 {
3925 3926 PyDictValues *newvalues = new_values(values->capacity);
3926 3927 if (newvalues == NULL) {
3927 -PyErr_NoMemory();
3928 3928 return NULL;
3929 3929 }
3930 3930 newvalues->size = values->size;
@@ -7189,6 +7189,13 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
7189 7189 PyDictValues *values = copy_values(mp->ma_values);
7190 7190
7191 7191 if (values == NULL) {
7192 +/* Out of memory. Clear the dict */
7193 +PyInterpreterState *interp = _PyInterpreterState_GET();
7194 +PyDictKeysObject *oldkeys = mp->ma_keys;
7195 +set_keys(mp, Py_EMPTY_KEYS);
7196 +dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
7197 +STORE_USED(mp, 0);
7198 +PyErr_NoMemory();
7192 7199 return -1;
7193 7200 }
7194 7201 mp->ma_values = values;