Skip to content

Commit 9361207

Browse files
[3.13] gh-142495: Make defaultdict keep existed value when racing with __missing__ (GH-142668) (GH-142858)
(cherry picked from commit a043407) Co-authored-by: Edward Xu <xuxiangad@gmail.com>
1 parent e07cda3 commit 9361207

File tree

3 files changed

+27
-5
lines changed

3 files changed

+27
-5
lines changed

Lib/test/test_defaultdict.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,5 +186,23 @@ def test_union(self):
186186
with self.assertRaises(TypeError):
187187
i |= None
188188

189+
def test_factory_conflict_with_set_value(self):
190+
key = "conflict_test"
191+
count = 0
192+
193+
def default_factory():
194+
nonlocal count
195+
count += 1
196+
local_count = count
197+
if count == 1:
198+
test_dict[key]
199+
return local_count
200+
201+
test_dict = defaultdict(default_factory)
202+
203+
self.assertEqual(count, 0)
204+
self.assertEqual(test_dict[key], 2)
205+
self.assertEqual(count, 2)
206+
189207
if __name__ == "__main__":
190208
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:class:`collections.defaultdict` now prioritizes :meth:`~object.__setitem__`
2+
when inserting default values from ``default_factory``. This prevents race
3+
conditions where a default value would overwrite a value set before
4+
``default_factory`` returns.

Modules/_collectionsmodule.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,11 +2203,11 @@ defdict_missing(defdictobject *dd, PyObject *key)
22032203
value = _PyObject_CallNoArgs(factory);
22042204
if (value == NULL)
22052205
return value;
2206-
if (PyObject_SetItem((PyObject *)dd, key, value) < 0) {
2207-
Py_DECREF(value);
2208-
return NULL;
2209-
}
2210-
return value;
2206+
PyObject *result = NULL;
2207+
(void)PyDict_SetDefaultRef((PyObject *)dd, key, value, &result);
2208+
// 'result' is NULL, or a strong reference to 'value' or 'dd[key]'
2209+
Py_DECREF(value);
2210+
return result;
22112211
}
22122212

22132213
static inline PyObject*

0 commit comments

Comments
 (0)