Preserve recursively-set value in defaultdict.__missing__#7718
Conversation
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.
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (2)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: (module 'collections test_defaultdict' not found) Legend:
|
thanks for the check ! |
Background
CPython's
defaultdict.__missing__(Modules/_collectionsmodule.c::defdict_missing) callsdefault_factory()first; if the factory's recursion populatedself[key]while running, the existing value is preserved instead of being overwritten.RustPython ships a Python fallback at
Lib/collections/_defaultdict.py(the C-implemented_collections.defaultdictis not available — see the comment atLib/collections/__init__.py:60). That fallback unconditionally executedself[key] = valafter the factory returned, overwriting any value the recursive call had already stored.Repro
Fix
Add
if key in self: return self[key]after the factory call, 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.Tests unmasked
test_defaultdict.TestDefaultDict.test_factory_conflict_with_set_valueVerification
defaultdict(int)increment,defaultdict()with no factory still raisesKeyErrortest_defaultdict,test_collections,test_dict,test_typing,test_descr(~1,116 tests)