https://github.com/python/cpython/commit/a0434075108efe6acdfba34f42545f4d80ac9a5e
commit: a0434075108efe6acdfba34f42545f4d80ac9a5e
branch: main
author: Edward Xu <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-12-16T17:04:20+02:00
summary:
gh-142495: Make `defaultdict` keep existed value when racing with `__missing__`
(GH-142668)
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 3ba48d5d9d3c64..3b14a21fa8428e 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -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*
_______________________________________________
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]