Skip to content

Commit c7467e4

Browse files
authored
Merge pull request RustPython#1839 from RustPython/coolreader18/stdlib-json-py
Replace the json module with the Python implementation from CPython
2 parents 9420622 + 95d12d0 commit c7467e4

31 files changed

+2816
-96
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/json/__init__.py

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
2+
JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
3+
interchange format.
4+
5+
:mod:`json` exposes an API familiar to users of the standard library
6+
:mod:`marshal` and :mod:`pickle` modules. It is derived from a
7+
version of the externally maintained simplejson library.
8+
9+
Encoding basic Python object hierarchies::
10+
11+
>>> import json
12+
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
13+
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
14+
>>> print(json.dumps("\"foo\bar"))
15+
"\"foo\bar"
16+
>>> print(json.dumps('\u1234'))
17+
"\u1234"
18+
>>> print(json.dumps('\\'))
19+
"\\"
20+
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
21+
{"a": 0, "b": 0, "c": 0}
22+
>>> from io import StringIO
23+
>>> io = StringIO()
24+
>>> json.dump(['streaming API'], io)
25+
>>> io.getvalue()
26+
'["streaming API"]'
27+
28+
Compact encoding::
29+
30+
>>> import json
31+
>>> mydict = {'4': 5, '6': 7}
32+
>>> json.dumps([1,2,3,mydict], separators=(',', ':'))
33+
'[1,2,3,{"4":5,"6":7}]'
34+
35+
Pretty printing::
36+
37+
>>> import json
38+
>>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4))
39+
{
40+
"4": 5,
41+
"6": 7
42+
}
43+
44+
Decoding JSON::
45+
46+
>>> import json
47+
>>> obj = ['foo', {'bar': ['baz', None, 1.0, 2]}]
48+
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
49+
True
50+
>>> json.loads('"\\"foo\\bar"') == '"foo\x08ar'
51+
True
52+
>>> from io import StringIO
53+
>>> io = StringIO('["streaming API"]')
54+
>>> json.load(io)[0] == 'streaming API'
55+
True
56+
57+
Specializing JSON object decoding::
58+
59+
>>> import json
60+
>>> def as_complex(dct):
61+
... if '__complex__' in dct:
62+
... return complex(dct['real'], dct['imag'])
63+
... return dct
64+
...
65+
>>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
66+
... object_hook=as_complex)
67+
(1+2j)
68+
>>> from decimal import Decimal
69+
>>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1')
70+
True
71+
72+
Specializing JSON object encoding::
73+
74+
>>> import json
75+
>>> def encode_complex(obj):
76+
... if isinstance(obj, complex):
77+
... return [obj.real, obj.imag]
78+
... raise TypeError(f'Object of type {obj.__class__.__name__} '
79+
... f'is not JSON serializable')
80+
...
81+
>>> json.dumps(2 + 1j, default=encode_complex)
82+
'[2.0, 1.0]'
83+
>>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
84+
'[2.0, 1.0]'
85+
>>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
86+
'[2.0, 1.0]'
87+
88+
89+
Using json.tool from the shell to validate and pretty-print::
90+
91+
$ echo '{"json":"obj"}' | python -m json.tool
92+
{
93+
"json": "obj"
94+
}
95+
$ echo '{ 1.2:3.4}' | python -m json.tool
96+
Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
97+
"""
98+
__version__ = '2.0.9'
99+
__all__ = [
100+
'dump', 'dumps', 'load', 'loads',
101+
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
102+
]
103+
104+
__author__ = 'Bob Ippolito <bob@redivi.com>'
105+
106+
from .decoder import JSONDecoder, JSONDecodeError
107+
from .encoder import JSONEncoder
108+
import codecs
109+
110+
_default_encoder = JSONEncoder(
111+
skipkeys=False,
112+
ensure_ascii=True,
113+
check_circular=True,
114+
allow_nan=True,
115+
indent=None,
116+
separators=None,
117+
default=None,
118+
)
119+
120+
def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
121+
allow_nan=True, cls=None, indent=None, separators=None,
122+
default=None, sort_keys=False, **kw):
123+
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
124+
``.write()``-supporting file-like object).
125+
126+
If ``skipkeys`` is true then ``dict`` keys that are not basic types
127+
(``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
128+
instead of raising a ``TypeError``.
129+
130+
If ``ensure_ascii`` is false, then the strings written to ``fp`` can
131+
contain non-ASCII characters if they appear in strings contained in
132+
``obj``. Otherwise, all such characters are escaped in JSON strings.
133+
134+
If ``check_circular`` is false, then the circular reference check
135+
for container types will be skipped and a circular reference will
136+
result in an ``OverflowError`` (or worse).
137+
138+
If ``allow_nan`` is false, then it will be a ``ValueError`` to
139+
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
140+
in strict compliance of the JSON specification, instead of using the
141+
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
142+
143+
If ``indent`` is a non-negative integer, then JSON array elements and
144+
object members will be pretty-printed with that indent level. An indent
145+
level of 0 will only insert newlines. ``None`` is the most compact
146+
representation.
147+
148+
If specified, ``separators`` should be an ``(item_separator, key_separator)``
149+
tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
150+
``(',', ': ')`` otherwise. To get the most compact JSON representation,
151+
you should specify ``(',', ':')`` to eliminate whitespace.
152+
153+
``default(obj)`` is a function that should return a serializable version
154+
of obj or raise TypeError. The default simply raises TypeError.
155+
156+
If *sort_keys* is true (default: ``False``), then the output of
157+
dictionaries will be sorted by key.
158+
159+
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
160+
``.default()`` method to serialize additional types), specify it with
161+
the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.
162+
163+
"""
164+
# cached encoder
165+
if (not skipkeys and ensure_ascii and
166+
check_circular and allow_nan and
167+
cls is None and indent is None and separators is None and
168+
default is None and not sort_keys and not kw):
169+
iterable = _default_encoder.iterencode(obj)
170+
else:
171+
if cls is None:
172+
cls = JSONEncoder
173+
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
174+
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
175+
separators=separators,
176+
default=default, sort_keys=sort_keys, **kw).iterencode(obj)
177+
# could accelerate with writelines in some versions of Python, at
178+
# a debuggability cost
179+
for chunk in iterable:
180+
fp.write(chunk)
181+
182+
183+
def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
184+
allow_nan=True, cls=None, indent=None, separators=None,
185+
default=None, sort_keys=False, **kw):
186+
"""Serialize ``obj`` to a JSON formatted ``str``.
187+
188+
If ``skipkeys`` is true then ``dict`` keys that are not basic types
189+
(``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
190+
instead of raising a ``TypeError``.
191+
192+
If ``ensure_ascii`` is false, then the return value can contain non-ASCII
193+
characters if they appear in strings contained in ``obj``. Otherwise, all
194+
such characters are escaped in JSON strings.
195+
196+
If ``check_circular`` is false, then the circular reference check
197+
for container types will be skipped and a circular reference will
198+
result in an ``OverflowError`` (or worse).
199+
200+
If ``allow_nan`` is false, then it will be a ``ValueError`` to
201+
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
202+
strict compliance of the JSON specification, instead of using the
203+
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
204+
205+
If ``indent`` is a non-negative integer, then JSON array elements and
206+
object members will be pretty-printed with that indent level. An indent
207+
level of 0 will only insert newlines. ``None`` is the most compact
208+
representation.
209+
210+
If specified, ``separators`` should be an ``(item_separator, key_separator)``
211+
tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
212+
``(',', ': ')`` otherwise. To get the most compact JSON representation,
213+
you should specify ``(',', ':')`` to eliminate whitespace.
214+
215+
``default(obj)`` is a function that should return a serializable version
216+
of obj or raise TypeError. The default simply raises TypeError.
217+
218+
If *sort_keys* is true (default: ``False``), then the output of
219+
dictionaries will be sorted by key.
220+
221+
To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
222+
``.default()`` method to serialize additional types), specify it with
223+
the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.
224+
225+
"""
226+
# cached encoder
227+
if (not skipkeys and ensure_ascii and
228+
check_circular and allow_nan and
229+
cls is None and indent is None and separators is None and
230+
default is None and not sort_keys and not kw):
231+
return _default_encoder.encode(obj)
232+
if cls is None:
233+
cls = JSONEncoder
234+
return cls(
235+
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
236+
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
237+
separators=separators, default=default, sort_keys=sort_keys,
238+
**kw).encode(obj)
239+
240+
241+
_default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None)
242+
243+
244+
def detect_encoding(b):
245+
bstartswith = b.startswith
246+
if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
247+
return 'utf-32'
248+
if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
249+
return 'utf-16'
250+
if bstartswith(codecs.BOM_UTF8):
251+
return 'utf-8-sig'
252+
253+
if len(b) >= 4:
254+
if not b[0]:
255+
# 00 00 -- -- - utf-32-be
256+
# 00 XX -- -- - utf-16-be
257+
return 'utf-16-be' if b[1] else 'utf-32-be'
258+
if not b[1]:
259+
# XX 00 00 00 - utf-32-le
260+
# XX 00 00 XX - utf-16-le
261+
# XX 00 XX -- - utf-16-le
262+
return 'utf-16-le' if b[2] or b[3] else 'utf-32-le'
263+
elif len(b) == 2:
264+
if not b[0]:
265+
# 00 XX - utf-16-be
266+
return 'utf-16-be'
267+
if not b[1]:
268+
# XX 00 - utf-16-le
269+
return 'utf-16-le'
270+
# default
271+
return 'utf-8'
272+
273+
274+
def load(fp, *, cls=None, object_hook=None, parse_float=None,
275+
parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
276+
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
277+
a JSON document) to a Python object.
278+
279+
``object_hook`` is an optional function that will be called with the
280+
result of any object literal decode (a ``dict``). The return value of
281+
``object_hook`` will be used instead of the ``dict``. This feature
282+
can be used to implement custom decoders (e.g. JSON-RPC class hinting).
283+
284+
``object_pairs_hook`` is an optional function that will be called with the
285+
result of any object literal decoded with an ordered list of pairs. The
286+
return value of ``object_pairs_hook`` will be used instead of the ``dict``.
287+
This feature can be used to implement custom decoders. If ``object_hook``
288+
is also defined, the ``object_pairs_hook`` takes priority.
289+
290+
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
291+
kwarg; otherwise ``JSONDecoder`` is used.
292+
"""
293+
return loads(fp.read(),
294+
cls=cls, object_hook=object_hook,
295+
parse_float=parse_float, parse_int=parse_int,
296+
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
297+
298+
299+
def loads(s, *, cls=None, object_hook=None, parse_float=None,
300+
parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
301+
"""Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance
302+
containing a JSON document) to a Python object.
303+
304+
``object_hook`` is an optional function that will be called with the
305+
result of any object literal decode (a ``dict``). The return value of
306+
``object_hook`` will be used instead of the ``dict``. This feature
307+
can be used to implement custom decoders (e.g. JSON-RPC class hinting).
308+
309+
``object_pairs_hook`` is an optional function that will be called with the
310+
result of any object literal decoded with an ordered list of pairs. The
311+
return value of ``object_pairs_hook`` will be used instead of the ``dict``.
312+
This feature can be used to implement custom decoders. If ``object_hook``
313+
is also defined, the ``object_pairs_hook`` takes priority.
314+
315+
``parse_float``, if specified, will be called with the string
316+
of every JSON float to be decoded. By default this is equivalent to
317+
float(num_str). This can be used to use another datatype or parser
318+
for JSON floats (e.g. decimal.Decimal).
319+
320+
``parse_int``, if specified, will be called with the string
321+
of every JSON int to be decoded. By default this is equivalent to
322+
int(num_str). This can be used to use another datatype or parser
323+
for JSON integers (e.g. float).
324+
325+
``parse_constant``, if specified, will be called with one of the
326+
following strings: -Infinity, Infinity, NaN.
327+
This can be used to raise an exception if invalid JSON numbers
328+
are encountered.
329+
330+
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
331+
kwarg; otherwise ``JSONDecoder`` is used.
332+
333+
The ``encoding`` argument is ignored and deprecated since Python 3.1.
334+
"""
335+
if isinstance(s, str):
336+
if s.startswith('\ufeff'):
337+
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
338+
s, 0)
339+
else:
340+
if not isinstance(s, (bytes, bytearray)):
341+
raise TypeError(f'the JSON object must be str, bytes or bytearray, '
342+
f'not {s.__class__.__name__}')
343+
s = s.decode(detect_encoding(s), 'surrogatepass')
344+
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+
354+
if (cls is None and object_hook is None and
355+
parse_int is None and parse_float is None and
356+
parse_constant is None and object_pairs_hook is None and not kw):
357+
return _default_decoder.decode(s)
358+
if cls is None:
359+
cls = JSONDecoder
360+
if object_hook is not None:
361+
kw['object_hook'] = object_hook
362+
if object_pairs_hook is not None:
363+
kw['object_pairs_hook'] = object_pairs_hook
364+
if parse_float is not None:
365+
kw['parse_float'] = parse_float
366+
if parse_int is not None:
367+
kw['parse_int'] = parse_int
368+
if parse_constant is not None:
369+
kw['parse_constant'] = parse_constant
370+
return cls(**kw).decode(s)

0 commit comments

Comments
 (0)