602 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			602 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Implementation of JSONEncoder
 | |
| """
 | |
| from __future__ import absolute_import
 | |
| import re
 | |
| from operator import itemgetter
 | |
| from decimal import Decimal
 | |
| from .compat import u, unichr, binary_type, string_types, integer_types, PY3
 | |
| def _import_speedups():
 | |
|     try:
 | |
|         from . import _speedups
 | |
|         return _speedups.encode_basestring_ascii, _speedups.make_encoder
 | |
|     except ImportError:
 | |
|         return None, None
 | |
| c_encode_basestring_ascii, c_make_encoder = _import_speedups()
 | |
| 
 | |
| from .decoder import PosInf
 | |
| 
 | |
| #ESCAPE = re.compile(ur'[\x00-\x1f\\"\b\f\n\r\t\u2028\u2029]')
 | |
| # This is required because u() will mangle the string and ur'' isn't valid
 | |
| # python3 syntax
 | |
| ESCAPE = re.compile(u'[\\x00-\\x1f\\\\"\\b\\f\\n\\r\\t\u2028\u2029]')
 | |
| ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
 | |
| HAS_UTF8 = re.compile(r'[\x80-\xff]')
 | |
| ESCAPE_DCT = {
 | |
|     '\\': '\\\\',
 | |
|     '"': '\\"',
 | |
|     '\b': '\\b',
 | |
|     '\f': '\\f',
 | |
|     '\n': '\\n',
 | |
|     '\r': '\\r',
 | |
|     '\t': '\\t',
 | |
| }
 | |
| for i in range(0x20):
 | |
|     #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
 | |
|     ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
 | |
| for i in [0x2028, 0x2029]:
 | |
|     ESCAPE_DCT.setdefault(unichr(i), '\\u%04x' % (i,))
 | |
| 
 | |
| FLOAT_REPR = repr
 | |
| 
 | |
| def encode_basestring(s, _PY3=PY3, _q=u('"')):
 | |
|     """Return a JSON representation of a Python string
 | |
| 
 | |
|     """
 | |
|     if _PY3:
 | |
|         if isinstance(s, binary_type):
 | |
|             s = s.decode('utf-8')
 | |
|     else:
 | |
|         if isinstance(s, str) and HAS_UTF8.search(s) is not None:
 | |
|             s = s.decode('utf-8')
 | |
|     def replace(match):
 | |
|         return ESCAPE_DCT[match.group(0)]
 | |
|     return _q + ESCAPE.sub(replace, s) + _q
 | |
| 
 | |
| 
 | |
| def py_encode_basestring_ascii(s, _PY3=PY3):
 | |
|     """Return an ASCII-only JSON representation of a Python string
 | |
| 
 | |
|     """
 | |
|     if _PY3:
 | |
|         if isinstance(s, binary_type):
 | |
|             s = s.decode('utf-8')
 | |
|     else:
 | |
|         if isinstance(s, str) and HAS_UTF8.search(s) is not None:
 | |
|             s = s.decode('utf-8')
 | |
|     def replace(match):
 | |
|         s = match.group(0)
 | |
|         try:
 | |
|             return ESCAPE_DCT[s]
 | |
|         except KeyError:
 | |
|             n = ord(s)
 | |
|             if n < 0x10000:
 | |
|                 #return '\\u{0:04x}'.format(n)
 | |
|                 return '\\u%04x' % (n,)
 | |
|             else:
 | |
|                 # surrogate pair
 | |
|                 n -= 0x10000
 | |
|                 s1 = 0xd800 | ((n >> 10) & 0x3ff)
 | |
|                 s2 = 0xdc00 | (n & 0x3ff)
 | |
|                 #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
 | |
|                 return '\\u%04x\\u%04x' % (s1, s2)
 | |
|     return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
 | |
| 
 | |
| 
 | |
| encode_basestring_ascii = (
 | |
|     c_encode_basestring_ascii or py_encode_basestring_ascii)
 | |
| 
 | |
| class JSONEncoder(object):
 | |
|     """Extensible JSON <http://json.org> encoder for Python data structures.
 | |
