annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/json/encoder.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 JSONEncoder
jpayne@69 2 """
jpayne@69 3 import re
jpayne@69 4
jpayne@69 5 try:
jpayne@69 6 from _json import encode_basestring_ascii as c_encode_basestring_ascii
jpayne@69 7 except ImportError:
jpayne@69 8 c_encode_basestring_ascii = None
jpayne@69 9 try:
jpayne@69 10 from _json import encode_basestring as c_encode_basestring
jpayne@69 11 except ImportError:
jpayne@69 12 c_encode_basestring = None
jpayne@69 13 try:
jpayne@69 14 from _json import make_encoder as c_make_encoder
jpayne@69 15 except ImportError:
jpayne@69 16 c_make_encoder = None
jpayne@69 17
jpayne@69 18 ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
jpayne@69 19 ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
jpayne@69 20 HAS_UTF8 = re.compile(b'[\x80-\xff]')
jpayne@69 21 ESCAPE_DCT = {
jpayne@69 22 '\\': '\\\\',
jpayne@69 23 '"': '\\"',
jpayne@69 24 '\b': '\\b',
jpayne@69 25 '\f': '\\f',
jpayne@69 26 '\n': '\\n',
jpayne@69 27 '\r': '\\r',
jpayne@69 28 '\t': '\\t',
jpayne@69 29 }
jpayne@69 30 for i in range(0x20):
jpayne@69 31 ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
jpayne@69 32 #ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
jpayne@69 33
jpayne@69 34 INFINITY = float('inf')
jpayne@69 35
jpayne@69 36 def py_encode_basestring(s):
jpayne@69 37 """Return a JSON representation of a Python string
jpayne@69 38
jpayne@69 39 """
jpayne@69 40 def replace(match):
jpayne@69 41 return ESCAPE_DCT[match.group(0)]
jpayne@69 42 return '"' + ESCAPE.sub(replace, s) + '"'
jpayne@69 43
jpayne@69 44
jpayne@69 45 encode_basestring = (c_encode_basestring or py_encode_basestring)
jpayne@69 46
jpayne@69 47
jpayne@69 48 def py_encode_basestring_ascii(s):
jpayne@69 49 """Return an ASCII-only JSON representation of a Python string
jpayne@69 50
jpayne@69 51 """
jpayne@69 52 def replace(match):
jpayne@69 53 s = match.group(0)
jpayne@69 54 try:
jpayne@69 55 return ESCAPE_DCT[s]
jpayne@69 56 except KeyError:
jpayne@69 57 n = ord(s)
jpayne@69 58 if n < 0x10000:
jpayne@69 59 return '\\u{0:04x}'.format(n)
jpayne@69 60 #return '\\u%04x' % (n,)
jpayne@69 61 else:
jpayne@69 62 # surrogate pair
jpayne@69 63 n -= 0x10000
jpayne@69 64 s1 = 0xd800 | ((n >> 10) & 0x3ff)
jpayne@69 65 s2 = 0xdc00 | (n & 0x3ff)
jpayne@69 66 return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
jpayne@69 67 return '"' + ESCAPE_ASCII.sub(replace, s) + '"'
jpayne@69 68
jpayne@69 69
jpayne@69 70 encode_basestring_ascii = (
jpayne@69 71 c_encode_basestring_ascii or py_encode_basestring_ascii)
jpayne@69 72
jpayne@69 73 class JSONEncoder(object):
jpayne@69 74 """Extensible JSON <http://json.org> encoder for Python data structures.
jpayne@69 75
jpayne@69 76 Supports the following objects and types by default:
jpayne@69 77
jpayne@69 78 +-------------------+---------------+
jpayne@69 79 | Python | JSON |
jpayne@69 80 +===================+===============+
jpayne@69 81 | dict | object |
jpayne@69 82 +-------------------+---------------+
jpayne@69 83 | list, tuple | array |
jpayne@69 84 +-------------------+---------------+
jpayne@69 85 | str | string |
jpayne@69 86 +-------------------+---------------+
jpayne@69 87 | int, float | number |
jpayne@69 88 +-------------------+---------------+
jpayne@69 89 | True | true |
jpayne@69 90 +-------------------+---------------+
jpayne@69 91 | False | false |
jpayne@69 92 +-------------------+---------------+
jpayne@69 93 | None | null |
jpayne@69 94 +-------------------+---------------+
jpayne@69 95
jpayne@69 96 To extend this to recognize other objects, subclass and implement a
jpayne@69 97 ``.default()`` method with another method that returns a serializable
jpayne@69 98 object for ``o`` if possible, otherwise it should call the superclass
jpayne@69 99 implementation (to raise ``TypeError``).
jpayne@69 100
jpayne@69 101 """
jpayne@69 102 item_separator = ', '
jpayne@69 103 key_separator = ': '
jpayne@69 104 def __init__(self, *, skipkeys=False, ensure_ascii=True,
jpayne@69 105 check_circular=True, allow_nan=True, sort_keys=False,
jpayne@69 106 indent=None, separators=None, default=None):
jpayne@69 107 """Constructor for JSONEncoder, with sensible defaults.
jpayne@69 108
jpayne@69 109 If skipkeys is false, then it is a TypeError to attempt
jpayne@69 110 encoding of keys that are not str, int, float or None. If
jpayne@69 111 skipkeys is True, such items are simply skipped.
jpayne@69 112
jpayne@69 113 If ensure_ascii is true, the output is guaranteed to be str
jpayne@69 114 objects with all incoming non-ASCII characters escaped. If
jpayne@69 115 ensure_ascii is false, the output can contain non-ASCII characters.
jpayne@69 116
jpayne@69 117 If check_circular is true, then lists, dicts, and custom encoded
jpayne@69 118 objects will be checked for circular references during encoding to
jpayne@69 119 prevent an infinite recursion (which would cause an OverflowError).
jpayne@69 120 Otherwise, no such check takes place.
jpayne@69 121
jpayne@69 122 If allow_nan is true, then NaN, Infinity, and -Infinity will be
jpayne@69 123 encoded as such. This behavior is not JSON specification compliant,
jpayne@69 124 but is consistent with most JavaScript based encoders and decoders.
jpayne@69 125 Otherwise, it will be a ValueError to encode such floats.
jpayne@69 126
jpayne@69 127 If sort_keys is true, then the output of dictionaries will be
jpayne@69 128 sorted by key; this is useful for regression tests to ensure
jpayne@69 129 that JSON serializations can be compared on a day-to-day basis.
jpayne@69 130
jpayne@69 131 If indent is a non-negative integer, then JSON array
jpayne@69 132 elements and object members will be pretty-printed with that
jpayne@69 133 indent level. An indent level of 0 will only insert newlines.
jpayne@69 134 None is the most compact representation.
jpayne@69 135
jpayne@69 136 If specified, separators should be an (item_separator, key_separator)
jpayne@69 137 tuple. The default is (', ', ': ') if *indent* is ``None`` and
jpayne@69 138 (',', ': ') otherwise. To get the most compact JSON representation,
jpayne@69 139 you should specify (',', ':') to eliminate whitespace.
jpayne@69 140
jpayne@69 141 If specified, default is a function that gets called for objects
jpayne@69 142 that can't otherwise be serialized. It should return a JSON encodable
jpayne@69 143 version of the object or raise a ``TypeError``.
jpayne@69 144
jpayne@69 145 """
jpayne@69 146
jpayne@69 147 self.skipkeys = skipkeys
jpayne@69 148 self.ensure_ascii = ensure_ascii
jpayne@69 149 self.check_circular = check_circular
jpayne@69 150 self.allow_nan = allow_nan
jpayne@69 151 self.sort_keys = sort_keys
jpayne@69 152 self.indent = indent
jpayne@69 153 if separators is not None:
jpayne@69 154 self.item_separator, self.key_separator = separators
jpayne@69 155 elif indent is not None:
jpayne@69 156 self.item_separator = ','
jpayne@69 157 if default is not None:
jpayne@69 158 self.default = default
jpayne@69 159
jpayne@69 160 def default(self, o):
jpayne@69 161 """Implement this method in a subclass such that it returns
jpayne@69 162 a serializable object for ``o``, or calls the base implementation
jpayne@69 163 (to raise a ``TypeError``).
jpayne@69 164
jpayne@69 165 For example, to support arbitrary iterators, you could
jpayne@69 166 implement default like this::
jpayne@69 167
jpayne@69 168 def default(self, o):
jpayne@69 169 try:
jpayne@69 170 iterable = iter(o)
jpayne@69 171 except TypeError:
jpayne@69 172 pass
jpayne@69 173 else:
jpayne@69 174 return list(iterable)
jpayne@69 175 # Let the base class default method raise the TypeError
jpayne@69 176 return JSONEncoder.default(self, o)
jpayne@69 177
jpayne@69 178 """
jpayne@69 179 raise TypeError(f'Object of type {o.__class__.__name__} '
jpayne@69 180 f'is not JSON serializable')
jpayne@69 181
jpayne@69 182 def encode(self, o):
jpayne@69 183 """Return a JSON string representation of a Python data structure.
jpayne@69 184
jpayne@69 185 >>> from json.encoder import JSONEncoder
jpayne@69 186 >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
jpayne@69 187 '{"foo": ["bar", "baz"]}'
jpayne@69 188
jpayne@69 189 """
jpayne@69 190 # This is for extremely simple cases and benchmarks.
jpayne@69 191 if isinstance(o, str):
jpayne@69 192 if self.ensure_ascii:
jpayne@69 193 return encode_basestring_ascii(o)
jpayne@69 194 else:
jpayne@69 195 return encode_basestring(o)
jpayne@69 196 # This doesn't pass the iterator directly to ''.join() because the
jpayne@69 197 # exceptions aren't as detailed. The list call should be roughly
jpayne@69 198 # equivalent to the PySequence_Fast that ''.join() would do.
jpayne@69 199 chunks = self.iterencode(o, _one_shot=True)
jpayne@69 200 if not isinstance(chunks, (list, tuple)):
jpayne@69 201 chunks = list(chunks)
jpayne@69 202 return ''.join(chunks)
jpayne@69 203
jpayne@69 204 def iterencode(self, o, _one_shot=False):
jpayne@69 205 """Encode the given object and yield each string
jpayne@69 206 representation as available.
jpayne@69 207
jpayne@69 208 For example::
jpayne@69 209
jpayne@69 210 for chunk in JSONEncoder().iterencode(bigobject):
jpayne@69 211 mysocket.write(chunk)
jpayne@69 212
jpayne@69 213 """
jpayne@69 214 if self.check_circular:
jpayne@69 215 markers = {}
jpayne@69 216 else:
jpayne@69 217 markers = None
jpayne@69 218 if self.ensure_ascii:
jpayne@69 219 _encoder = encode_basestring_ascii
jpayne@69 220 else:
jpayne@69 221 _encoder = encode_basestring
jpayne@69 222
jpayne@69 223 def floatstr(o, allow_nan=self.allow_nan,
jpayne@69 224 _repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY):
jpayne@69 225 # Check for specials. Note that this type of test is processor
jpayne@69 226 # and/or platform-specific, so do tests which don't depend on the
jpayne@69 227 # internals.
jpayne@69 228
jpayne@69 229 if o != o:
jpayne@69 230 text = 'NaN'
jpayne@69 231 elif o == _inf:
jpayne@69 232 text = 'Infinity'
jpayne@69 233 elif o == _neginf:
jpayne@69 234 text = '-Infinity'
jpayne@69 235 else:
jpayne@69 236 return _repr(o)
jpayne@69 237
jpayne@69 238 if not allow_nan:
jpayne@69 239 raise ValueError(
jpayne@69 240 "Out of range float values are not JSON compliant: " +
jpayne@69 241 repr(o))
jpayne@69 242
jpayne@69 243 return text
jpayne@69 244
jpayne@69 245
jpayne@69 246 if (_one_shot and c_make_encoder is not None
jpayne@69 247 and self.indent is None):
jpayne@69 248 _iterencode = c_make_encoder(
jpayne@69 249 markers, self.default, _encoder, self.indent,
jpayne@69 250 self.key_separator, self.item_separator, self.sort_keys,
jpayne@69 251 self.skipkeys, self.allow_nan)
jpayne@69 252 else:
jpayne@69 253 _iterencode = _make_iterencode(
jpayne@69 254 markers, self.default, _encoder, self.indent, floatstr,
jpayne@69 255 self.key_separator, self.item_separator, self.sort_keys,
jpayne@69 256 self.skipkeys, _one_shot)
jpayne@69 257 return _iterencode(o, 0)
jpayne@69 258
jpayne@69 259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
jpayne@69 260 _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
jpayne@69 261 ## HACK: hand-optimized bytecode; turn globals into locals
jpayne@69 262 ValueError=ValueError,
jpayne@69 263 dict=dict,
jpayne@69 264 float=float,
jpayne@69 265 id=id,
jpayne@69 266 int=int,
jpayne@69 267 isinstance=isinstance,
jpayne@69 268 list=list,
jpayne@69 269 str=str,
jpayne@69 270 tuple=tuple,
jpayne@69 271 _intstr=int.__repr__,
jpayne@69 272 ):
jpayne@69 273
jpayne@69 274 if _indent is not None and not isinstance(_indent, str):
jpayne@69 275 _indent = ' ' * _indent
jpayne@69 276
jpayne@69 277 def _iterencode_list(lst, _current_indent_level):
jpayne@69 278 if not lst:
jpayne@69 279 yield '[]'
jpayne@69 280 return
jpayne@69 281 if markers is not None:
jpayne@69 282 markerid = id(lst)
jpayne@69 283 if markerid in markers:
jpayne@69 284 raise ValueError("Circular reference detected")
jpayne@69 285 markers[markerid] = lst
jpayne@69 286 buf = '['
jpayne@69 287 if _indent is not None:
jpayne@69 288 _current_indent_level += 1
jpayne@69 289 newline_indent = '\n' + _indent * _current_indent_level
jpayne@69 290 separator = _item_separator + newline_indent
jpayne@69 291 buf += newline_indent
jpayne@69 292 else:
jpayne@69 293 newline_indent = None
jpayne@69 294 separator = _item_separator
jpayne@69 295 first = True
jpayne@69 296 for value in lst:
jpayne@69 297 if first:
jpayne@69 298 first = False
jpayne@69 299 else:
jpayne@69 300 buf = separator
jpayne@69 301 if isinstance(value, str):
jpayne@69 302 yield buf + _encoder(value)
jpayne@69 303 elif value is None:
jpayne@69 304 yield buf + 'null'
jpayne@69 305 elif value is True:
jpayne@69 306 yield buf + 'true'
jpayne@69 307 elif value is False:
jpayne@69 308 yield buf + 'false'
jpayne@69 309 elif isinstance(value, int):
jpayne@69 310 # Subclasses of int/float may override __repr__, but we still
jpayne@69 311 # want to encode them as integers/floats in JSON. One example
jpayne@69 312 # within the standard library is IntEnum.
jpayne@69 313 yield buf + _intstr(value)
jpayne@69 314 elif isinstance(value, float):
jpayne@69 315 # see comment above for int
jpayne@69 316 yield buf + _floatstr(value)
jpayne@69 317 else:
jpayne@69 318 yield buf
jpayne@69 319 if isinstance(value, (list, tuple)):
jpayne@69 320 chunks = _iterencode_list(value, _current_indent_level)
jpayne@69 321 elif isinstance(value, dict):
jpayne@69 322 chunks = _iterencode_dict(value, _current_indent_level)
jpayne@69 323 else:
jpayne@69 324 chunks = _iterencode(value, _current_indent_level)
jpayne@69 325 yield from chunks
jpayne@69 326 if newline_indent is not None:
jpayne@69 327 _current_indent_level -= 1
jpayne@69 328 yield '\n' + _indent * _current_indent_level
jpayne@69 329 yield ']'
jpayne@69 330 if markers is not None:
jpayne@69 331 del markers[markerid]
jpayne@69 332
jpayne@69 333 def _iterencode_dict(dct, _current_indent_level):
jpayne@69 334 if not dct:
jpayne@69 335 yield '{}'
jpayne@69 336 return
jpayne@69 337 if markers is not None:
jpayne@69 338 markerid = id(dct)
jpayne@69 339 if markerid in markers:
jpayne@69 340 raise ValueError("Circular reference detected")
jpayne@69 341 markers[markerid] = dct
jpayne@69 342 yield '{'
jpayne@69 343 if _indent is not None:
jpayne@69 344 _current_indent_level += 1
jpayne@69 345 newline_indent = '\n' + _indent * _current_indent_level
jpayne@69 346 item_separator = _item_separator + newline_indent
jpayne@69 347 yield newline_indent
jpayne@69 348 else:
jpayne@69 349 newline_indent = None
jpayne@69 350 item_separator = _item_separator
jpayne@69 351 first = True
jpayne@69 352 if _sort_keys:
jpayne@69 353 items = sorted(dct.items())
jpayne@69 354 else:
jpayne@69 355 items = dct.items()
jpayne@69 356 for key, value in items:
jpayne@69 357 if isinstance(key, str):
jpayne@69 358 pass
jpayne@69 359 # JavaScript is weakly typed for these, so it makes sense to
jpayne@69 360 # also allow them. Many encoders seem to do something like this.
jpayne@69 361 elif isinstance(key, float):
jpayne@69 362 # see comment for int/float in _make_iterencode
jpayne@69 363 key = _floatstr(key)
jpayne@69 364 elif key is True:
jpayne@69 365 key = 'true'
jpayne@69 366 elif key is False:
jpayne@69 367 key = 'false'
jpayne@69 368 elif key is None:
jpayne@69 369 key = 'null'
jpayne@69 370 elif isinstance(key, int):
jpayne@69 371 # see comment for int/float in _make_iterencode
jpayne@69 372 key = _intstr(key)
jpayne@69 373 elif _skipkeys:
jpayne@69 374 continue
jpayne@69 375 else:
jpayne@69 376 raise TypeError(f'keys must be str, int, float, bool or None, '
jpayne@69 377 f'not {key.__class__.__name__}')
jpayne@69 378 if first:
jpayne@69 379 first = False
jpayne@69 380 else:
jpayne@69 381 yield item_separator
jpayne@69 382 yield _encoder(key)
jpayne@69 383 yield _key_separator
jpayne@69 384 if isinstance(value, str):
jpayne@69 385 yield _encoder(value)
jpayne@69 386 elif value is None:
jpayne@69 387 yield 'null'
jpayne@69 388 elif value is True:
jpayne@69 389 yield 'true'
jpayne@69 390 elif value is False:
jpayne@69 391 yield 'false'
jpayne@69 392 elif isinstance(value, int):
jpayne@69 393 # see comment for int/float in _make_iterencode
jpayne@69 394 yield _intstr(value)
jpayne@69 395 elif isinstance(value, float):
jpayne@69 396 # see comment for int/float in _make_iterencode
jpayne@69 397 yield _floatstr(value)
jpayne@69 398 else:
jpayne@69 399 if isinstance(value, (list, tuple)):
jpayne@69 400 chunks = _iterencode_list(value, _current_indent_level)
jpayne@69 401 elif isinstance(value, dict):
jpayne@69 402 chunks = _iterencode_dict(value, _current_indent_level)
jpayne@69 403 else:
jpayne@69 404 chunks = _iterencode(value, _current_indent_level)
jpayne@69 405 yield from chunks
jpayne@69 406 if newline_indent is not None:
jpayne@69 407 _current_indent_level -= 1
jpayne@69 408 yield '\n' + _indent * _current_indent_level
jpayne@69 409 yield '}'
jpayne@69 410 if markers is not None:
jpayne@69 411 del markers[markerid]
jpayne@69 412
jpayne@69 413 def _iterencode(o, _current_indent_level):
jpayne@69 414 if isinstance(o, str):
jpayne@69 415 yield _encoder(o)
jpayne@69 416 elif o is None:
jpayne@69 417 yield 'null'
jpayne@69 418 elif o is True:
jpayne@69 419 yield 'true'
jpayne@69 420 elif o is False:
jpayne@69 421 yield 'false'
jpayne@69 422 elif isinstance(o, int):
jpayne@69 423 # see comment for int/float in _make_iterencode
jpayne@69 424 yield _intstr(o)
jpayne@69 425 elif isinstance(o, float):
jpayne@69 426 # see comment for int/float in _make_iterencode
jpayne@69 427 yield _floatstr(o)
jpayne@69 428 elif isinstance(o, (list, tuple)):
jpayne@69 429 yield from _iterencode_list(o, _current_indent_level)
jpayne@69 430 elif isinstance(o, dict):
jpayne@69 431 yield from _iterencode_dict(o, _current_indent_level)
jpayne@69 432 else:
jpayne@69 433 if markers is not None:
jpayne@69 434 markerid = id(o)
jpayne@69 435 if markerid in markers:
jpayne@69 436 raise ValueError("Circular reference detected")
jpayne@69 437 markers[markerid] = o
jpayne@69 438 o = _default(o)
jpayne@69 439 yield from _iterencode(o, _current_indent_level)
jpayne@69 440 if markers is not None:
jpayne@69 441 del markers[markerid]
jpayne@69 442 return _iterencode