https://github.com/python/cpython/commit/cef423cbe20dab5af89f4cbcfc9abda3cd5ba9f5
commit: cef423cbe20dab5af89f4cbcfc9abda3cd5ba9f5
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: encukou <[email protected]>
date: 2026-03-12T10:45:43+01:00
summary:

[3.13] gh-145492: Fix defaultdict __repr__ infinite recursion (GH-145659) 
(GH-145746)

(cherry picked from commit 2d35f9bc1cf61b27639ed992dfbf363ab436fd8b)

Includes test fix-up from GH-145788
(cherry picked from commit aa4240ebea1aacc907b1efa58e7f547d90cff3b1)

Co-authored-by: Thomas Kowalski <[email protected]>
Co-authored-by: Matt Van Horn <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-03-09-00-00-00.gh-issue-145492.457Afc.rst
M Lib/test/test_defaultdict.py
M Modules/_collectionsmodule.c

diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py
index fbd7354a915a0a..a193eb10f16d17 100644
--- a/Lib/test/test_defaultdict.py
+++ b/Lib/test/test_defaultdict.py
@@ -204,5 +204,20 @@ def default_factory():
         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 f"ProblematicFactory for {dd}"
+
+        dd = defaultdict(ProblematicFactory())
+        # Should not raise RecursionError
+        r = repr(dd)
+        self.assertIn("ProblematicFactory for", 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 00000000000000..297ee4099f12c5
--- /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 f5fc0d02a16607..b2c3d0e42be4cb 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -2337,9 +2337,10 @@ defdict_repr(defdictobject *dd)
             }
             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);

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to