This commit is contained in:
Ankit Goel 2025-12-08 06:11:38 +02:00 committed by GitHub
commit 9082a159c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 25 additions and 3 deletions

View file

@ -457,6 +457,9 @@ Encoders and Decoders
This can be used to decode a JSON document from a string that may have This can be used to decode a JSON document from a string that may have
extraneous data at the end. extraneous data at the end.
.. versionchanged:: 3.14
Now ignores any leading whitespace instead of returning an error
.. class:: JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None) .. class:: JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

View file

@ -342,23 +342,27 @@ def decode(self, s, _w=WHITESPACE.match):
containing a JSON document). containing a JSON document).
""" """
obj, end = self.raw_decode(s, idx=_w(s, 0).end()) obj, end = self.raw_decode(s)
end = _w(s, end).end() end = _w(s, end).end()
if end != len(s): if end != len(s):
raise JSONDecodeError("Extra data", s, end) raise JSONDecodeError("Extra data", s, end)
return obj return obj
def raw_decode(self, s, idx=0): def raw_decode(self, s, idx=0, _w=WHITESPACE.match):
"""Decode a JSON document from ``s`` (a ``str`` beginning with """Decode a JSON document from ``s`` (a ``str`` beginning with
a JSON document) and return a 2-tuple of the Python a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended. representation and the index in ``s`` where the document ended.
Whitespace at the beginning of the document will be ignored.
Optionally, ``idx`` can be used to specify an offset in ``s``
where the document begins.
This can be used to decode a JSON document from a string that may This can be used to decode a JSON document from a string that may
have extraneous data at the end. have extraneous data at the end.
""" """
try: try:
obj, end = self.scan_once(s, idx) obj, end = self.scan_once(s, idx=_w(s, idx).end())
except StopIteration as err: except StopIteration as err:
raise JSONDecodeError("Expecting value", s, err.value) from None raise JSONDecodeError("Expecting value", s, err.value) from None
return obj, end return obj, end

View file

@ -130,6 +130,20 @@ def test_limit_int(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
self.loads('1' * (maxdigits + 1)) self.loads('1' * (maxdigits + 1))
class TestRawDecode:
def test_whitespace(self):
decoder = self.json.JSONDecoder()
self.assertEqual(decoder.raw_decode(' {}'), ({}, 3))
self.assertEqual(decoder.raw_decode(' []'), ([], 4))
self.assertEqual(decoder.raw_decode(' ""'), ('', 5))
s = ' { "key" : "value" , "k":"v" } \n' \
' { "key": "value", "k" :"v"} '
val1, n1 = decoder.raw_decode(s)
val2, n2 = decoder.raw_decode(s[n1:])
self.assertEqual(val1, {"key":"value", "k":"v"})
self.assertEqual(val2, {"key":"value", "k":"v"})
class TestPyDecode(TestDecode, PyTest): pass class TestPyDecode(TestDecode, PyTest): pass
class TestCDecode(TestDecode, CTest): pass class TestCDecode(TestDecode, CTest): pass
class TestPyRawDecode(TestRawDecode, PyTest): pass
class TestCRawDecode(TestRawDecode, CTest): pass

View file

@ -0,0 +1 @@
Ignore leading whitespace in :func:`JSONDecoder.raw_decode`