From: Matt Van Horn Date: Tue, 10 Mar 2026 13:20:42 +0000 (-0700) Subject: gh-145492: Fix defaultdict __repr__ infinite recursion (GH-145659) X-Git-Tag: v3.15.0a8~379^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d35f9bc1cf61b27639ed992dfbf363ab436fd8b;p=thirdparty%2FPython%2Fcpython.git gh-145492: Fix defaultdict __repr__ infinite recursion (GH-145659) Co-Authored-By: Thomas Kowalski --- diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index fbd7354a915a..732e9a876ca8 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -204,5 +204,20 @@ class TestDefaultDict(unittest.TestCase): self.assertEqual(test_dict[key], 2) self.assertEqual(count, 2) + def test_repr_recursive_factory(self): + # gh-145492: defaultdict.__repr__ should not cause infinite recursion + # when the factory's __repr__ calls repr() on the defaultdict. + class ProblematicFactory: + def __call__(self): + return {} + def __repr__(self): + repr(dd) + return "ProblematicFactory()" + + dd = defaultdict(ProblematicFactory()) + # Should not raise RecursionError + r = repr(dd) + self.assertIn('ProblematicFactory()', r) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-03-09-00-00-00.gh-issue-145492.457Afc.rst b/Misc/NEWS.d/next/Library/2026-03-09-00-00-00.gh-issue-145492.457Afc.rst new file mode 100644 index 000000000000..297ee4099f12 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-09-00-00-00.gh-issue-145492.457Afc.rst @@ -0,0 +1,3 @@ +Fix infinite recursion in :class:`collections.defaultdict` ``__repr__`` +when a ``defaultdict`` contains itself. Based on analysis by KowalskiThomas +in :gh:`145492`. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index c3d63c8aab4b..15c9aa419118 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -2385,9 +2385,10 @@ defdict_repr(PyObject *op) } defrepr = PyUnicode_FromString("..."); } - else + else { defrepr = PyObject_Repr(dd->default_factory); - Py_ReprLeave(dd->default_factory); + Py_ReprLeave(dd->default_factory); + } } if (defrepr == NULL) { Py_DECREF(baserepr);