Skip to content

Commit 2d35f9b

Browse files
gh-145492: Fix defaultdict __repr__ infinite recursion (GH-145659)
Co-Authored-By: Thomas Kowalski <thom.kowa@gmail.com>
1 parent 728e4a0 commit 2d35f9b

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-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()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix infinite recursion in :class:`collections.defaultdict` ``__repr__``
2+
when a ``defaultdict`` contains itself. Based on analysis by KowalskiThomas
3+
in :gh:`145492`.

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)