annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/json/decoder.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 """Implementation of JSONDecoder
jpayne@69 2 """
jpayne@69 3 import re
jpayne@69 4
jpayne@69 5 from json import scanner
jpayne@69 6 try:
jpayne@69 7 from _json import scanstring as c_scanstring
jpayne@69 8 except ImportError:
jpayne@69 9 c_scanstring = None
jpayne@69 10
jpayne@69 11 __all__ = ['JSONDecoder', 'JSONDecodeError']
jpayne@69 12
jpayne@69 13 FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
jpayne@69 14
jpayne@69 15 NaN = float('nan')
jpayne@69 16 PosInf = float('inf')
jpayne@69 17 NegInf = float('-inf')
jpayne@69 18
jpayne@69 19
jpayne@69 20 class JSONDecodeError(ValueError):
jpayne@69 21 """Subclass of ValueError with the following additional properties:
jpayne@69 22
jpayne@69 23 msg: The unformatted error message
jpayne@69 24 doc: The JSON document being parsed
jpayne@69 25 pos: The start index of doc where parsing failed
jpayne@69 26 lineno: The line corresponding to pos
jpayne@69 27 colno: The column corresponding to pos
jpayne@69 28
jpayne@69 29 """
jpayne@69 30 # Note that this exception is used from _json
jpayne@69 31 def __init__(self, msg, doc, pos):
jpayne@69 32 lineno = doc.count('\n', 0, pos) + 1
jpayne@69 33 colno = pos - doc.rfind('\n', 0, pos)
jpayne@69 34 errmsg = '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
jpayne@69 35 ValueError.__init__(self, errmsg)
jpayne@69 36 self.msg = msg
jpayne@69 37 self.doc = doc
jpayne@69 38 self.pos = pos
jpayne@69 39 self.lineno = lineno
jpayne@69 40 self.colno = colno
jpayne@69 41
jpayne@69 42 def __reduce__(self):
jpayne@69 43 return self.__class__, (self.msg, self.doc, self.pos)
jpayne@69 44
jpayne@69 45
jpayne@69 46 _CONSTANTS = {
jpayne@69 47 '-Infinity': NegInf,
jpayne@69 48 'Infinity': PosInf,
jpayne@69 49 'NaN': NaN,
jpayne@69 50 }
jpayne@69 51
jpayne@69 52
jpayne@69 53 STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
jpayne@69 54 BACKSLASH = {
jpayne@69 55 '"': '"', '\\': '\\', '/': '/',
jpayne@69 56 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t',
jpayne@69 57 }
jpayne@69 58
jpayne@69 59 def _decode_uXXXX(s, pos):
jpayne@69 60 esc = s[pos + 1:pos + 5]
jpayne@69 61 if len(esc) == 4 and esc[1] not in 'xX':
jpayne@69 62 try:
jpayne@69 63 return int(esc, 16)
jpayne@69 64 except ValueError:
jpayne@69 65 pass
jpayne@69 66 msg = "Invalid \\uXXXX escape"
jpayne@69 67 raise JSONDecodeError(msg, s, pos)
jpayne@69 68
jpayne@69 69 def py_scanstring(s, end, strict=True,
jpayne@69 70 _b=BACKSLASH, _m=STRINGCHUNK.match):
jpayne@69 71 """Scan the string s for a JSON string. End is the index of the
jpayne@69 72 character in s after the quote that started the JSON string.
jpayne@69 73 Unescapes all valid JSON string escape sequences and raises ValueError
jpayne@69 74 on attempt to decode an invalid string. If strict is False then literal
jpayne@69 75 control characters are allowed in the string.
jpayne@69 76
jpayne@69 77 Returns a tuple of the decoded string and the index of the character in s
jpayne@69 78 after the end quote."""
jpayne@69 79 chunks = []
jpayne@69 80 _append = chunks.append
jpayne@69 81 begin = end - 1
jpayne@69 82 while 1:
jpayne@69 83 chunk = _m(s, end)
jpayne@69 84 if chunk is None:
jpayne@69 85 raise JSONDecodeError("Unterminated string starting at", s, begin)
jpayne@69 86 end = chunk.end()
jpayne@69 87 content, terminator = chunk.groups()
jpayne@69 88 # Content is contains zero or more unescaped string characters
jpayne@69 89 if content:
jpayne@69 90 _append(content)
jpayne@69 91 # Terminator is the end of string, a literal control character,
jpayne@69 92 # or a backslash denoting that an escape sequence follows
jpayne@69 93 if terminator == '"':
jpayne@69 94 break
jpayne@69 95 elif terminator != '\\':
jpayne@69 96 if strict:
jpayne@69 97 #msg = "Invalid control character %r at" % (terminator,)
jpayne@69 98 msg = "Invalid control character {0!r} at".format(terminator)
jpayne@69 99 raise JSONDecodeError(msg, s, end)
jpayne@69 100 else:
jpayne@69 101 _append(terminator)
jpayne@69 102 continue
jpayne@69 103 try:
jpayne@69 104 esc = s[end]
jpayne@69 105 except IndexError:
jpayne@69 106 raise JSONDecodeError("Unterminated string starting at",
jpayne@69 107 s, begin) from None
jpayne@69 108 # If not a unicode escape sequence, must be in the lookup table
jpayne@69 109 if esc != 'u':
jpayne@69 110 try:
jpayne@69 111 char = _b[esc]
jpayne@69 112 except KeyError:
jpayne@69 113 msg = "Invalid \\escape: {0!r}".format(esc)
jpayne@69 114 raise JSONDecodeError(msg, s, end)
jpayne@69 115 end += 1
jpayne@69 116 else:
jpayne@69 117 uni = _decode_uXXXX(s, end)
jpayne@69 118 end += 5
jpayne@69 119 if 0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':
jpayne@69 120 uni2 = _decode_uXXXX(s, end + 1)
jpayne@69 121 if 0xdc00 <= uni2 <= 0xdfff:
jpayne@69 122 uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
jpayne@69 123 end += 6
jpayne@69 124 char = chr(uni)
jpayne@69 125 _append(char)
jpayne@69 126 return ''.join(chunks), end
jpayne@69 127
jpayne@69 128
jpayne@69 129 # Use speedup if available
jpayne@69 130 scanstring = c_scanstring or py_scanstring
jpayne@69 131
jpayne@69 132 WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
jpayne@69 133 WHITESPACE_STR = ' \t\n\r'
jpayne@69 134
jpayne@69 135
jpayne@69 136 def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook,
jpayne@69 137 memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
jpayne@69 138 s, end = s_and_end
jpayne@69 139 pairs = []
jpayne@69 140 pairs_append = pairs.append
jpayne@69 141 # Backwards compatibility
jpayne@69 142 if memo is None:
jpayne@69 143 memo = {}
jpayne@69 144 memo_get = memo.setdefault
jpayne@69 145 # Use a slice to prevent IndexError from being raised, the following
jpayne@69 146 # check will raise a more specific ValueError if the string is empty
jpayne@69 147 nextchar = s[end:end + 1]
jpayne@69 148 # Normally we expect nextchar == '"'
jpayne@69 149 if nextchar != '"':
jpayne@69 150 if nextchar in _ws:
jpayne@69 151 end = _w(s, end).end()
jpayne@69 152 nextchar = s[end:end + 1]
jpayne@69 153 # Trivial empty object
jpayne@69 154 if nextchar == '}':
jpayne@69 155 if object_pairs_hook is not None:
jpayne@69 156 result = object_pairs_hook(pairs)
jpayne@69 157 return result, end + 1
jpayne@69 158 pairs = {}
jpayne@69 159 if object_hook is not None:
jpayne@69 160 pairs = object_hook(pairs)
jpayne@69 161 return pairs, end + 1
jpayne@69 162 elif nextchar != '"':
jpayne@69 163 raise JSONDecodeError(
jpayne@69 164 "Expecting property name enclosed in double quotes", s, end)
jpayne@69 165 end += 1
jpayne@69 166 while True:
jpayne@69 167 key, end = scanstring(s, end, strict)
jpayne@69 168 key = memo_get(key, key)
jpayne@69 169 # To skip some function call overhead we optimize the fast paths where
jpayne@69 170 # the JSON key separator is ": " or just ":".
jpayne@69 171 if s[end:end + 1] != ':':
jpayne@69 172 end = _w(s, end).end()
jpayne@69 173 if s[end:end + 1] != ':':
jpayne@69 174 raise JSONDecodeError("Expecting ':' delimiter", s, end)
jpayne@69 175 end += 1
jpayne@69 176
jpayne@69 177 try:
jpayne@69 178 if s[end] in _ws:
jpayne@69 179 end += 1
jpayne@69 180 if s[end] in _ws:
jpayne@69 181 end = _w(s, end + 1).end()
jpayne@69 182 except IndexError:
jpayne@69 183 pass
jpayne@69 184
jpayne@69 185 try:
jpayne@69 186 value, end = scan_once(s, end)
jpayne@69 187 except StopIteration as err:
jpayne@69 188 raise JSONDecodeError("Expecting value", s, err.value) from None
jpayne@69 189 pairs_append((key, value))
jpayne@69 190 try:
jpayne@69 191 nextchar = s[end]
jpayne@69 192 if nextchar in _ws:
jpayne@69 193 end = _w(s, end + 1).end()
jpayne@69 194 nextchar = s[end]
jpayne@69 195 except IndexError:
jpayne@69 196 nextchar = ''
jpayne@69 197 end += 1
jpayne@69 198
jpayne@69 199 if nextchar == '}':
jpayne@69 200 break
jpayne@69 201 elif nextchar != ',':
jpayne@69 202 raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
jpayne@69 203 end = _w(s, end).end()
jpayne@69 204 nextchar = s[end:end + 1]
jpayne@69 205 end += 1
jpayne@69 206 if nextchar != '"':
jpayne@69 207 raise JSONDecodeError(
jpayne@69 208 "Expecting property name enclosed in double quotes", s, end - 1)
jpayne@69 209 if object_pairs_hook is not None:
jpayne@69 210 result = object_pairs_hook(pairs)
jpayne@69 211 return result, end
jpayne@69 212 pairs = dict(pairs)
jpayne@69 213 if object_hook is not None:
jpayne@69 214 pairs = object_hook(pairs)
jpayne@69 215 return pairs, end
jpayne@69 216
jpayne@69 217 def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
jpayne@69 218 s, end = s_and_end
jpayne@69 219 values = []
jpayne@69 220 nextchar = s[end:end + 1]
jpayne@69 221 if nextchar in _ws:
jpayne@69 222 end = _w(s, end + 1).end()
jpayne@69 223 nextchar = s[end:end + 1]
jpayne@69 224 # Look-ahead for trivial empty array
jpayne@69 225 if nextchar == ']':
jpayne@69 226 return values, end + 1
jpayne@69 227 _append = values.append
jpayne@69 228 while True:
jpayne@69 229 try:
jpayne@69 230 value, end = scan_once(s, end)
jpayne@69 231 except StopIteration as err:
jpayne@69 232 raise JSONDecodeError("Expecting value", s, err.value) from None
jpayne@69 233 _append(value)
jpayne@69 234 nextchar = s[end:end + 1]
jpayne@69 235 if nextchar in _ws:
jpayne@69 236 end = _w(s, end + 1).end()
jpayne@69 237 nextchar = s[end:end + 1]
jpayne@69 238 end += 1
jpayne@69 239 if nextchar == ']':
jpayne@69 240 break
jpayne@69 241 elif nextchar != ',':
jpayne@69 242 raise JSONDecodeError("Expecting ',' delimiter", s, end - 1)
jpayne@69 243 try:
jpayne@69 244 if s[end] in _ws:
jpayne@69 245 end += 1
jpayne@69 246 if s[end] in _ws:
jpayne@69 247 end = _w(s, end + 1).end()
jpayne@69 248 except IndexError:
jpayne@69 249 pass
jpayne@69 250
jpayne@69 251 return values, end
jpayne@69 252
jpayne@69 253
jpayne@69 254 class JSONDecoder(object):
jpayne@69 255 """Simple JSON <http://json.org> decoder
jpayne@69 256
jpayne@69 257 Performs the following translations in decoding by default:
jpayne@69 258
jpayne@69 259 +---------------+-------------------+
jpayne@69 260 | JSON | Python |
jpayne@69 261 +===============+===================+
jpayne@69 262 | object | dict |
jpayne@69 263 +---------------+-------------------+
jpayne@69 264 | array | list |
jpayne@69 265 +---------------+-------------------+
jpayne@69 266 | string | str |
jpayne@69 267 +---------------+-------------------+
jpayne@69 268 | number (int) | int |
jpayne@69 269 +---------------+-------------------+
jpayne@69 270 | number (real) | float |
jpayne@69 271 +---------------+-------------------+
jpayne@69 272 | true | True |
jpayne@69 273 +---------------+-------------------+
jpayne@69 274 | false | False |
jpayne@69 275 +---------------+-------------------+
jpayne@69 276 | null | None |
jpayne@69 277 +---------------+-------------------+
jpayne@69 278
jpayne@69 279 It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
jpayne@69 280 their corresponding ``float`` values, which is outside the JSON spec.
jpayne@69 281
jpayne@69 282 """
jpayne@69 283
jpayne@69 284 def __init__(self, *, object_hook=None, parse_float=None,
jpayne@69 285 parse_int=None, parse_constant=None, strict=True,
jpayne@69 286 object_pairs_hook=None):
jpayne@69 287 """``object_hook``, if specified, will be called with the result
jpayne@69 288 of every JSON object decoded and its return value will be used in
jpayne@69 289 place of the given ``dict``. This can be used to provide custom
jpayne@69 290 deserializations (e.g. to support JSON-RPC class hinting).
jpayne@69 291
jpayne@69 292 ``object_pairs_hook``, if specified will be called with the result of
jpayne@69 293 every JSON object decoded with an ordered list of pairs. The return
jpayne@69 294 value of ``object_pairs_hook`` will be used instead of the ``dict``.
jpayne@69 295 This feature can be used to implement custom decoders.
jpayne@69 296 If ``object_hook`` is also defined, the ``object_pairs_hook`` takes
jpayne@69 297 priority.
jpayne@69 298
jpayne@69 299 ``parse_float``, if specified, will be called with the string
jpayne@69 300 of every JSON float to be decoded. By default this is equivalent to
jpayne@69 301 float(num_str). This can be used to use another datatype or parser
jpayne@69 302 for JSON floats (e.g. decimal.Decimal).
jpayne@69 303
jpayne@69 304 ``parse_int``, if specified, will be called with the string
jpayne@69 305 of every JSON int to be decoded. By default this is equivalent to
jpayne@69 306 int(num_str). This can be used to use another datatype or parser
jpayne@69 307 for JSON integers (e.g. float).
jpayne@69 308
jpayne@69 309 ``parse_constant``, if specified, will be called with one of the
jpayne@69 310 following strings: -Infinity, Infinity, NaN.
jpayne@69 311 This can be used to raise an exception if invalid JSON numbers
jpayne@69 312 are encountered.
jpayne@69 313
jpayne@69 314 If ``strict`` is false (true is the default), then control
jpayne@69 315 characters will be allowed inside strings. Control characters in
jpayne@69 316 this context are those with character codes in the 0-31 range,
jpayne@69 317 including ``'\\t'`` (tab), ``'\\n'``, ``'\\r'`` and ``'\\0'``.
jpayne@69 318 """
jpayne@69 319 self.object_hook = object_hook
jpayne@69 320 self.parse_float = parse_float or float
jpayne@69 321 self.parse_int = parse_int or int
jpayne@69 322 self.parse_constant = parse_constant or _CONSTANTS.__getitem__
jpayne@69 323 self.strict = strict
jpayne@69 324 self.object_pairs_hook = object_pairs_hook
jpayne@69 325 self.parse_object = JSONObject
jpayne@69 326 self.parse_array = JSONArray
jpayne@69 327 self.parse_string = scanstring
jpayne@69 328 self.memo = {}
jpayne@69 329 self.scan_once = scanner.make_scanner(self)
jpayne@69 330
jpayne@69 331
jpayne@69 332 def decode(self, s, _w=WHITESPACE.match):
jpayne@69 333 """Return the Python representation of ``s`` (a ``str`` instance
jpayne@69 334 containing a JSON document).
jpayne@69 335
jpayne@69 336 """
jpayne@69 337 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
jpayne@69 338 end = _w(s, end).end()
jpayne@69 339 if end != len(s):
jpayne@69 340 raise JSONDecodeError("Extra data", s, end)
jpayne@69 341 return obj
jpayne@69 342
jpayne@69 343 def raw_decode(self, s, idx=0):
jpayne@69 344 """Decode a JSON document from ``s`` (a ``str`` beginning with
jpayne@69 345 a JSON document) and return a 2-tuple of the Python
jpayne@69 346 representation and the index in ``s`` where the document ended.
jpayne@69 347
jpayne@69 348 This can be used to decode a JSON document from a string that may
jpayne@69 349 have extraneous data at the end.
jpayne@69 350
jpayne@69 351 """
jpayne@69 352 try:
jpayne@69 353 obj, end = self.scan_once(s, idx)
jpayne@69 354 except StopIteration as err:
jpayne@69 355 raise JSONDecodeError("Expecting value", s, err.value) from None
jpayne@69 356 return obj, end