1from test import support
2from test.test_json import PyTest, CTest
3
4
5class JSONTestObject:
6    pass
7
8
9class TestRecursion:
10    def test_listrecursion(self):
11        x = []
12        x.append(x)
13        try:
14            self.dumps(x)
15        except ValueError:
16            pass
17        else:
18            self.fail("didn't raise ValueError on list recursion")
19        x = []
20        y = [x]
21        x.append(y)
22        try:
23            self.dumps(x)
24        except ValueError:
25            pass
26        else:
27            self.fail("didn't raise ValueError on alternating list recursion")
28        y = []
29        x = [y, y]
30        # ensure that the marker is cleared
31        self.dumps(x)
32
33    def test_dictrecursion(self):
34        x = {}
35        x["test"] = x
36        try:
37            self.dumps(x)
38        except ValueError:
39            pass
40        else:
41            self.fail("didn't raise ValueError on dict recursion")
42        x = {}
43        y = {"a": x, "b": x}
44        # ensure that the marker is cleared
45        self.dumps(x)
46
47    def test_defaultrecursion(self):
48        class RecursiveJSONEncoder(self.json.JSONEncoder):
49            recurse = False
50            def default(self, o):
51                if o is JSONTestObject:
52                    if self.recurse:
53                        return [JSONTestObject]
54                    else:
55                        return 'JSONTestObject'
56                return self.json.JSONEncoder.default(o)
57
58        enc = RecursiveJSONEncoder()
59        self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"')
60        enc.recurse = True
61        try:
62            enc.encode(JSONTestObject)
63        except ValueError:
64            pass
65        else:
66            self.fail("didn't raise ValueError on default recursion")
67
68
69    def test_highly_nested_objects_decoding(self):
70        # test that loading highly-nested objects doesn't segfault when C
71        # accelerations are used. See #12017
72        with self.assertRaises(RecursionError):
73            with support.infinite_recursion():
74                self.loads('{"a":' * 100000 + '1' + '}' * 100000)
75        with self.assertRaises(RecursionError):
76            with support.infinite_recursion():
77                self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
78        with self.assertRaises(RecursionError):
79            with support.infinite_recursion():
80                self.loads('[' * 100000 + '1' + ']' * 100000)
81
82    def test_highly_nested_objects_encoding(self):
83        # See #12051
84        l, d = [], {}
85        for x in range(100000):
86            l, d = [l], {'k':d}
87        with self.assertRaises(RecursionError):
88            with support.infinite_recursion():
89                self.dumps(l)
90        with self.assertRaises(RecursionError):
91            with support.infinite_recursion():
92                self.dumps(d)
93
94    def test_endless_recursion(self):
95        # See #12051
96        class EndlessJSONEncoder(self.json.JSONEncoder):
97            def default(self, o):
98                """If check_circular is False, this will keep adding another list."""
99                return [o]
100
101        with self.assertRaises(RecursionError):
102            with support.infinite_recursion():
103                EndlessJSONEncoder(check_circular=False).encode(5j)
104
105
106class TestPyRecursion(TestRecursion, PyTest): pass
107class TestCRecursion(TestRecursion, CTest): pass
108