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