forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_recursion.py
More file actions
149 lines (133 loc) · 5.15 KB
/
test_recursion.py
File metadata and controls
149 lines (133 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import weakref
import sys
from test import support
from test.test_json import PyTest, CTest
class JSONTestObject:
pass
class TestRecursion:
def test_listrecursion(self):
x = []
x.append(x)
try:
self.dumps(x)
except ValueError as exc:
self.assertEqual(exc.__notes__, ["when serializing list item 0"])
else:
self.fail("didn't raise ValueError on list recursion")
x = []
y = [x]
x.append(y)
try:
self.dumps(x)
except ValueError as exc:
self.assertEqual(exc.__notes__, ["when serializing list item 0"]*2)
else:
self.fail("didn't raise ValueError on alternating list recursion")
y = []
x = [y, y]
# ensure that the marker is cleared
self.dumps(x)
def test_dictrecursion(self):
x = {}
x["test"] = x
try:
self.dumps(x)
except ValueError as exc:
self.assertEqual(exc.__notes__, ["when serializing dict item 'test'"])
else:
self.fail("didn't raise ValueError on dict recursion")
x = {}
y = {"a": x, "b": x}
# ensure that the marker is cleared
self.dumps(x)
def test_defaultrecursion(self):
class RecursiveJSONEncoder(self.json.JSONEncoder):
recurse = False
def default(self, o):
if o is JSONTestObject:
if self.recurse:
return [JSONTestObject]
else:
return 'JSONTestObject'
return self.json.JSONEncoder.default(o)
enc = RecursiveJSONEncoder()
self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"')
enc.recurse = True
try:
enc.encode(JSONTestObject)
except ValueError as exc:
self.assertEqual(exc.__notes__,
["when serializing list item 0",
"when serializing type object"])
else:
self.fail("didn't raise ValueError on default recursion")
@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_highly_nested_objects_decoding(self):
very_deep = 500_000
# test that loading highly-nested objects doesn't segfault when C
# accelerations are used. See #12017
with self.assertRaises(RecursionError):
with support.infinite_recursion():
self.loads('{"a":' * very_deep + '1' + '}' * very_deep)
with self.assertRaises(RecursionError):
with support.infinite_recursion():
self.loads('{"a":' * very_deep + '[1]' + '}' * very_deep)
with self.assertRaises(RecursionError):
with support.infinite_recursion():
self.loads('[' * very_deep + '1' + ']' * very_deep)
@support.skip_if_unlimited_stack_size
@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.requires_resource('cpu')
def test_highly_nested_objects_encoding(self):
# See #12051
l, d = [], {}
for x in range(500_000):
l, d = [l], {'k':d}
with self.assertRaises(RecursionError):
with support.infinite_recursion(5000):
self.dumps(l)
with self.assertRaises(RecursionError):
with support.infinite_recursion(5000):
self.dumps(d)
@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_endless_recursion(self):
# See #12051
class EndlessJSONEncoder(self.json.JSONEncoder):
def default(self, o):
"""If check_circular is False, this will keep adding another list."""
return [o]
with self.assertRaises(RecursionError):
with support.infinite_recursion(1000):
EndlessJSONEncoder(check_circular=False).encode(5j)
@support.skip_if_unlimited_stack_size
@support.skip_emscripten_stack_overflow()
@support.skip_wasi_stack_overflow()
def test_memory_leak_on_recursion_error(self):
# Test that no memory leak occurs when a RecursionError is raised.
class LeakTestObj:
pass
weak_refs = []
def default(obj):
if isinstance(obj, LeakTestObj):
new_obj = LeakTestObj()
weak_refs.append(weakref.ref(new_obj))
return [new_obj]
raise TypeError
depth = min(500, sys.getrecursionlimit() - 10)
obj = LeakTestObj()
for _ in range(depth):
obj = [obj]
with support.infinite_recursion():
with self.assertRaises(RecursionError):
self.dumps(obj, default=default)
support.gc_collect()
self.assertTrue(weak_refs, "No objects were created to track")
for i, ref in enumerate(weak_refs):
self.assertIsNone(ref(), f"object {i} still alive")
class TestPyRecursion(TestRecursion, PyTest): pass
class TestCRecursion(TestRecursion, CTest): pass