Skip to content

Commit cbcaae0

Browse files
mvanhornclaude
andcommitted
gh-145492: Fix defaultdict __repr__ infinite recursion
Move Py_ReprLeave(dd->default_factory) inside the else branch so it is only called when Py_ReprEnter returned 0 (successfully entered). When Py_ReprEnter detects recursion (returns > 0), it does not add a new entry to the repr tracking list. Calling Py_ReprLeave in that case incorrectly removed the entry from the outer (non-recursive) call, which allowed subsequent recursive calls to bypass the guard entirely, leading to infinite recursion. Includes a regression test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0156133 commit cbcaae0

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

Lib/test/test_defaultdict.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,5 +204,20 @@ def default_factory():
204204
self.assertEqual(test_dict[key], 2)
205205
self.assertEqual(count, 2)
206206

207+
def test_repr_recursive_factory(self):
208+
# gh-145492: defaultdict.__repr__ should not cause infinite recursion
209+
# when the factory's __repr__ calls repr() on the defaultdict.
210+
class ProblematicFactory:
211+
def __call__(self):
212+
return {}
213+
def __repr__(self):
214+
repr(dd)
215+
return "ProblematicFactory()"
216+
217+
dd = defaultdict(ProblematicFactory())
218+
# Should not raise RecursionError
219+
r = repr(dd)
220+
self.assertIn('ProblematicFactory()', r)
221+
207222
if __name__ == "__main__":
208223
unittest.main()

Modules/_collectionsmodule.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,9 +2385,10 @@ defdict_repr(PyObject *op)
23852385
}
23862386
defrepr = PyUnicode_FromString("...");
23872387
}
2388-
else
2388+
else {
23892389
defrepr = PyObject_Repr(dd->default_factory);
2390-
Py_ReprLeave(dd->default_factory);
2390+
Py_ReprLeave(dd->default_factory);
2391+
}
23912392
}
23922393
if (defrepr == NULL) {
23932394
Py_DECREF(baserepr);

0 commit comments

Comments
 (0)