Skip to content

Commit 413e825

Browse files
CPython developersyouknowone
authored andcommitted
Update {test_}json from CPython 3.10.5
1 parent 6bf7599 commit 413e825

File tree

8 files changed

+160
-50
lines changed

8 files changed

+160
-50
lines changed

Lib/json/__init__.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
133133
134134
If ``check_circular`` is false, then the circular reference check
135135
for container types will be skipped and a circular reference will
136-
result in an ``OverflowError`` (or worse).
136+
result in an ``RecursionError`` (or worse).
137137
138138
If ``allow_nan`` is false, then it will be a ``ValueError`` to
139139
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
@@ -195,7 +195,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
195195
196196
If ``check_circular`` is false, then the circular reference check
197197
for container types will be skipped and a circular reference will
198-
result in an ``OverflowError`` (or worse).
198+
result in an ``RecursionError`` (or worse).
199199
200200
If ``allow_nan`` is false, then it will be a ``ValueError`` to
201201
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
@@ -329,8 +329,6 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
329329
330330
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
331331
kwarg; otherwise ``JSONDecoder`` is used.
332-
333-
The ``encoding`` argument is ignored and deprecated since Python 3.1.
334332
"""
335333
if isinstance(s, str):
336334
if s.startswith('\ufeff'):
@@ -342,15 +340,6 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
342340
f'not {s.__class__.__name__}')
343341
s = s.decode(detect_encoding(s), 'surrogatepass')
344342

345-
if "encoding" in kw:
346-
import warnings
347-
warnings.warn(
348-
"'encoding' is ignored and deprecated. It will be removed in Python 3.9",
349-
DeprecationWarning,
350-
stacklevel=2
351-
)
352-
del kw['encoding']
353-
354343
if (cls is None and object_hook is None and
355344
parse_int is None and parse_float is None and
356345
parse_constant is None and object_pairs_hook is None and not kw):

Lib/json/encoder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True,
116116
117117
If check_circular is true, then lists, dicts, and custom encoded
118118
objects will be checked for circular references during encoding to
119-
prevent an infinite recursion (which would cause an OverflowError).
119+
prevent an infinite recursion (which would cause an RecursionError).
120120
Otherwise, no such check takes place.
121121
122122
If allow_nan is true, then NaN, Infinity, and -Infinity will be

Lib/json/tool.py

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import argparse
1414
import json
1515
import sys
16+
from pathlib import Path
1617

1718

1819
def main():
@@ -25,31 +26,60 @@ def main():
2526
help='a JSON file to be validated or pretty-printed',
2627
default=sys.stdin)
2728
parser.add_argument('outfile', nargs='?',
28-
type=argparse.FileType('w', encoding="utf-8"),
29+
type=Path,
2930
help='write the output of infile to outfile',
30-
default=sys.stdout)
31+
default=None)
3132
parser.add_argument('--sort-keys', action='store_true', default=False,
3233
help='sort the output of dictionaries alphabetically by key')
34+
parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false',
35+
help='disable escaping of non-ASCII characters')
3336
parser.add_argument('--json-lines', action='store_true', default=False,
34-
help='parse input using the jsonlines format')
37+
help='parse input using the JSON Lines format. '
38+
'Use with --no-indent or --compact to produce valid JSON Lines output.')
39+
group = parser.add_mutually_exclusive_group()
40+
group.add_argument('--indent', default=4, type=int,
41+
help='separate items with newlines and use this number '
42+
'of spaces for indentation')
43+
group.add_argument('--tab', action='store_const', dest='indent',
44+
const='\t', help='separate items with newlines and use '
45+
'tabs for indentation')
46+
group.add_argument('--no-indent', action='store_const', dest='indent',
47+
const=None,
48+
help='separate items with spaces rather than newlines')
49+
group.add_argument('--compact', action='store_true',
50+
help='suppress all whitespace separation (most compact)')
3551
options = parser.parse_args()
3652

37-
infile = options.infile
38-
outfile = options.outfile
39-
sort_keys = options.sort_keys
40-
json_lines = options.json_lines
41-
with infile, outfile:
53+
dump_args = {
54+
'sort_keys': options.sort_keys,
55+
'indent': options.indent,
56+
'ensure_ascii': options.ensure_ascii,
57+
}
58+
if options.compact:
59+
dump_args['indent'] = None
60+
dump_args['separators'] = ',', ':'
61+
62+
with options.infile as infile:
4263
try:
43-
if json_lines:
64+
if options.json_lines:
4465
objs = (json.loads(line) for line in infile)
4566
else:
46-
objs = (json.load(infile), )
47-
for obj in objs:
48-
json.dump(obj, outfile, sort_keys=sort_keys, indent=4)
49-
outfile.write('\n')
67+
objs = (json.load(infile),)
68+
69+
if options.outfile is None:
70+
out = sys.stdout
71+
else:
72+
out = options.outfile.open('w', encoding='utf-8')
73+
with out as outfile:
74+
for obj in objs:
75+
json.dump(obj, outfile, **dump_args)
76+
outfile.write('\n')
5077
except ValueError as e:
5178
raise SystemExit(e)
5279

5380

5481
if __name__ == '__main__':
55-
main()
82+
try:
83+
main()
84+
except BrokenPipeError as exc:
85+
sys.exit(exc.errno)

Lib/test/test_json/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from test import support
77
from test.support import import_helper
88

9+
910
# import json with and without accelerations
1011
# XXX RUSTPYTHON: we don't import _json as fresh since the fresh module isn't placed
1112
# into the sys.modules cache, and therefore the vm can't recognize the _json.Scanner class
@@ -40,7 +41,6 @@ def test_pyjson(self):
4041
'json.encoder')
4142

4243
class TestCTest(CTest):
43-
@unittest.expectedFailure
4444
def test_cjson(self):
4545
self.assertEqual(self.json.scanner.make_scanner.__module__, '_json')
4646
self.assertEqual(self.json.decoder.scanstring.__module__, '_json')

Lib/test/test_json/test_decode.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ def test_negative_index(self):
9797
d = self.json.JSONDecoder()
9898
self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000)
9999

100-
def test_deprecated_encode(self):
101-
with self.assertWarns(DeprecationWarning):
102-
self.loads('{}', encoding='fake')
103-
104100
class TestPyDecode(TestDecode, PyTest): pass
105101
# TODO: RUSTPYTHON
106102
class TestCDecode(TestDecode, CTest): # pass

Lib/test/test_json/test_recursion.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from test import support
12
from test.test_json import PyTest, CTest
23

34

@@ -52,7 +53,7 @@ def default(self, o):
5253
return [JSONTestObject]
5354
else:
5455
return 'JSONTestObject'
55-
return pyjson.JSONEncoder.default(o)
56+
return self.json.JSONEncoder.default(o)
5657

5758
enc = RecursiveJSONEncoder()
5859
self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"')
@@ -69,21 +70,26 @@ def test_highly_nested_objects_decoding(self):
6970
# test that loading highly-nested objects doesn't segfault when C
7071
# accelerations are used. See #12017
7172
with self.assertRaises(RecursionError):
72-
self.loads('{"a":' * 100000 + '1' + '}' * 100000)
73+
with support.infinite_recursion():
74+
self.loads('{"a":' * 100000 + '1' + '}' * 100000)
7375
with self.assertRaises(RecursionError):
74-
self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
76+
with support.infinite_recursion():
77+
self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
7578
with self.assertRaises(RecursionError):
76-
self.loads('[' * 100000 + '1' + ']' * 100000)
79+
with support.infinite_recursion():
80+
self.loads('[' * 100000 + '1' + ']' * 100000)
7781

7882
def test_highly_nested_objects_encoding(self):
7983
# See #12051
8084
l, d = [], {}
8185
for x in range(100000):
8286
l, d = [l], {'k':d}
8387
with self.assertRaises(RecursionError):
84-
self.dumps(l)
88+
with support.infinite_recursion():
89+
self.dumps(l)
8590
with self.assertRaises(RecursionError):
86-
self.dumps(d)
91+
with support.infinite_recursion():
92+
self.dumps(d)
8793

8894
def test_endless_recursion(self):
8995
# See #12051
@@ -93,7 +99,8 @@ def default(self, o):
9399
return [o]
94100

95101
with self.assertRaises(RecursionError):
96-
EndlessJSONEncoder(check_circular=False).encode(5j)
102+
with support.infinite_recursion():
103+
EndlessJSONEncoder(check_circular=False).encode(5j)
97104

98105

99106
class TestPyRecursion(TestRecursion, PyTest): pass

Lib/test/test_json/test_speedups.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ def bad_encoder2(*args):
6262
with self.assertRaises(ZeroDivisionError):
6363
enc('spam', 4)
6464

65+
def test_bad_markers_argument_to_encoder(self):
66+
# https://bugs.python.org/issue45269
67+
with self.assertRaisesRegex(
68+
TypeError,
69+
r'make_encoder\(\) argument 1 must be dict or None, not int',
70+
):
71+
self.json.encoder.c_make_encoder(1, None, None, None, ': ', ', ',
72+
False, False, False)
73+
6574
# TODO: RUSTPYTHON, translate the encoder to Rust
6675
@unittest.expectedFailure
6776
def test_bad_bool_args(self):

Lib/test/test_json/test_tool.py

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import errno
12
import os
23
import sys
34
import textwrap
45
import unittest
5-
from subprocess import Popen, PIPE
6+
import subprocess
7+
68
from test import support
79
from test.support import os_helper
810
from test.support.script_helper import assert_python_ok
@@ -85,10 +87,9 @@ class TestTool(unittest.TestCase):
8587

8688
def test_stdin_stdout(self):
8789
args = sys.executable, '-m', 'json.tool'
88-
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
89-
out, err = proc.communicate(self.data.encode())
90-
self.assertEqual(out.splitlines(), self.expect.encode().splitlines())
91-
self.assertEqual(err, b'')
90+
process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True)
91+
self.assertEqual(process.stdout, self.expect)
92+
self.assertEqual(process.stderr, '')
9293

9394
def _create_infile(self, data=None):
9495
infile = os_helper.TESTFN
@@ -124,18 +125,26 @@ def test_infile_outfile(self):
124125
outfile = os_helper.TESTFN + '.out'
125126
rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile)
126127
self.addCleanup(os.remove, outfile)
127-
with open(outfile, "r") as fp:
128+
with open(outfile, "r", encoding="utf-8") as fp:
129+
self.assertEqual(fp.read(), self.expect)
130+
self.assertEqual(rc, 0)
131+
self.assertEqual(out, b'')
132+
self.assertEqual(err, b'')
133+
134+
def test_writing_in_place(self):
135+
infile = self._create_infile()
136+
rc, out, err = assert_python_ok('-m', 'json.tool', infile, infile)
137+
with open(infile, "r", encoding="utf-8") as fp:
128138
self.assertEqual(fp.read(), self.expect)
129139
self.assertEqual(rc, 0)
130140
self.assertEqual(out, b'')
131141
self.assertEqual(err, b'')
132142

133143
def test_jsonlines(self):
134144
args = sys.executable, '-m', 'json.tool', '--json-lines'
135-
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
136-
out, err = proc.communicate(self.jsonlines_raw.encode())
137-
self.assertEqual(out.splitlines(), self.jsonlines_expect.encode().splitlines())
138-
self.assertEqual(err, b'')
145+
process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True)
146+
self.assertEqual(process.stdout, self.jsonlines_expect)
147+
self.assertEqual(process.stderr, '')
139148

140149
def test_help_flag(self):
141150
rc, out, err = assert_python_ok('-m', 'json.tool', '-h')
@@ -150,3 +159,73 @@ def test_sort_keys_flag(self):
150159
self.assertEqual(out.splitlines(),
151160
self.expect_without_sort_keys.encode().splitlines())
152161
self.assertEqual(err, b'')
162+
163+
def test_indent(self):
164+
input_ = '[1, 2]'
165+
expect = textwrap.dedent('''\
166+
[
167+
1,
168+
2
169+
]
170+
''')
171+
args = sys.executable, '-m', 'json.tool', '--indent', '2'
172+
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
173+
self.assertEqual(process.stdout, expect)
174+
self.assertEqual(process.stderr, '')
175+
176+
def test_no_indent(self):
177+
input_ = '[1,\n2]'
178+
expect = '[1, 2]\n'
179+
args = sys.executable, '-m', 'json.tool', '--no-indent'
180+
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
181+
self.assertEqual(process.stdout, expect)
182+
self.assertEqual(process.stderr, '')
183+
184+
def test_tab(self):
185+
input_ = '[1, 2]'
186+
expect = '[\n\t1,\n\t2\n]\n'
187+
args = sys.executable, '-m', 'json.tool', '--tab'
188+
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
189+
self.assertEqual(process.stdout, expect)
190+
self.assertEqual(process.stderr, '')
191+
192+
def test_compact(self):
193+
input_ = '[ 1 ,\n 2]'
194+
expect = '[1,2]\n'
195+
args = sys.executable, '-m', 'json.tool', '--compact'
196+
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
197+
self.assertEqual(process.stdout, expect)
198+
self.assertEqual(process.stderr, '')
199+
200+
def test_no_ensure_ascii_flag(self):
201+
infile = self._create_infile('{"key":"💩"}')
202+
outfile = os_helper.TESTFN + '.out'
203+
self.addCleanup(os.remove, outfile)
204+
assert_python_ok('-m', 'json.tool', '--no-ensure-ascii', infile, outfile)
205+
with open(outfile, "rb") as f:
206+
lines = f.read().splitlines()
207+
# asserting utf-8 encoded output file
208+
expected = [b'{', b' "key": "\xf0\x9f\x92\xa9"', b"}"]
209+
self.assertEqual(lines, expected)
210+
211+
def test_ensure_ascii_default(self):
212+
infile = self._create_infile('{"key":"💩"}')
213+
outfile = os_helper.TESTFN + '.out'
214+
self.addCleanup(os.remove, outfile)
215+
assert_python_ok('-m', 'json.tool', infile, outfile)
216+
with open(outfile, "rb") as f:
217+
lines = f.read().splitlines()
218+
# asserting an ascii encoded output file
219+
expected = [b'{', rb' "key": "\ud83d\udca9"', b"}"]
220+
self.assertEqual(lines, expected)
221+
222+
@unittest.skipIf(sys.platform =="win32", "The test is failed with ValueError on Windows")
223+
def test_broken_pipe_error(self):
224+
cmd = [sys.executable, '-m', 'json.tool']
225+
proc = subprocess.Popen(cmd,
226+
stdout=subprocess.PIPE,
227+
stdin=subprocess.PIPE)
228+
# bpo-39828: Closing before json.tool attempts to write into stdout.
229+
proc.stdout.close()
230+
proc.communicate(b'"{}"')
231+
self.assertEqual(proc.returncode, errno.EPIPE)

0 commit comments

Comments
 (0)