f.a = 'a'
self.assertEqual(f.__dict__, {1:1, 'a':'a'})
+ def check_reentrant_insertion(self, mutate):
+ # This object will trigger mutation of the dict when replaced
+ # by another value. Note this relies on refcounting: the test
+ # won't achieve its purpose on fully-GCed Python implementations.
+ class Mutating:
+ def __del__(self):
+ mutate(d)
+
+ d = {k: Mutating() for k in 'abcdefghijklmnopqr'}
+ for k in list(d):
+ d[k] = k
+
+ def test_reentrant_insertion(self):
+ # Reentrant insertion shouldn't crash (see issue #22653)
+ def mutate(d):
+ d['b'] = 5
+ self.check_reentrant_insertion(mutate)
+
+ def mutate(d):
+ d.update(self.__dict__)
+ d.clear()
+ self.check_reentrant_insertion(mutate)
+
+ def mutate(d):
+ while d:
+ d.popitem()
+ self.check_reentrant_insertion(mutate)
+
+
from test import mapping_tests
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
Core and Builtins
-----------------
+- Issue #22653: Fix an assertion failure in debug mode when doing a reentrant
+ dict insertion in debug mode.
+
- Issue #22643: Fix integer overflow in Unicode case operations (upper, lower,
title, swapcase, casefold).
if (ep == NULL) {
return -1;
}
+ assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
Py_INCREF(value);
MAINTAIN_TRACKING(mp, key, value);
old_value = *value_addr;
if (old_value != NULL) {
assert(ep->me_key != NULL && ep->me_key != dummy);
*value_addr = value;
- Py_DECREF(old_value); /* which **CAN** re-enter */
+ Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
}
else {
if (ep->me_key == NULL) {
}
mp->ma_used++;
*value_addr = value;
+ assert(ep->me_key != NULL && ep->me_key != dummy);
}
- assert(ep->me_key != NULL && ep->me_key != dummy);
- assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
return 0;
}