Skip to content

Commit b4e50c9

Browse files
committed
Address review: remove redundant comments, use nonlocal, add list mutation test
1 parent 3c29d13 commit b4e50c9

2 files changed

Lines changed: 123 additions & 71 deletions

File tree

Lines changed: 123 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
from test.test_json import CTest
2+
from test.support import gc_collect
23

34

45
class BadBool:
56
def __bool__(self):
6-
1/0
7+
1 / 0
78

89

910
class TestSpeedups(CTest):
1011
def test_scanstring(self):
1112
self.assertEqual(self.json.decoder.scanstring.__module__, "_json")
12-
self.assertIs(self.json.decoder.scanstring, self.json.decoder.c_scanstring)
13+
self.assertIs(
14+
self.json.decoder.scanstring, self.json.decoder.c_scanstring
15+
)
1316

1417
def test_encode_basestring_ascii(self):
15-
self.assertEqual(self.json.encoder.encode_basestring_ascii.__module__,
16-
"_json")
17-
self.assertIs(self.json.encoder.encode_basestring_ascii,
18-
self.json.encoder.c_encode_basestring_ascii)
18+
self.assertEqual(
19+
self.json.encoder.encode_basestring_ascii.__module__, "_json"
20+
)
21+
self.assertIs(
22+
self.json.encoder.encode_basestring_ascii,
23+
self.json.encoder.c_encode_basestring_ascii,
24+
)
1925

2026

2127
class TestDecode(CTest):
@@ -25,119 +31,170 @@ def test_make_scanner(self):
2531
def test_bad_bool_args(self):
2632
def test(value):
2733
self.json.decoder.JSONDecoder(strict=BadBool()).decode(value)
34+
2835
self.assertRaises(ZeroDivisionError, test, '""')
29-
self.assertRaises(ZeroDivisionError, test, '{}')
36+
self.assertRaises(ZeroDivisionError, test, "{}")
3037

3138

3239
class TestEncode(CTest):
3340
def test_make_encoder(self):
3441
# bpo-6986: The interpreter shouldn't crash in case c_make_encoder()
3542
# receives invalid arguments.
36-
self.assertRaises(TypeError, self.json.encoder.c_make_encoder,
43+
self.assertRaises(
44+
TypeError,
45+
self.json.encoder.c_make_encoder,
3746
(True, False),
38-
b"\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75",
39-
None)
47+
b"\xcd\x7d\x3d\x4e\x12\x4c\xf9\x79\xd7\x52\xba\x82\xf2\x27\x4a\x7d\xa0\xca\x75",
48+
None,
49+
)
4050

4151
def test_bad_str_encoder(self):
4252
# Issue #31505: There shouldn't be an assertion failure in case
4353
# c_make_encoder() receives a bad encoder() argument.
4454
def bad_encoder1(*args):
4555
return None
46-
enc = self.json.encoder.c_make_encoder(None, lambda obj: str(obj),
47-
bad_encoder1, None, ': ', ', ',
48-
False, False, False)
56+
57+
enc = self.json.encoder.c_make_encoder(
58+
None,
59+
lambda obj: str(obj),
60+
bad_encoder1,
61+
None,
62+
": ",
63+
", ",
64+
False,
65+
False,
66+
False,
67+
)
4968
with self.assertRaises(TypeError):
50-
enc('spam', 4)
69+
enc("spam", 4)
5170
with self.assertRaises(TypeError):
52-
enc({'spam': 42}, 4)
71+
enc({"spam": 42}, 4)
5372

5473
def bad_encoder2(*args):
55-
1/0
56-
enc = self.json.encoder.c_make_encoder(None, lambda obj: str(obj),
57-
bad_encoder2, None, ': ', ', ',
58-
False, False, False)
74+
1 / 0
75+
76+
enc = self.json.encoder.c_make_encoder(
77+
None,
78+
lambda obj: str(obj),
79+
bad_encoder2,
80+
None,
81+
": ",
82+
", ",
83+
False,
84+
False,
85+
False,
86+
)
5987
with self.assertRaises(ZeroDivisionError):
60-
enc('spam', 4)
88+
enc("spam", 4)
6189

6290
def test_bad_markers_argument_to_encoder(self):
6391
# https://bugs.python.org/issue45269
6492
with self.assertRaisesRegex(
6593
TypeError,
66-
r'make_encoder\(\) argument 1 must be dict or None, not int',
94+
r"make_encoder\(\) argument 1 must be dict or None, not int",
6795
):
68-
self.json.encoder.c_make_encoder(1, None, None, None, ': ', ', ',
69-
False, False, False)
96+
self.json.encoder.c_make_encoder(
97+
1, None, None, None, ": ", ", ", False, False, False
98+
)
7099

71100
def test_bad_bool_args(self):
72101
def test(name):
73-
self.json.encoder.JSONEncoder(**{name: BadBool()}).encode({'a': 1})
74-
self.assertRaises(ZeroDivisionError, test, 'skipkeys')
75-
self.assertRaises(ZeroDivisionError, test, 'ensure_ascii')
76-
self.assertRaises(ZeroDivisionError, test, 'check_circular')
77-
self.assertRaises(ZeroDivisionError, test, 'allow_nan')
78-
self.assertRaises(ZeroDivisionError, test, 'sort_keys')
102+
self.json.encoder.JSONEncoder(**{name: BadBool()}).encode({"a": 1})
103+
104+
self.assertRaises(ZeroDivisionError, test, "skipkeys")
105+
self.assertRaises(ZeroDivisionError, test, "ensure_ascii")
106+
self.assertRaises(ZeroDivisionError, test, "check_circular")
107+
self.assertRaises(ZeroDivisionError, test, "allow_nan")
108+
self.assertRaises(ZeroDivisionError, test, "sort_keys")
79109

