Kaydet (Commit) d7773d92 authored tarafından bennorth's avatar bennorth Kaydeden (comit) Senthil Kumaran

bpo-18533: Avoid RecursionError from repr() of recursive dictview (#4823)

dictview_repr(): Use a Py_ReprEnter() / Py_ReprLeave() pair to check
for recursion, and produce "..." if so.

test_recursive_repr(): Check for the string rather than a
RecursionError.  (Test cannot be any tighter as contents are
implementation-dependent.)

test_deeply_nested_repr(): Add new test, replacing the original
test_recursive_repr().  It checks that a RecursionError is raised in
the case of a non-recursive but deeply nested structure.  (Very
similar to what test_repr_deep() in test/test_dict.py does for a
normal dict.)

OrderedDictTests: Add new test case, to test behavior on OrderedDict
instances containing their own values() or items().
üst e76daebc
import collections.abc import collections.abc
import copy import copy
import pickle import pickle
import sys
import unittest import unittest
class DictSetTest(unittest.TestCase): class DictSetTest(unittest.TestCase):
...@@ -202,6 +203,20 @@ class DictSetTest(unittest.TestCase): ...@@ -202,6 +203,20 @@ class DictSetTest(unittest.TestCase):
def test_recursive_repr(self): def test_recursive_repr(self):
d = {} d = {}
d[42] = d.values() d[42] = d.values()
r = repr(d)
# Cannot perform a stronger test, as the contents of the repr
# are implementation-dependent. All we can say is that we
# want a str result, not an exception of any sort.
self.assertIsInstance(r, str)
d[42] = d.items()
r = repr(d)
# Again.
self.assertIsInstance(r, str)
def test_deeply_nested_repr(self):
d = {}
for i in range(sys.getrecursionlimit() + 100):
d = {42: d.values()}
self.assertRaises(RecursionError, repr, d) self.assertRaises(RecursionError, repr, d)
def test_copy(self): def test_copy(self):
......
...@@ -355,6 +355,20 @@ class OrderedDictTests: ...@@ -355,6 +355,20 @@ class OrderedDictTests:
self.assertEqual(repr(od), self.assertEqual(repr(od),
"OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])") "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
def test_repr_recursive_values(self):
OrderedDict = self.OrderedDict
od = OrderedDict()
od[42] = od.values()
r = repr(od)
# Cannot perform a stronger test, as the contents of the repr
# are implementation-dependent. All we can say is that we
# want a str result, not an exception of any sort.
self.assertIsInstance(r, str)
od[42] = od.items()
r = repr(od)
# Again.
self.assertIsInstance(r, str)
def test_setdefault(self): def test_setdefault(self):
OrderedDict = self.OrderedDict OrderedDict = self.OrderedDict
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
......
``repr()`` on a dict containing its own ``values()`` or ``items()`` no
longer raises ``RecursionError``; OrderedDict similarly. Instead, use
``...``, as for other recursive structures. Patch by Ben North.
...@@ -3853,14 +3853,22 @@ static PyObject * ...@@ -3853,14 +3853,22 @@ static PyObject *
dictview_repr(_PyDictViewObject *dv) dictview_repr(_PyDictViewObject *dv)
{ {
PyObject *seq; PyObject *seq;
PyObject *result; PyObject *result = NULL;
Py_ssize_t rc;
rc = Py_ReprEnter((PyObject *)dv);
if (rc != 0) {
return rc > 0 ? PyUnicode_FromString("...") : NULL;
}
seq = PySequence_List((PyObject *)dv); seq = PySequence_List((PyObject *)dv);
if (seq == NULL) if (seq == NULL) {
return NULL; goto Done;
}
result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq);
Py_DECREF(seq); Py_DECREF(seq);
Done:
Py_ReprLeave((PyObject *)dv);
return result; return result;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment