0001"""
0002Implementation of JSONEncoder
0003"""
0004import re
0005
0006# this should match any kind of infinity
0007INFCHARS = re.compile(r'[infINF]')
0008ESCAPE = re.compile(r'[\x00-\x19\\"\b\f\n\r\t]')
0009ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
0010ESCAPE_DCT = {
0011    '\\': '\\\\',
0012    '"': '\\"',
0013    '\b': '\\b',
0014    '\f': '\\f',
0015    '\n': '\\n',
0016    '\r': '\\r',
0017    '\t': '\\t',
0018}
0019for i in range(20):
0020    ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
0021
0022def floatstr(o, allow_nan=True):
0023    s = str(o)
0024    # If the first non-sign is a digit then it's not a special value
0025    if (o < 0.0 and s[1].isdigit()) or s[0].isdigit():
0026        return s
0027    elif not allow_nan:
0028        raise ValueError("Out of range float values are not JSON compliant: %r"
0029            % (o,))
0030    # These are the string representations on the platforms I've tried
0031    if s == 'nan':
0032        return 'NaN'
0033    if s == 'inf':
0034        return 'Infinity'
0035    if s == '-inf':
0036        return '-Infinity'
0037    # NaN should either be inequal to itself, or equal to everything
0038    if o != o or o == 0.0:
0039        return 'NaN'
0040    # Last ditch effort, assume inf
0041    if o < 0:
0042        return '-Infinity'
0043    return 'Infinity'
0044
0045def encode_basestring(s):
0046    """
0047    Return a JSON representation of a Python string
0048    """
0049    def replace(match):
0050        return ESCAPE_DCT[match.group(0)]
0051    return '"' + ESCAPE.sub(replace, s) + '"'
0052
0053def encode_basestring_ascii(s):
0054    def replace(match):
0055        s = match.group(0)
0056        try:
0057            return ESCAPE_DCT[s]
0058        except KeyError:
0059            return '\\u%04x' % (ord(s),)
0060    return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
0061
0062
0063class JSONEncoder(object):
0064    """
0065    Extensible JSON <http://json.org> encoder for Python data structures.
0066
0067    Supports the following objects and types by default:
0068    
0069    +-------------------+---------------+
0070    | Python            | JSON          |
0071    +===================+===============+
0072    | dict              | object        |
0073    +-------------------+---------------+
0074    | list, tuple       | array         |
0075    +-------------------+---------------+
0076    | str, unicode      | string        |
0077    +-------------------+---------------+
0078    | int, long, float  | number        |
0079    +-------------------+---------------+
0080    | True              | true          |
0081    +-------------------+---------------+
0082    | False             | false         |
0083    +-------------------+---------------+
0084    | None              | null          |
0085    +-------------------+---------------+
0086
0087    To extend this to recognize other objects, subclass and implement a
0088    ``.default()`` method with another method that returns a serializable
0089    object for ``o`` if possible, otherwise it should call the superclass
0090    implementation (to raise ``TypeError``).
0091    """
0092    __all__ = ['__init__', 'default', 'encode', 'iterencode']
0093    def __init__(self, skipkeys=False, ensure_ascii=True, check_circular=True,
0094            allow_nan=True):
0095        """
0096        Constructor for JSONEncoder, with sensible defaults.
0097        
0098        If skipkeys is False, then it is a TypeError to attempt
0099        encoding of keys that are not str, int, long, float or None.  If
0100        skipkeys is True, such items are simply skipped.
0101
0102        If ensure_ascii is True, the output is guaranteed to be str
0103        objects with all incoming unicode characters escaped.  If ensure_ascii
0104        is false, the output will be unicode object.
0105
0106        If check_circular is True, then lists, dicts, and custom encoded
0107        objects will be checked for circular references during encoding to
0108        prevent an infinite recursion (which would cause an OverflowError).
0109        Otherwise, no such check takes place.
0110
0111        If allow_nan is True, then NaN, Infinity, and -Infinity will be
0112        encoded as such.  This behavior is not JSON specification compliant,
0113        but is consistent with most JavaScript based encoders and decoders.
0114        Otherwise, it will be a ValueError to encode such floats.
0115        """
0116
0117        self.skipkeys = skipkeys
0118        self.ensure_ascii = ensure_ascii
0119        self.check_circular = check_circular
0120        self.allow_nan = allow_nan
0121
0122    def _iterencode_list(self, lst, markers=None):
0123        if not lst:
0124            yield '[]'
0125            return
0126        if markers is not None:
0127            markerid = id(lst)
0128            if markerid in markers:
0129                raise ValueError("Circular reference detected")
0130            markers[markerid] = lst
0131        yield '['
0132        first = True
0133        for value in lst:
0134            if first:
0135                first = False
0136            else:
0137                yield ', '
0138            for chunk in self._iterencode(value, markers):
0139                yield chunk
0140        yield ']'
0141        if markers is not None:
0142            del markers[markerid]
0143
0144    def _iterencode_dict(self, dct, markers=None):
0145        if not dct:
0146            yield '{}'
0147            return
0148        if markers is not None:
0149            markerid = id(dct)
0150            if markerid in markers:
0151                raise ValueError("Circular reference detected")
0152            markers[markerid] = dct
0153        yield '{'
0154        first = True
0155        if self.ensure_ascii:
0156            encoder = encode_basestring_ascii
0157        else:
0158            encoder = encode_basestring
0159        allow_nan = self.allow_nan
0160        for key, value in dct.iteritems():
0161            if isinstance(key, basestring):
0162                pass
0163            # JavaScript is weakly typed for these, so it makes sense to
0164            # also allow them.  Many encoders seem to do something like this.
0165            elif isinstance(key, float):
0166                key = floatstr(key, allow_nan)
0167            elif isinstance(key, (int, long)):
0168                key = str(key)
0169            elif key is True:
0170                key = 'true'
0171            elif key is False:
0172                key = 'false'
0173            elif key is None:
0174                key = 'null'
0175            elif self.skipkeys:
0176                continue
0177            else:
0178                raise TypeError("key %r is not a string" % (key,))
0179            if first:
0180                first = False
0181            else:
0182                yield ', '
0183            yield encoder(key)
0184            yield ':'
0185            for chunk in self._iterencode(value, markers):
0186                yield chunk
0187        yield '}'
0188        if markers is not None:
0189            del markers[markerid]
0190
0191    def _iterencode(self, o, markers=None):
0192        if isinstance(o, basestring):
0193            if self.ensure_ascii:
0194                encoder = encode_basestring_ascii
0195            else:
0196                encoder = encode_basestring
0197            yield encoder(o)
0198        elif o is None:
0199            yield 'null'
0200        elif o is True:
0201            yield 'true'
0202        elif o is False:
0203            yield 'false'
0204        elif isinstance(o, (int, long)):
0205            yield str(o)
0206        elif isinstance(o, float):
0207            yield floatstr(o, self.allow_nan)
0208        elif isinstance(o, (list, tuple)):
0209            for chunk in self._iterencode_list(o, markers):
0210                yield chunk
0211        elif isinstance(o, dict):
0212            for chunk in self._iterencode_dict(o, markers):
0213                yield chunk
0214        else:
0215            if markers is not None:
0216                markerid = id(o)
0217                if markerid in markers:
0218                    raise ValueError("Circular reference detected")
0219                markers[markerid] = o
0220            for chunk in self._iterencode_default(o, markers):
0221                yield chunk
0222            if markers is not None:
0223                del markers[markerid]
0224
0225    def _iterencode_default(self, o, markers=None):
0226        newobj = self.default(o)
0227        return self._iterencode(newobj, markers)
0228
0229    def default(self, o):
0230        """
0231        Implement this method in a subclass such that it returns
0232        a serializable object for ``o``, or calls the base implementation
0233        (to raise a ``TypeError``).
0234
0235        For example, to support arbitrary iterators, you could
0236        implement default like this::
0237            
0238            def default(self, o):
0239                try:
0240                    iterable = iter(o)
0241                except TypeError:
0242                    pass
0243                else:
0244                    return list(iterable)
0245                return JSONEncoder.default(self, o)
0246        """
0247        raise TypeError("%r is not JSON serializable" % (o,))
0248
0249    def encode(self, o):
0250        """
0251        Return a JSON string representation of a Python data structure.
0252
0253        >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
0254        '{"foo":["bar", "baz"]}'
0255        """
0256        # This doesn't pass the iterator directly to ''.join() because it
0257        # sucks at reporting exceptions.  It's going to do this internally
0258        # anyway because it uses PySequence_Fast or similar.
0259        chunks = list(self.iterencode(o))
0260        return ''.join(chunks)
0261
0262    def iterencode(self, o):
0263        """
0264        Encode the given object and yield each string
0265        representation as available.
0266        
0267        For example::
0268            
0269            for chunk in JSONEncoder().iterencode(bigobject):
0270                mysocket.write(chunk)
0271        """
0272        if self.check_circular:
0273            markers = {}
0274        else:
0275            markers = None
0276        return self._iterencode(o, markers)
0277
0278__all__ = ['JSONEncoder']