| 
 | |
|     Supports the following objects and types by default:
 | |
| 
 | |
|     +-------------------+---------------+
 | |
|     | Python            | JSON          |
 | |
|     +===================+===============+
 | |
|     | dict, namedtuple  | object        |
 | |
|     +-------------------+---------------+
 | |
|     | list, tuple       | array         |
 | |
|     +-------------------+---------------+
 | |
|     | str, unicode      | string        |
 | |
|     +-------------------+---------------+
 | |
|     | int, long, float  | number        |
 | |
|     +-------------------+---------------+
 | |
|     | True              | true          |
 | |
|     +-------------------+---------------+
 | |
|     | False             | false         |
 | |
|     +-------------------+---------------+
 | |
|     | None              | null          |
 | |
|     +-------------------+---------------+
 | |
| 
 | |
|     To extend this to recognize other objects, subclass and implement a
 | |
|     ``.default()`` method with another method that returns a serializable
 | |
|     object for ``o`` if possible, otherwise it should call the superclass
 | |
|     implementation (to raise ``TypeError``).
 | |
| 
 | |
|     """
 | |
|     item_separator = ', '
 | |
|     key_separator = ': '
 | |
|     def __init__(self, skipkeys=False, ensure_ascii=True,
 | |
|             check_circular=True, allow_nan=True, sort_keys=False,
 | |
|             indent=None, separators=None, encoding='utf-8', default=None,
 | |
|             use_decimal=True, namedtuple_as_object=True,
 | |
|             tuple_as_array=True, bigint_as_string=False,
 | |
|             item_sort_key=None):
 | |
|         """Constructor for JSONEncoder, with sensible defaults.
 | |
| 
 | |
|         If skipkeys is false, then it is a TypeError to attempt
 | |
|         encoding of keys that are not str, int, long, float or None.  If
 | |
|         skipkeys is True, such items are simply skipped.
 | |
| 
 | |
|         If ensure_ascii is true, the output is guaranteed to be str
 | |
|         objects with all incoming unicode characters escaped.  If
 | |
|         ensure_ascii is false, the output will be unicode object.
 | |
| 
 | |
|         If check_circular is true, then lists, dicts, and custom encoded
 | |
|         objects will be checked for circular references during encoding to
 | |
|         prevent an infinite recursion (which would cause an OverflowError).
 | |
|         Otherwise, no such check takes place.
 | |
| 
 | |
|         If allow_nan is true, then NaN, Infinity, and -Infinity will be
 | |
|         encoded as such.  This behavior is not JSON specification compliant,
 | |
|         but is consistent with most JavaScript based encoders and decoders.
 | |
|         Otherwise, it will be a ValueError to encode such floats.
 | |
| 
 | |
|         If sort_keys is true, then the output of dictionaries will be
 | |
|         sorted by key; this is useful for regression tests to ensure
 | |
|         that JSON serializations can be compared on a day-to-day basis.
 | |
| 
 | |
|         If indent is a string, then JSON array elements and object members
 | |
|         will be pretty-printed with a newline followed by that string repeated
 | |
|         for each level of nesting. ``None`` (the default) selects the most compact
 | |
|         representation without any newlines. For backwards compatibility with
 | |
|         versions of simplejson earlier than 2.1.0, an integer is also accepted
 | |
|         and is converted to a string with that many spaces.
 | |
| 
 | |
|         If specified, separators should be a (item_separator, key_separator)
 | |
|         tuple.  The default is (', ', ': ').  To get the most compact JSON
 | |
|         representation you should specify (',', ':') to eliminate whitespace.
 | |
| 
 | |
|         If specified, default is a function that gets called for objects
 | |
|         that can't otherwise be serialized.  It should return a JSON encodable
 | |
|         version of the object or raise a ``TypeError``.
 | |
| 
 | |
|         If encoding is not None, then all input strings will be
 | |
|         transformed into unicode using that encoding prior to JSON-encoding.
 | |
|         The default is UTF-8.
 | |
| 
 | |
|         If use_decimal is true (not the default), ``decimal.Decimal`` will
 | |
|         be supported directly by the encoder. For the inverse, decode JSON
 | |
|         with ``parse_float=decimal.Decimal``.
 | |
| 
 | |
|         If namedtuple_as_object is true (the default), objects with
 | |
|         ``_asdict()`` methods will be encoded as JSON objects.
 | |
| 
 | |
|         If tuple_as_array is true (the default), tuple (and subclasses) will
 | |
|         be encoded as JSON arrays.
 | |
| 
 | |
|         If bigint_as_string is true (not the default), ints 2**53 and higher
 | |
|         or lower than -2**53 will be encoded as strings. This is to avoid the
 | |
|         rounding that happens in Javascript otherwise.
 | |
| 
 | |
|         If specified, item_sort_key is a callable used to sort the items in
 | |
|         each dictionary. This is useful if you want to sort items other than
 | |
|         in alphabetical order by key.
 | |
|         """
 | |
| 
 | |
|         self.skipkeys = skipkeys
 | |
|         self.ensure_ascii = ensure_ascii
 | |
|         self.check_circular = check_circular
 | |
|         self.allow_nan = allow_nan
 | |
|         self.sort_keys = sort_keys
 | |
|         self.use_decimal = use_decimal
 | |
|         self.namedtuple_as_object = namedtuple_as_object
 | |
|         self.tuple_as_array = tuple_as_array
 | |
|         self.bigint_as_string = bigint_as_string
 | |
|         self.item_sort_key = item_sort_key
 | |
|         if indent is not None and not isinstance(indent, string_types):
 | |
|             indent = indent * ' '
 | |
|         self.indent = indent
 | |
|         if separators is not None:
 | |
|             self.item_separator, self.key_separator = separators
 | |
|         elif indent is not None:
 | |
|             self.item_separator = ','
 | |
|         if default is not None:
 | |
|             self.default = default
 | |
|         self.encoding = encoding
 | |
| 
 | |
|     def default(self, o):
 | |
|         """Implement this method in a subclass such that it returns
 | |
|         a serializable object for ``o``, or calls the base implementation
 | |
|         (to raise a ``TypeError``).
 | |
| 
 | |
|         For example, to support arbitrary iterators, you could
 | |
|         implement default like this::
 | |
| 
 | |
|             def default(self, o):
 | |
|                 try:
 | |
|                     iterable = iter(o)
 | |
|                 except TypeError:
 | |
|                     pass
 | |
|                 else:
 | |
|                     return list(iterable)
 | |
|                 return JSONEncoder.default(self, o)
 | |
| 
 | |
|         """
 | |
|         raise TypeError(repr(o) + " is not JSON serializable")
 | |
| 
 | |
|     def encode(self, o):
 | |
|         """Return a JSON string representation of a Python data structure.
 | |
| 
 | |
|         >>> from simplejson import JSONEncoder
 | |
|         >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
 | |
|         '{"foo": ["bar", "baz"]}'
 | |
| 
 | |
|         """
 | |
|         # This is for extremely simple cases and benchmarks.
 | |
|         if isinstance(o, binary_type):
 | |
|             _encoding = self.encoding
 | |
|             if (_encoding is not None and not (_encoding == 'utf-8')):
 | |
|                 o = o.decode(_encoding)
 | |
|         if isinstance(o, string_types):
 | |
|             if self.ensure_ascii:
 | |
|                 return encode_basestring_ascii(o)
 | |
|             else:
 | |
|                 return encode_basestring(o)
 | |
|         # This doesn't pass the iterator directly to ''.join() because the
 | |
|         # exceptions aren't as detailed.  The list call should be roughly
 | |
|         # equivalent to the PySequence_Fast that ''.join() would do.
 | |
|         chunks = self.iterencode(o, _one_shot=True)
 | |
|         if not isinstance(chunks, (list, tuple)):
 | |
|             chunks = list(chunks)
 | |
|         if self.ensure_ascii:
 | |
|             return ''.join(chunks)
 | |
|         else:
 | |
|             return u''.join(chunks)
 | |
| 
 | |
|     def iterencode(self, o, _one_shot=False):
 | |
|         """Encode the given object and yield each string
 | |
|         representation as available.
 | |
| 
 | |
|         For example::
 | |
| 
 | |
|             for chunk in JSONEncoder().iterencode(bigobject):
 | |
|                 mysocket.write(chunk)
 | |
| 
 | |
|         """
 | |
|         if self.check_circular:
 | |
|             markers = {}
 | |
|         else:
 | |
|             markers = None
 | |
|         if self.ensure_ascii:
 | |
|             _encoder = encode_basestring_ascii
 | |
|         else:
 | |
|             _encoder = encode_basestring
 | |
|         if self.encoding != 'utf-8':
 | |
|             def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
 | |
|                 if isinstance(o, binary_type):
 | |
|                     o = o.decode(_encoding)
 | |
|                 return _orig_encoder(o)
 | |
| 
 | |
|         def floatstr(o, allow_nan=self.allow_nan,
 | |
|                 _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
 | |
|             # Check for specials. Note that this type of test is processor
 | |
|             # and/or platform-specific, so do tests which don't depend on
 | |
|             # the internals.
 | |
| 
 | |
|             if o != o:
 | |
|                 text = 'NaN'
 | |
|             elif o == _inf:
 | |
|                 text = 'Infinity'
 | |
|             elif o == _neginf:
 | |
|                 text = '-Infinity'
 | |
|             else:
 | |
|                 return _repr(o)
 | |
| 
 | |
|             if not allow_nan:
 | |
|                 raise ValueError(
 | |
|                     "Out of range float values are not JSON compliant: " +
 | |
|                     repr(o))
 | |
| 
 | |
|             return text
 | |
| 
 | |
| 
 | |
|         key_memo = {}
 | |
|         if (_one_shot and c_make_encoder is not None
 | |
|                 and self.indent is None):
 | |
|             _iterencode = c_make_encoder(
 | |
|                 markers, self.default, _encoder, self.indent,
 | |
|                 self.key_separator, self.item_separator, self.sort_keys,
 | |
|                 self.skipkeys, self.allow_nan, key_memo, self.use_decimal,
 | |
|                 self.namedtuple_as_object, self.tuple_as_array,
 | |
|                 self.bigint_as_string, self.item_sort_key,
 | |
|                 self.encoding,
 | |
|                 Decimal)
 | |
|         else:
 | |
|             _iterencode = _make_iterencode(
 | |
|                 markers, self.default, _encoder, self.indent, floatstr,
 | |
|                 self.key_separator, self.item_separator, self.sort_keys,
 | |
|                 self.skipkeys, _one_shot, self.use_decimal,
 | |
|                 self.namedtuple_as_object, self.tuple_as_array,
 | |
|                 self.bigint_as_string, self.item_sort_key,
 | |
|                 self.encoding,
 | |
|                 Decimal=Decimal)
 | |
|         try:
 | |
|             return _iterencode(o, 0)
 | |
|         finally:
 | |
|             key_memo.clear()
 | |
| 
 | |
| 
 | |
| class JSONEncoderForHTML(JSONEncoder):
 | |
|     """An encoder that produces JSON safe to embed in HTML.
 | |
| 
 | |
|     To embed JSON content in, say, a script tag on a web page, the
 | |
|     characters &, < and > should be escaped. They cannot be escaped
 | |
|     with the usual entities (e.g. &) because they are not expanded
 | |
|     within <script> tags.
 | |
