bpo-32137: The repr of deeply nested dict now raises a RecursionError (GH-4570) (GH-4689)
instead of crashing due to a stack overflow.
This perhaps will fix similar problems in other extension types.
(cherry picked from commit 1fb72d2ad2
)
This commit is contained in:
parent
581ce25e1f
commit
688b6dec4e
|
@ -53,10 +53,11 @@ class CommonTest(seq_tests.CommonTest):
|
||||||
self.assertEqual(str(a2), "[0, 1, 2, [...], 3]")
|
self.assertEqual(str(a2), "[0, 1, 2, [...], 3]")
|
||||||
self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]")
|
self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]")
|
||||||
|
|
||||||
l0 = []
|
def test_repr_deep(self):
|
||||||
|
a = self.type2test([])
|
||||||
for i in range(sys.getrecursionlimit() + 100):
|
for i in range(sys.getrecursionlimit() + 100):
|
||||||
l0 = [l0]
|
a = self.type2test([a])
|
||||||
self.assertRaises(RecursionError, repr, l0)
|
self.assertRaises(RecursionError, repr, a)
|
||||||
|
|
||||||
def test_print(self):
|
def test_print(self):
|
||||||
d = self.type2test(range(200))
|
d = self.type2test(range(200))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# tests common to dict and UserDict
|
# tests common to dict and UserDict
|
||||||
import unittest
|
import unittest
|
||||||
import collections
|
import collections
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class BasicTestMappingProtocol(unittest.TestCase):
|
class BasicTestMappingProtocol(unittest.TestCase):
|
||||||
|
@ -619,6 +620,14 @@ class TestHashMappingProtocol(TestMappingProtocol):
|
||||||
d = self._full_mapping({1: BadRepr()})
|
d = self._full_mapping({1: BadRepr()})
|
||||||
self.assertRaises(Exc, repr, d)
|
self.assertRaises(Exc, repr, d)
|
||||||
|
|
||||||
|
def test_repr_deep(self):
|
||||||
|
d = self._empty_mapping()
|
||||||
|
for i in range(sys.getrecursionlimit() + 100):
|
||||||
|
d0 = d
|
||||||
|
d = self._empty_mapping()
|
||||||
|
d[1] = d0
|
||||||
|
self.assertRaises(RecursionError, repr, d)
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
self.assertEqual(self._empty_mapping(), self._empty_mapping())
|
self.assertEqual(self._empty_mapping(), self._empty_mapping())
|
||||||
self.assertEqual(self._full_mapping({1: 2}),
|
self.assertEqual(self._full_mapping({1: 2}),
|
||||||
|
|
|
@ -468,6 +468,12 @@ class DictTest(unittest.TestCase):
|
||||||
d = {1: BadRepr()}
|
d = {1: BadRepr()}
|
||||||
self.assertRaises(Exc, repr, d)
|
self.assertRaises(Exc, repr, d)
|
||||||
|
|
||||||
|
def test_repr_deep(self):
|
||||||
|
d = {}
|
||||||
|
for i in range(sys.getrecursionlimit() + 100):
|
||||||
|
d = {1: d}
|
||||||
|
self.assertRaises(RecursionError, repr, d)
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
self.assertEqual({}, {})
|
self.assertEqual({}, {})
|
||||||
self.assertEqual({1: 2}, {1: 2})
|
self.assertEqual({1: 2}, {1: 2})
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The repr of deeply nested dict now raises a RecursionError instead of
|
||||||
|
crashing due to a stack overflow.
|
|
@ -366,10 +366,7 @@ list_repr(PyListObject *v)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Py_EnterRecursiveCall(" while getting the repr of a list"))
|
|
||||||
goto error;
|
|
||||||
s = PyObject_Repr(v->ob_item[i]);
|
s = PyObject_Repr(v->ob_item[i]);
|
||||||
Py_LeaveRecursiveCall();
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
|
@ -482,7 +482,12 @@ PyObject_Repr(PyObject *v)
|
||||||
assert(!PyErr_Occurred());
|
assert(!PyErr_Occurred());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* It is possible for a type to have a tp_repr representation that loops
|
||||||
|
infinitely. */
|
||||||
|
if (Py_EnterRecursiveCall(" while getting the repr of an object"))
|
||||||
|
return NULL;
|
||||||
res = (*v->ob_type->tp_repr)(v);
|
res = (*v->ob_type->tp_repr)(v);
|
||||||
|
Py_LeaveRecursiveCall();
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (!PyUnicode_Check(res)) {
|
if (!PyUnicode_Check(res)) {
|
||||||
|
|
|
@ -300,10 +300,7 @@ tuplerepr(PyTupleObject *v)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Py_EnterRecursiveCall(" while getting the repr of a tuple"))
|
|
||||||
goto error;
|
|
||||||
s = PyObject_Repr(v->ob_item[i]);
|
s = PyObject_Repr(v->ob_item[i]);
|
||||||
Py_LeaveRecursiveCall();
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue