Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions Lib/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
$ echo '{ 1.2:3.4}' | python -m json
Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
"""
__version__ = '2.0.9'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
Expand Down Expand Up @@ -242,7 +241,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
**kw).encode(obj)


_default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None)
_default_decoder = JSONDecoder()


def detect_encoding(b):
Expand Down Expand Up @@ -276,7 +275,8 @@ def detect_encoding(b):


def load(fp, *, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
parse_int=None, parse_constant=None, object_pairs_hook=None,
array_hook=None, **kw):
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
a JSON document) to a Python object.

Expand All @@ -292,17 +292,27 @@ def load(fp, *, cls=None, object_hook=None, parse_float=None,
``object_hook`` is also defined, the ``object_pairs_hook`` takes
priority.

``array_hook`` is an optional function that will be called with the
result of any literal array decode (a ``list``). The return value of
this function will be used instead of the ``list``. This feature can
be used along ``object_pairs_hook`` to customize the resulting data
structure - for example, by setting that to ``frozendict`` and
``array_hook`` to ``tuple``, one can get a deep immutable data structure
from any JSON data.

To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg; otherwise ``JSONDecoder`` is used.
"""
return loads(fp.read(),
cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook,
array_hook=array_hook, **kw)


def loads(s, *, cls=None, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
parse_int=None, parse_constant=None, object_pairs_hook=None,
array_hook=None, **kw):
"""Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance
containing a JSON document) to a Python object.

Expand All @@ -318,6 +328,14 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
``object_hook`` is also defined, the ``object_pairs_hook`` takes
priority.

``array_hook`` is an optional function that will be called with the
result of any literal array decode (a ``list``). The return value of
this function will be used instead of the ``list``. This feature can
be used along ``object_pairs_hook`` to customize the resulting data
structure - for example, by setting that to ``frozendict`` and
``array_hook`` to ``tuple``, one can get a deep immutable data structure
from any JSON data.

``parse_float``, if specified, will be called with the string
of every JSON float to be decoded. By default this is equivalent to
float(num_str). This can be used to use another datatype or parser
Expand Down Expand Up @@ -348,18 +366,30 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,

if (cls is None and object_hook is None and
parse_int is None and parse_float is None and
parse_constant is None and object_pairs_hook is None and not kw):
parse_constant is None and object_pairs_hook is None
and array_hook is None and not kw):
return _default_decoder.decode(s)
if cls is None:
cls = JSONDecoder
if object_hook is not None:
kw['object_hook'] = object_hook
if object_pairs_hook is not None:
kw['object_pairs_hook'] = object_pairs_hook
if array_hook is not None:
kw['array_hook'] = array_hook
if parse_float is not None:
kw['parse_float'] = parse_float
if parse_int is not None:
kw['parse_int'] = parse_int
if parse_constant is not None:
kw['parse_constant'] = parse_constant
return cls(**kw).decode(s)


def __getattr__(name):
if name == "__version__":
from warnings import _deprecated

_deprecated("__version__", remove=(3, 20))
return "2.0.9" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
35 changes: 17 additions & 18 deletions Lib/json/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,6 @@ class JSONDecodeError(ValueError):
colno: The column corresponding to pos

"""
# RUSTPYTHON SPECIFIC
@classmethod
def _from_serde(cls, msg, doc, line, col):
pos = 0
# 0-indexed
line -= 1
col -= 1
while line > 0:
i = doc.index('\n', pos)
line -= 1
pos = i
pos += col
return cls(msg, doc, pos)

# Note that this exception is used from _json
def __init__(self, msg, doc, pos):
lineno = doc.count('\n', 0, pos) + 1
Expand All @@ -57,11 +43,11 @@ def __reduce__(self):
return self.__class__, (self.msg, self.doc, self.pos)


_CONSTANTS = {
_CONSTANTS = frozendict({
'-Infinity': NegInf,
'Infinity': PosInf,
'NaN': NaN,
}
})