80110
def test_unsortable_keys(self):
81111
with self.assertRaises(TypeError):
82-
self.json.encoder.JSONEncoder(sort_keys=True).encode({'a': 1, 1: 'a'})
112+
self.json.encoder.JSONEncoder(sort_keys=True).encode(
113+
{"a": 1, 1: "a"}
114+
)
83115

84116
def test_current_indent_level(self):
85117
enc = self.json.encoder.c_make_encoder(
86118
markers=None,
87119
default=str,
88120
encoder=self.json.encoder.c_encode_basestring,
89-
indent='\t',
90-
key_separator=': ',
91-
item_separator=', ',
121+
indent="\t",
122+
key_separator=": ",
123+
item_separator=", ",
92124
sort_keys=False,
93125
skipkeys=False,
94-
allow_nan=False)
95-
expected = (
96-
'[\n'
97-
'\t"spam", \n'
98-
'\t{\n'
99-
'\t\t"ham": "eggs"\n'
100-
'\t}\n'
101-
']')
102-
self.assertEqual(enc(['spam', {'ham': 'eggs'}], 0)[0], expected)
103-
self.assertEqual(enc(['spam', {'ham': 'eggs'}], -3)[0], expected)
126+
allow_nan=False,
127+
)
128+
expected = '[\n\t"spam", \n\t{\n\t\t"ham": "eggs"\n\t}\n]'
129+
self.assertEqual(enc(["spam", {"ham": "eggs"}], 0)[0], expected)
130+
self.assertEqual(enc(["spam", {"ham": "eggs"}], -3)[0], expected)
104131
expected2 = (
105-
'[\n'
132+
"[\n"
106133
'\t\t\t\t"spam", \n'
107-
'\t\t\t\t{\n'
134+
"\t\t\t\t{\n"
108135
'\t\t\t\t\t"ham": "eggs"\n'
109-
'\t\t\t\t}\n'
110-
'\t\t\t]')
111-
self.assertEqual(enc(['spam', {'ham': 'eggs'}], 3)[0], expected2)
112-
self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}], 3.0)
113-
self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}])
114-
115-
def test_mutate_items_during_encode(self):
116-
c_make_encoder = getattr(self.json.encoder, 'c_make_encoder', None)
117-
if c_make_encoder is None:
118-
self.skipTest("c_make_encoder not available")
136+
"\t\t\t\t}\n"
137+
"\t\t\t]"
138+
)
139+
self.assertEqual(enc(["spam", {"ham": "eggs"}], 3)[0], expected2)
140+
self.assertRaises(TypeError, enc, ["spam", {"ham": "eggs"}], 3.0)
141+
self.assertRaises(TypeError, enc, ["spam", {"ham": "eggs"}])
119142

120-
cache = []
143+
def test_mutate_dict_items_during_encode(self):
144+
items = None
121145

122146
class BadDict(dict):
123147
def items(self):
124-
entries = [("boom", object())]
125-
cache.append(entries)
126-
return entries
148+
nonlocal items
149+
items = [("boom", object())]
150+
return items
127151

128152
def encode_str(obj):
129-
if cache:
130-
cache.pop().clear()
153+
nonlocal items
154+
if items is not None:
155+
items.clear()
156+
items = None
157+
gc_collect()
131158
return '"x"'
132159

133-
encoder = c_make_encoder(
134-
None, lambda o: "null",
135-
encode_str, None,
136-
": ", ", ", False,
137-
False, True
160+
encoder = self.json.encoder.c_make_encoder(
161+
None,
162+
lambda o: "null",
163+
encode_str,
164+
None,
165+
": ",
166+
", ",
167+
False,
168+
False,
169+
True,
138170
)
139171

140172
try:
141173
encoder(BadDict(real=1), 0)
142174
except (ValueError, RuntimeError):
143175
pass
176+
177+
def test_mutate_list_during_encode(self):
178+
lst = [object() for _ in range(10)]
179+
180+
def default(obj):
181+
lst.clear()
182+
gc_collect()
183+
return None
184+
185+
encoder = self.json.encoder.c_make_encoder(
186+
None,
187+
default,
188+
self.json.encoder.c_encode_basestring,
189+
None,
190+
": ",
191+
", ",
192+
False,
193+
False,
194+
True,
195+
)
196+
197+
try:
198+
encoder(lst, 0)
199+
except (ValueError, RuntimeError):
200+
pass

Modules/_json.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,8 +1767,6 @@ _encoder_iterate_dict_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer,
17671767
PyObject *key, *value;
17681768
Py_ssize_t pos = 0;
17691769
while (PyDict_Next(dct, &pos, &key, &value)) {
1770-
// GH-142831: The key and value must be strong-referenced to avoid
1771-
// use-after-free if the user code modifies the dict during iteration.
17721770
Py_INCREF(key);
17731771
Py_INCREF(value);
17741772

@@ -1884,9 +1882,6 @@ _encoder_iterate_fast_seq_lock_held(PyEncoderObject *s, PyUnicodeWriter *writer,
18841882
{
18851883
for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(s_fast); i++) {
18861884
PyObject *obj = PySequence_Fast_GET_ITEM(s_fast, i);
1887-
1888-
// GH-142831: The object must be strong-referenced to avoid use-after-free
1889-
// if the user code modifies the sequence during iteration.
18901885
Py_INCREF(obj);
18911886

18921887
if (i) {

0 commit comments

Comments
 (0)