[3.13] GH-124547: Clear instance dictionary if memory error occurs du… · python/cpython@80de976 (original) (raw)
File tree
3 files changed
lines changed
- Misc/NEWS.d/next/Core_and_Builtins
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; |