gh-142495: Make defaultdict keep existed value when racing with __missing__ (GH-142668)

This commit is contained in:
Edward Xu 2025-12-16 23:04:20 +08:00 committed by GitHub
parent 47ec96f133
commit a043407510
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 5 deletions

View file

@ -186,5 +186,23 @@ def test_union(self):
with self.assertRaises(TypeError):
i |= None
def test_factory_conflict_with_set_value(self):
key = "conflict_test"
count = 0
def default_factory():
nonlocal count
count += 1
local_count = count
if count == 1:
test_dict[key]
return local_count
test_dict = defaultdict(default_factory)
self.assertEqual(count, 0)
self.assertEqual(test_dict[key], 2)
self.assertEqual(count, 2)
if __name__ == "__main__":
unittest.main()

View file

@ -0,0 +1,4 @@
:class:`collections.defaultdict` now prioritizes :meth:`~object.__setitem__`
when inserting default values from ``default_factory``. This prevents race
conditions where a default value would overwrite a value set before
``default_factory`` returns.

View file

@ -2231,11 +2231,11 @@ defdict_missing(PyObject *op, PyObject *key)
value = _PyObject_CallNoArgs(factory);
if (value == NULL)
return value;
if (PyObject_SetItem(op, key, value) < 0) {
Py_DECREF(value);
return NULL;
}
return value;
PyObject *result = NULL;
(void)PyDict_SetDefaultRef(op, key, value, &result);
// 'result' is NULL, or a strong reference to 'value' or 'op[key]'
Py_DECREF(value);
return result;
}
static inline PyObject*