]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-145492: Fix defaultdict __repr__ infinite recursion (GH-145659)
authorMatt Van Horn <mvanhorn@users.noreply.github.com>
Tue, 10 Mar 2026 13:20:42 +0000 (06:20 -0700)
committerGitHub <noreply@github.com>
Tue, 10 Mar 2026 13:20:42 +0000 (14:20 +0100)
Co-Authored-By: Thomas Kowalski <thom.kowa@gmail.com>
Lib/test/test_defaultdict.py
Misc/NEWS.d/next/Library/2026-03-09-00-00-00.gh-issue-145492.457Afc.rst [new file with mode: 0644]
Modules/_collectionsmodule.c

index fbd7354a915a0a001a2c7ed8a976a82315be71d3..732e9a876ca8adc555fe237651c3989ab2aa8589 100644 (file)
@@ -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 (file)
index 0000000..297ee40
--- /dev/null
@@ -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`.
index c3d63c8aab4b477411d00bda7d56992324a6b6d6..15c9aa419118225836e78e029c236f5bb8250778 100644 (file)
@@ -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);