Preserve recursively-set value in defaultdict.__missing__ (#7718)

CPython's defaultdict.__missing__ (Modules/_collectionsmodule.c::defdict_missing)
calls default_factory() first; if the factory's recursion already populated
self[key] while running, the existing value is preserved instead of being
overwritten.

RustPython ships a Python fallback at Lib/collections/_defaultdict.py
(the C _collections.defaultdict is not available). That fallback
unconditionally executed self[key] = val after the factory returned,
overwriting any value the recursive call had already stored.

Add a 'if key in self: return self[key]' guard before the assignment.
dict.__contains__ does not invoke __missing__, so there's no recursion
risk; in the common non-reentrant case the check is False and behavior
is unchanged.

Unmasks test_defaultdict.TestDefaultDict.test_factory_conflict_with_set_value.
This commit is contained in:
Changjoon authored and GitHub committed 2026-04-29 18:14:32 +09:00
1 parent 82e8b200db
commit 330b18f2fe
2 files changed
+4 -1

No files matched your search

+4
View File
@@ -17,6 +17,10 @@ class defaultdict(dict):
val = self.default_factory()
else:
raise KeyError(key)
# CPython parity: a recursive __missing__ via factory() may have
# already populated key; preserve that value instead of overwriting.
if key in self:
return self[key]
self[key] = val
return val
-1
View File
@@ -186,7 +186,6 @@ class TestDefaultDict(unittest.TestCase):
with self.assertRaises(TypeError):
i |= None
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 1 != 2
def test_factory_conflict_with_set_value(self):
key = "conflict_test"
count = 0