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
|