HEXDIGITS = re.compile(r'[0-9A-Fa-f]{4}', FLAGS)
Expand Down Expand Up @@ -232,7 +218,7 @@ def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook,
pairs = object_hook(pairs)
return pairs, end

def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
def JSONArray(s_and_end, scan_once, array_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
s, end = s_and_end
values = []
nextchar = s[end:end + 1]
Expand All @@ -241,6 +227,8 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
nextchar = s[end:end + 1]
# Look-ahead for trivial empty array
if nextchar == ']':
if array_hook is not None:
values = array_hook(values)
return values, end + 1
_append = values.append
while True:
Expand Down Expand Up @@ -270,6 +258,8 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
if nextchar == ']':
raise JSONDecodeError("Illegal trailing comma before end of array", s, comma_idx)

if array_hook is not None:
values = array_hook(values)
return values, end


Expand Down Expand Up @@ -305,7 +295,7 @@ class JSONDecoder(object):

def __init__(self, *, object_hook=None, parse_float=None,
parse_int=None, parse_constant=None, strict=True,
object_pairs_hook=None):
object_pairs_hook=None, array_hook=None):
"""``object_hook``, if specified, will be called with the result
of every JSON object decoded and its return value will be used in
place of the given ``dict``. This can be used to provide custom
Expand All @@ -318,6 +308,14 @@ def __init__(self, *, object_hook=None, parse_float=None,
If ``object_hook`` is also defined, the ``object_pairs_hook`` takes
priority.

``array_hook`` is an optional function that will be called with the
result of any literal array decode (a ``list``). The return value of
this function will be used instead of the ``list``. This feature can
be used along ``object_pairs_hook`` to customize the resulting data
structure - for example, by setting that to ``frozendict`` and
``array_hook`` to ``tuple``, one can get a deep immutable data
structute from any JSON data.

``parse_float``, if specified, will be called with the string
of every JSON float to be decoded. By default this is equivalent to
float(num_str). This can be used to use another datatype or parser
Expand All @@ -344,6 +342,7 @@ def __init__(self, *, object_hook=None, parse_float=None,
self.parse_constant = parse_constant or _CONSTANTS.__getitem__
self.strict = strict
self.object_pairs_hook = object_pairs_hook
self.array_hook = array_hook
self.parse_object = JSONObject
self.parse_array = JSONArray
self.parse_string = scanstring
Expand Down
38 changes: 18 additions & 20 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class JSONEncoder(object):
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
| dict, frozendict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
Expand Down Expand Up @@ -264,17 +264,6 @@ def floatstr(o, allow_nan=self.allow_nan,

def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
_key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
## HACK: hand-optimized bytecode; turn globals into locals
ValueError=ValueError,
dict=dict,
float=float,
id=id,
int=int,
isinstance=isinstance,
list=list,
str=str,
tuple=tuple,
_intstr=int.__repr__,
):

def _iterencode_list(lst, _current_indent_level):
Expand Down Expand Up @@ -311,15 +300,15 @@ def _iterencode_list(lst, _current_indent_level):
# Subclasses of int/float may override __repr__, but we still
# want to encode them as integers/floats in JSON. One example
# within the standard library is IntEnum.
yield buf + _intstr(value)
yield buf + int.__repr__(value)
elif isinstance(value, float):
# see comment above for int
yield buf + _floatstr(value)
else:
yield buf
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
elif isinstance(value, dict):
elif isinstance(value, (dict, frozendict)):
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
Expand Down Expand Up @@ -374,7 +363,7 @@ def _iterencode_dict(dct, _current_indent_level):
key = 'null'
elif isinstance(key, int):
# see comment for int/float in _make_iterencode
key = _intstr(key)
key = int.__repr__(key)
elif _skipkeys:
continue
else:
Expand All @@ -399,14 +388,14 @@ def _iterencode_dict(dct, _current_indent_level):
yield 'false'
elif isinstance(value, int):
# see comment for int/float in _make_iterencode
yield _intstr(value)
yield int.__repr__(value)
elif isinstance(value, float):
# see comment for int/float in _make_iterencode
yield _floatstr(value)
else:
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
elif isinstance(value, dict):
elif isinstance(value, (dict, frozendict)):
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
Expand Down Expand Up @@ -434,13 +423,13 @@ def _iterencode(o, _current_indent_level):
yield 'false'
elif isinstance(o, int):
# see comment for int/float in _make_iterencode
yield _intstr(o)
yield int.__repr__(o)
elif isinstance(o, float):
# see comment for int/float in _make_iterencode
yield _floatstr(o)
elif isinstance(o, (list, tuple)):
yield from _iterencode_list(o, _current_indent_level)
elif isinstance(o, dict):
elif isinstance(o, (dict, frozendict)):
yield from _iterencode_dict(o, _current_indent_level)
else:
if markers is not None:
Expand All @@ -458,4 +447,13 @@ def _iterencode(o, _current_indent_level):
raise
if markers is not None:
del markers[markerid]
return _iterencode

def _iterencode_once(o, _current_indent_level):
nonlocal _iterencode, _iterencode_dict, _iterencode_list
try:
yield from _iterencode(o, _current_indent_level)
finally:
# Break reference cycles due to mutually recursive closures:
del _iterencode, _iterencode_dict, _iterencode_list

return _iterencode_once
3 changes: 2 additions & 1 deletion Lib/json/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def py_make_scanner(context):
parse_constant = context.parse_constant
object_hook = context.object_hook
object_pairs_hook = context.object_pairs_hook
array_hook = context.array_hook
memo = context.memo

def _scan_once(string, idx):
Expand All @@ -37,7 +38,7 @@ def _scan_once(string, idx):
return parse_object((string, idx + 1), strict,
_scan_once, object_hook, object_pairs_hook, memo)
elif nextchar == '[':
return parse_array((string, idx + 1), _scan_once)
return parse_array((string, idx + 1), _scan_once, array_hook)
elif nextchar == 'n' and string[idx:idx + 4] == 'null':
return None, idx + 4
elif nextchar == 't' and string[idx:idx + 4] == 'true':
Expand Down
13 changes: 7 additions & 6 deletions Lib/json/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
(?P<null>null)
''', re.VERBOSE)

_group_to_theme_color = {
_group_to_theme_color = frozendict({
"key": "definition",
"string": "string",
"number": "number",
"boolean": "keyword",
"null": "keyword",
}
})


def _colorize_json(json_str, theme):
Expand All @@ -44,20 +44,21 @@ def _replace_match_callback(match):
def main():
description = ('A simple command line interface for json module '
'to validate and pretty-print JSON objects.')
parser = argparse.ArgumentParser(description=description, color=True)
parser = argparse.ArgumentParser(description=description)
parser.add_argument('infile', nargs='?',
help='a JSON file to be validated or pretty-printed',
help='a JSON file to be validated or pretty-printed; '
'defaults to `stdin`',
default='-')
parser.add_argument('outfile', nargs='?',
help='write the output of infile to outfile',
help='write the output of `infile` to `outfile`',
default=None)
parser.add_argument('--sort-keys', action='store_true', default=False,
help='sort the output of dictionaries alphabetically by key')
parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false',
help='disable escaping of non-ASCII characters')
parser.add_argument('--json-lines', action='store_true', default=False,
help='parse input using the JSON Lines format. '
'Use with --no-indent or --compact to produce valid JSON Lines output.')
'Use with `--no-indent` or `--compact` to produce valid JSON Lines output.')
group = parser.add_mutually_exclusive_group()
group.add_argument('--indent', default=4, type=int,
help='separate items with newlines and use this number '
Expand Down
Loading
Loading