File: test_recursion.py

package info (click to toggle)
python-sdjson 0.5.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 560 kB
  • sloc: python: 1,566; makefile: 6; sh: 6
file content (124 lines) | stat: -rw-r--r-- 2,537 bytes parent folder | download
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
# stdlib
from typing import Dict, List

# 3rd party
import pytest
from coincidence.selectors import not_pypy

# this package
import sdjson


class JSONTestObject:
	pass


def test_listrecursion() -> None:
	x: List[List] = []
	x.append(x)

	try:
		sdjson.dumps(x)
	except ValueError:
		pass
	else:
		pytest.fail("didn't raise ValueError on list recursion")

	x = []
	y = [x]
	x.append(y)

	try:
		sdjson.dumps(x)
	except ValueError:
		pass
	else:
		pytest.fail("didn't raise ValueError on alternating list recursion")

	y = []
	x = [y, y]
	# ensure that the marker is cleared
	sdjson.dumps(x)


def test_dictrecursion() -> None:
	x: Dict[str, Dict] = {}
	x["test"] = x

	try:
		sdjson.dumps(x)
	except ValueError:
		pass
	else:
		pytest.fail("didn't raise ValueError on dict recursion")

	x = {}
	y = {'a': x, 'b': x}
	# ensure that the marker is cleared
	sdjson.dumps(x)


def test_defaultrecursion() -> None:

	class RecursiveJSONEncoder(sdjson.JSONEncoder):
		recurse = False

		def default(self, o: object):
			if o is JSONTestObject:
				if self.recurse:
					return [JSONTestObject]
				else:
					return "JSONTestObject"
			return sdjson.JSONEncoder.default(self, o)

	enc = RecursiveJSONEncoder()
	assert enc.encode(JSONTestObject) == '"JSONTestObject"'
	enc.recurse = True
	try:
		enc.encode(JSONTestObject)
	except ValueError:
		pass
	else:
		pytest.fail("didn't raise ValueError on default recursion")


def test_highly_nested_objects_decoding() -> None:
	# test that loading highly-nested objects doesn't segfault when C
	# accelerations are used. See #12017
	with pytest.raises(RecursionError):
		sdjson.loads('{"a":' * 100000 + '1' + '}' * 100000)
	with pytest.raises(RecursionError):
		sdjson.loads('{"a":' * 100000 + "[1]" + '}' * 100000)
	with pytest.raises(RecursionError):
		sdjson.loads('[' * 100000 + '1' + ']' * 100000)


@not_pypy("Breaks coverage tracing on PyPy")
def test_highly_nested_objects_encoding() -> None:
	# See #12051
	l: List[List]
	d: Dict[str, Dict]
	l, d = [], {}

	for x in range(100000):
		l, d = [l], {'k': d}

	with pytest.raises(RecursionError):
		sdjson.dumps(l)
	with pytest.raises(RecursionError):
		sdjson.dumps(d)


@not_pypy("Breaks coverage tracing on PyPy")
def test_endless_recursion() -> None:
	# See #12051
	class EndlessJSONEncoder(sdjson.JSONEncoder):

		def default(self, o: object) -> List:
			"""
			If check_circular is False, this will keep adding another list.
			"""
			return [o]

	with pytest.raises(RecursionError):
		EndlessJSONEncoder(check_circular=False).encode(5j)