https://github.com/python/cpython/commit/9361207bc2d198a10f7b9b6edda98f7da328cf93 commit: 9361207bc2d198a10f7b9b6edda98f7da328cf93 branch: 3.13 author: Serhiy Storchaka <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2025-12-17T08:40:47Z summary:
[3.13] gh-142495: Make `defaultdict` keep existed value when racing with `__missing__` (GH-142668) (GH-142858) (cherry picked from commit a0434075108efe6acdfba34f42545f4d80ac9a5e) Co-authored-by: Edward Xu <[email protected]> files: A Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst M Lib/test/test_defaultdict.py M Modules/_collectionsmodule.c diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index bdbe9b81e8fb3f..fbd7354a915a0a 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -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() diff --git a/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst b/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst new file mode 100644 index 00000000000000..3e1a624fe56473 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-13-23-26-42.gh-issue-142495.I88Uv_.rst @@ -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. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 2c30e5c70a6cc2..b356c917c1d5ef 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2203,11 +2203,11 @@ defdict_missing(defdictobject *dd, PyObject *key) value = _PyObject_CallNoArgs(factory); if (value == NULL) return value; - if (PyObject_SetItem((PyObject *)dd, key, value) < 0) { - Py_DECREF(value); - return NULL; - } - return value; + PyObject *result = NULL; + (void)PyDict_SetDefaultRef((PyObject *)dd, key, value, &result); + // 'result' is NULL, or a strong reference to 'value' or 'dd[key]' + Py_DECREF(value); + return result; } static inline PyObject* _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