|     """
 | |
| 
 | |
|     def encode(self, o):
 | |
|         # Override JSONEncoder.encode because it has hacks for
 | |
|         # performance that make things more complicated.
 | |
|         chunks = self.iterencode(o, True)
 | |
|         if self.ensure_ascii:
 | |
|             return ''.join(chunks)
 | |
|         else:
 | |
|             return u''.join(chunks)
 | |
| 
 | |
|     def iterencode(self, o, _one_shot=False):
 | |
|         chunks = super(JSONEncoderForHTML, self).iterencode(o, _one_shot)
 | |
|         for chunk in chunks:
 | |
|             chunk = chunk.replace('&', '\\u0026')
 | |
|             chunk = chunk.replace('<', '\\u003c')
 | |
|             chunk = chunk.replace('>', '\\u003e')
 | |
|             yield chunk
 | |
| 
 | |
| 
 | |
| def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
 | |
|         _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
 | |
|         _use_decimal, _namedtuple_as_object, _tuple_as_array,
 | |
|         _bigint_as_string, _item_sort_key, _encoding,
 | |
|         ## HACK: hand-optimized bytecode; turn globals into locals
 | |
|         _PY3=PY3,
 | |
|         ValueError=ValueError,
 | |
|         string_types=string_types,
 | |
|         Decimal=Decimal,
 | |
|         dict=dict,
 | |
|         float=float,
 | |
|         id=id,
 | |
|         integer_types=integer_types,
 | |
|         isinstance=isinstance,
 | |
|         list=list,
 | |
|         str=str,
 | |
|         tuple=tuple,
 | |
|     ):
 | |
|     if _item_sort_key and not callable(_item_sort_key):
 | |
|         raise TypeError("item_sort_key must be None or callable")
 | |
|     elif _sort_keys and not _item_sort_key:
 | |
|         _item_sort_key = itemgetter(0)
 | |
| 
 | |
|     def _iterencode_list(lst, _current_indent_level):
 | |
|         if not lst:
 | |
|             yield '[]'
 | |
|             return
 | |
|         if markers is not None:
 | |
|             markerid = id(lst)
 | |
|             if markerid in markers:
 | |
|                 raise ValueError("Circular reference detected")
 | |
|             markers[markerid] = lst
 | |
|         buf = '['
 | |
|         if _indent is not None:
 | |
|             _current_indent_level += 1
 | |
|             newline_indent = '\n' + (_indent * _current_indent_level)
 | |
|             separator = _item_separator + newline_indent
 | |
|             buf += newline_indent
 | |
|         else:
 | |
|             newline_indent = None
 | |
|             separator = _item_separator
 | |
|         first = True
 | |
|         for value in lst:
 | |
|             if first:
 | |
|                 first = False
 | |
|             else:
 | |
|                 buf = separator
 | |
|             if (isinstance(value, string_types) or
 | |
|                 (_PY3 and isinstance(value, binary_type))):
 | |
|                 yield buf + _encoder(value)
 | |
|             elif value is None:
 | |
|                 yield buf + 'null'
 | |
|             elif value is True:
 | |
|                 yield buf + 'true'
 | |
|             elif value is False:
 | |
|                 yield buf + 'false'
 | |
|             elif isinstance(value, integer_types):
 | |
|                 yield ((buf + str(value))
 | |
|                        if (not _bigint_as_string or
 | |
|                            (-1 << 53) < value < (1 << 53))
 | |
|                            else (buf + '"' + str(value) + '"'))
 | |
|             elif isinstance(value, float):
 | |
|                 yield buf + _floatstr(value)
 | |
|             elif _use_decimal and isinstance(value, Decimal):
 | |
|                 yield buf + str(value)
 | |
|             else:
 | |
|                 yield buf
 | |
|                 if isinstance(value, list):
 | |
|                     chunks = _iterencode_list(value, _current_indent_level)
 | |
|                 else:
 | |
|                     _asdict = _namedtuple_as_object and getattr(value, '_asdict', None)
 | |
|                     if _asdict and callable(_asdict):
 | |
|                         chunks = _iterencode_dict(_asdict(),
 | |
|                                                   _current_indent_level)
 | |
|                     elif _tuple_as_array and isinstance(value, tuple):
 | |
|                         chunks = _iterencode_list(value, _current_indent_level)
 | |
|                     elif isinstance(value, dict):
 | |
|                         chunks = _iterencode_dict(value, _current_indent_level)
 | |
|                     else:
 | |
|                         chunks = _iterencode(value, _current_indent_level)
 | |
|                 for chunk in chunks:
 | |
|                     yield chunk
 | |
|         if newline_indent is not None:
 | |
|             _current_indent_level -= 1
 | |
|             yield '\n' + (_indent * _current_indent_level)
 | |
|         yield ']'
 | |
|         if markers is not None:
 | |
|             del markers[markerid]
 | |
| 
 | |
|     def _stringify_key(key):
 | |
|         if isinstance(key, string_types): # pragma: no cover
 | |
|             pass
 | |
|         elif isinstance(key, binary_type):
 | |
|             key = key.decode(_encoding)
 | |
|         elif isinstance(key, float):
 | |
|             key = _floatstr(key)
 | |
|         elif key is True:
 | |
|             key = 'true'
 | |
|         elif key is False:
 | |
|             key = 'false'
 | |
|         elif key is None:
 | |
|             key = 'null'
 | |
|         elif isinstance(key, integer_types):
 | |
|             key = str(key)
 | |
|         elif _use_decimal and isinstance(key, Decimal):
 | |
|             key = str(key)
 | |
|         elif _skipkeys:
 | |
|             key = None
 | |
|         else:
 | |
|             raise TypeError("key " + repr(key) + " is not a string")
 | |
|         return key
 | |
| 
 | |
|     def _iterencode_dict(dct, _current_indent_level):
 | |
|         if not dct:
 | |
|             yield '{}'
 | |
|             return
 | |
|         if markers is not None:
 | |
|             markerid = id(dct)
 | |
|             if markerid in markers:
 | |
|                 raise ValueError("Circular reference detected")
 | |
|             markers[markerid] = dct
 | |
|         yield '{'
 | |
|         if _indent is not None:
 | |
|             _current_indent_level += 1
 | |
|             newline_indent = '\n' + (_indent * _current_indent_level)
 | |
|             item_separator = _item_separator + newline_indent
 | |
|             yield newline_indent
 | |
|         else:
 | |
|             newline_indent = None
 | |
|             item_separator = _item_separator
 | |
|         first = True
 | |
|         if _PY3:
 | |
|             iteritems = dct.items()
 | |
|         else:
 | |
|             iteritems = dct.iteritems()
 | |
|         if _item_sort_key:
 | |
|             items = []
 | |
|             for k, v in dct.items():
 | |
|                 if not isinstance(k, string_types):
 | |
|                     k = _stringify_key(k)
 | |
|                     if k is None:
 | |
|                         continue
 | |
|                 items.append((k, v))
 | |
|             items.sort(key=_item_sort_key)
 | |
|         else:
 | |
|             items = iteritems
 | |
|         for key, value in items:
 | |
|             if not (_item_sort_key or isinstance(key, string_types)):
 | |
|                 key = _stringify_key(key)
 | |
|                 if key is None:
 | |
|                     # _skipkeys must be True
 | |
|                     continue
 | |
|             if first:
 | |
|                 first = False
 | |
|             else:
 | |
|                 yield item_separator
 | |
|             yield _encoder(key)
 | |
|             yield _key_separator
 | |
|             if (isinstance(value, string_types) or
 | |
|                 (_PY3 and isinstance(value, binary_type))):
 | |
|                 yield _encoder(value)
 | |
|             elif value is None:
 | |
|                 yield 'null'
 | |
|             elif value is True:
 | |
|                 yield 'true'
 | |
|             elif value is False:
 | |
|                 yield 'false'
 | |
|             elif isinstance(value, integer_types):
 | |
|                 yield (str(value)
 | |
|                        if (not _bigint_as_string or
 | |
|                            (-1 << 53) < value < (1 << 53))
 | |
|                            else ('"' + str(value) + '"'))
 | |
|             elif isinstance(value, float):
 | |
|                 yield _floatstr(value)
 | |
|             elif _use_decimal and isinstance(value, Decimal):
 | |
|                 yield str(value)
 | |
|             else:
 | |
|                 if isinstance(value, list):
 | |
|                     chunks = _iterencode_list(value, _current_indent_level)
 | |
|                 else:
 | |
|                     _asdict = _namedtuple_as_object and getattr(value, '_asdict', None)
 | |
|                     if _asdict and callable(_asdict):
 | |
|                         chunks = _iterencode_dict(_asdict(),
 | |
|                                                   _current_indent_level)
 | |
|                     elif _tuple_as_array and isinstance(value, tuple):
 | |
|                         chunks = _iterencode_list(value, _current_indent_level)
 | |
|                     elif isinstance(value, dict):
 | |
|                         chunks = _iterencode_dict(value, _current_indent_level)
 | |
|                     else:
 | |
|                         chunks = _iterencode(value, _current_indent_level)
 | |
|                 for chunk in chunks:
 | |
|                     yield chunk
 | |
|         if newline_indent is not None:
 | |
|             _current_indent_level -= 1
 | |
|             yield '\n' + (_indent * _current_indent_level)
 | |
|         yield '}'
 | |
|         if markers is not None:
 | |
|             del markers[markerid]
 | |
| 
 | |
|     def _iterencode(o, _current_indent_level):
 | |
|         if (isinstance(o, string_types) or
 | |
|             (_PY3 and isinstance(o, binary_type))):
 | |
|             yield _encoder(o)
 | |
|         elif o is None:
 | |
|             yield 'null'
 | |
|         elif o is True:
 | |
|             yield 'true'
 | |
|         elif o is False:
 | |
|             yield 'false'
 | |
|         elif isinstance(o, integer_types):
 | |
|             yield (str(o)
 | |
|                    if (not _bigint_as_string or
 | |
|                        (-1 << 53) < o < (1 << 53))
 | |
|                        else ('"' + str(o) + '"'))
 | |
|         elif isinstance(o, float):
 | |
|             yield _floatstr(o)
 | |
|         elif isinstance(o, list):
 | |
|             for chunk in _iterencode_list(o, _current_indent_level):
 | |
|                 yield chunk
 | |
|         else:
 | |
|             _asdict = _namedtuple_as_object and getattr(o, '_asdict', None)
 | |
|             if _asdict and callable(_asdict):
 | |
|                 for chunk in _iterencode_dict(_asdict(), _current_indent_level):
 | |
|                     yield chunk
 | |
|             elif (_tuple_as_array and isinstance(o, tuple)):
 | |
|                 for chunk in _iterencode_list(o, _current_indent_level):
 | |
|                     yield chunk
 | |
|             elif isinstance(o, dict):
 | |
|                 for chunk in _iterencode_dict(o, _current_indent_level):
 | |
|                     yield chunk
 | |
|             elif _use_decimal and isinstance(o, Decimal):
 | |
|                 yield str(o)
 | |
|             else:
 | |
|                 if markers is not None:
 | |
|                     markerid = id(o)
 | |
|                     if markerid in markers:
 | |
|                         raise ValueError("Circular reference detected")
 | |
|                     markers[markerid] = o
 | |
|                 o = _default(o)
 | |
|                 for chunk in _iterencode(o, _current_indent_level):
 | |
|                     yield chunk
 | |
|                 if markers is not None:
 | |
|                     del markers[markerid]
 | |
| 
 | |
|     return _iterencode
 |