From 3e5f8a6975cb7f7c3dc9f84686f27c49bc4aa75a Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Fri, 8 Feb 2008 01:05:21 +0000 Subject: [PATCH] issue 2045: Infinite recursion when printing a subclass of defaultdict, if default_factory is set to a bound method. Backport of r60663. --- Lib/test/test_defaultdict.py | 23 +++++++++++++++++++++++ Misc/NEWS | 3 +++ Modules/collectionsmodule.c | 12 +++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index 08be005b424..5724fd0460d 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -141,6 +141,29 @@ class TestDefaultDict(unittest.TestCase): else: self.fail("expected KeyError") + def test_recursive_repr(self): + # Issue2045: stack overflow when default_factory is a bound method + class sub(defaultdict): + def __init__(self): + self.default_factory = self._factory + def _factory(self): + return [] + d = sub() + self.assert_(repr(d).startswith( + "defaultdict(>f, d + finally: + f.close() + finally: + os.remove(tfn) + def test_main(): test_support.run_unittest(TestDefaultDict) diff --git a/Misc/NEWS b/Misc/NEWS index e3a4aceecec..cf73bcae07b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 2.5.2c1? Core and builtins ----------------- +- Issue #2045: Fix an infinite recursion triggered when printing a subclass of + collections.defaultdict, if its default_factory is set to a bound method. + - Issue #1920: "while 0" statements were completely removed by the compiler, even in the presence of an "else" clause, which is supposed to be run when the condition is false. Now the compiler correctly emits bytecode for the diff --git a/Modules/collectionsmodule.c b/Modules/collectionsmodule.c index 9d128fc9591..dd3c0b7d8f1 100644 --- a/Modules/collectionsmodule.c +++ b/Modules/collectionsmodule.c @@ -1217,7 +1217,17 @@ defdict_repr(defdictobject *dd) if (dd->default_factory == NULL) defrepr = PyString_FromString("None"); else - defrepr = PyObject_Repr(dd->default_factory); + { + int status = Py_ReprEnter(dd->default_factory); + if (status != 0) { + if (status < 0) + return NULL; + defrepr = PyString_FromString("..."); + } + else + defrepr = PyObject_Repr(dd->default_factory); + Py_ReprLeave(dd->default_factory); + } if (defrepr == NULL) { Py_DECREF(baserepr); return NULL;