addon-sdk/source/python-lib/simplejson/decoder.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/python-lib/simplejson/decoder.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,343 @@
     1.4 +"""
     1.5 +Implementation of JSONDecoder
     1.6 +"""
     1.7 +import re
     1.8 +import sys
     1.9 +
    1.10 +from simplejson.scanner import Scanner, pattern
    1.11 +try:
    1.12 +    from simplejson._speedups import scanstring as c_scanstring
    1.13 +except ImportError:
    1.14 +    pass
    1.15 +
    1.16 +FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
    1.17 +
    1.18 +def _floatconstants():
    1.19 +    import struct
    1.20 +    import sys
    1.21 +    _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
    1.22 +    if sys.byteorder != 'big':
    1.23 +        _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
    1.24 +    nan, inf = struct.unpack('dd', _BYTES)
    1.25 +    return nan, inf, -inf
    1.26 +
    1.27 +NaN, PosInf, NegInf = _floatconstants()
    1.28 +
    1.29 +
    1.30 +def linecol(doc, pos):
    1.31 +    lineno = doc.count('\n', 0, pos) + 1
    1.32 +    if lineno == 1:
    1.33 +        colno = pos
    1.34 +    else:
    1.35 +        colno = pos - doc.rindex('\n', 0, pos)
    1.36 +    return lineno, colno
    1.37 +
    1.38 +
    1.39 +def errmsg(msg, doc, pos, end=None):
    1.40 +    lineno, colno = linecol(doc, pos)
    1.41 +    if end is None:
    1.42 +        return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
    1.43 +    endlineno, endcolno = linecol(doc, end)
    1.44 +    return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
    1.45 +        msg, lineno, colno, endlineno, endcolno, pos, end)
    1.46 +
    1.47 +
    1.48 +_CONSTANTS = {
    1.49 +    '-Infinity': NegInf,
    1.50 +    'Infinity': PosInf,
    1.51 +    'NaN': NaN,
    1.52 +    'true': True,
    1.53 +    'false': False,
    1.54 +    'null': None,
    1.55 +}
    1.56 +
    1.57 +def JSONConstant(match, context, c=_CONSTANTS):
    1.58 +    s = match.group(0)
    1.59 +    fn = getattr(context, 'parse_constant', None)
    1.60 +    if fn is None:
    1.61 +        rval = c[s]
    1.62 +    else:
    1.63 +        rval = fn(s)
    1.64 +    return rval, None
    1.65 +pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
    1.66 +
    1.67 +
    1.68 +def JSONNumber(match, context):
    1.69 +    match = JSONNumber.regex.match(match.string, *match.span())
    1.70 +    integer, frac, exp = match.groups()
    1.71 +    if frac or exp:
    1.72 +        fn = getattr(context, 'parse_float', None) or float
    1.73 +        res = fn(integer + (frac or '') + (exp or ''))
    1.74 +    else:
    1.75 +        fn = getattr(context, 'parse_int', None) or int
    1.76 +        res = fn(integer)
    1.77 +    return res, None
    1.78 +pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
    1.79 +
    1.80 +
    1.81 +STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
    1.82 +BACKSLASH = {
    1.83 +    '"': u'"', '\\': u'\\', '/': u'/',
    1.84 +    'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
    1.85 +}
    1.86 +
    1.87 +DEFAULT_ENCODING = "utf-8"
    1.88 +
    1.89 +def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
    1.90 +    if encoding is None:
    1.91 +        encoding = DEFAULT_ENCODING
    1.92 +    chunks = []
    1.93 +    _append = chunks.append
    1.94 +    begin = end - 1
    1.95 +    while 1:
    1.96 +        chunk = _m(s, end)
    1.97 +        if chunk is None:
    1.98 +            raise ValueError(
    1.99 +                errmsg("Unterminated string starting at", s, begin))
   1.100 +        end = chunk.end()
   1.101 +        content, terminator = chunk.groups()
   1.102 +        if content:
   1.103 +            if not isinstance(content, unicode):
   1.104 +                content = unicode(content, encoding)
   1.105 +            _append(content)
   1.106 +        if terminator == '"':
   1.107 +            break
   1.108 +        elif terminator != '\\':
   1.109 +            if strict:
   1.110 +                raise ValueError(errmsg("Invalid control character %r at", s, end))
   1.111 +            else:
   1.112 +                _append(terminator)
   1.113 +                continue
   1.114 +        try:
   1.115 +            esc = s[end]
   1.116 +        except IndexError:
   1.117 +            raise ValueError(
   1.118 +                errmsg("Unterminated string starting at", s, begin))
   1.119 +        if esc != 'u':
   1.120 +            try:
   1.121 +                m = _b[esc]
   1.122 +            except KeyError:
   1.123 +                raise ValueError(
   1.124 +                    errmsg("Invalid \\escape: %r" % (esc,), s, end))
   1.125 +            end += 1
   1.126 +        else:
   1.127 +            esc = s[end + 1:end + 5]
   1.128 +            next_end = end + 5
   1.129 +            msg = "Invalid \\uXXXX escape"
   1.130 +            try:
   1.131 +                if len(esc) != 4:
   1.132 +                    raise ValueError
   1.133 +                uni = int(esc, 16)
   1.134 +                if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
   1.135 +                    msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
   1.136 +                    if not s[end + 5:end + 7] == '\\u':
   1.137 +                        raise ValueError
   1.138 +                    esc2 = s[end + 7:end + 11]
   1.139 +                    if len(esc2) != 4:
   1.140 +                        raise ValueError
   1.141 +                    uni2 = int(esc2, 16)
   1.142 +                    uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
   1.143 +                    next_end += 6
   1.144 +                m = unichr(uni)
   1.145 +            except ValueError:
   1.146 +                raise ValueError(errmsg(msg, s, end))
   1.147 +            end = next_end
   1.148 +        _append(m)
   1.149 +    return u''.join(chunks), end
   1.150 +
   1.151 +
   1.152 +# Use speedup
   1.153 +try:
   1.154 +    scanstring = c_scanstring
   1.155 +except NameError:
   1.156 +    scanstring = py_scanstring
   1.157 +
   1.158 +def JSONString(match, context):
   1.159 +    encoding = getattr(context, 'encoding', None)
   1.160 +    strict = getattr(context, 'strict', True)
   1.161 +    return scanstring(match.string, match.end(), encoding, strict)
   1.162 +pattern(r'"')(JSONString)
   1.163 +
   1.164 +
   1.165 +WHITESPACE = re.compile(r'\s*', FLAGS)
   1.166 +
   1.167 +def JSONObject(match, context, _w=WHITESPACE.match):
   1.168 +    pairs = {}
   1.169 +    s = match.string
   1.170 +    end = _w(s, match.end()).end()
   1.171 +    nextchar = s[end:end + 1]
   1.172 +    # Trivial empty object
   1.173 +    if nextchar == '}':
   1.174 +        return pairs, end + 1
   1.175 +    if nextchar != '"':
   1.176 +        raise ValueError(errmsg("Expecting property name", s, end))
   1.177 +    end += 1
   1.178 +    encoding = getattr(context, 'encoding', None)
   1.179 +    strict = getattr(context, 'strict', True)
   1.180 +    iterscan = JSONScanner.iterscan
   1.181 +    while True:
   1.182 +        key, end = scanstring(s, end, encoding, strict)
   1.183 +        end = _w(s, end).end()
   1.184 +        if s[end:end + 1] != ':':
   1.185 +            raise ValueError(errmsg("Expecting : delimiter", s, end))
   1.186 +        end = _w(s, end + 1).end()
   1.187 +        try:
   1.188 +            value, end = iterscan(s, idx=end, context=context).next()
   1.189 +        except StopIteration:
   1.190 +            raise ValueError(errmsg("Expecting object", s, end))
   1.191 +        pairs[key] = value
   1.192 +        end = _w(s, end).end()
   1.193 +        nextchar = s[end:end + 1]
   1.194 +        end += 1
   1.195 +        if nextchar == '}':
   1.196 +            break
   1.197 +        if nextchar != ',':
   1.198 +            raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
   1.199 +        end = _w(s, end).end()
   1.200 +        nextchar = s[end:end + 1]
   1.201 +        end += 1
   1.202 +        if nextchar != '"':
   1.203 +            raise ValueError(errmsg("Expecting property name", s, end - 1))
   1.204 +    object_hook = getattr(context, 'object_hook', None)
   1.205 +    if object_hook is not None:
   1.206 +        pairs = object_hook(pairs)
   1.207 +    return pairs, end
   1.208 +pattern(r'{')(JSONObject)
   1.209 +
   1.210 +
   1.211 +def JSONArray(match, context, _w=WHITESPACE.match):
   1.212 +    values = []
   1.213 +    s = match.string
   1.214 +    end = _w(s, match.end()).end()
   1.215 +    # Look-ahead for trivial empty array
   1.216 +    nextchar = s[end:end + 1]
   1.217 +    if nextchar == ']':
   1.218 +        return values, end + 1
   1.219 +    iterscan = JSONScanner.iterscan
   1.220 +    while True:
   1.221 +        try:
   1.222 +            value, end = iterscan(s, idx=end, context=context).next()
   1.223 +        except StopIteration:
   1.224 +            raise ValueError(errmsg("Expecting object", s, end))
   1.225 +        values.append(value)
   1.226 +        end = _w(s, end).end()
   1.227 +        nextchar = s[end:end + 1]
   1.228 +        end += 1
   1.229 +        if nextchar == ']':
   1.230 +            break
   1.231 +        if nextchar != ',':
   1.232 +            raise ValueError(errmsg("Expecting , delimiter", s, end))
   1.233 +        end = _w(s, end).end()
   1.234 +    return values, end
   1.235 +pattern(r'\[')(JSONArray)
   1.236 +
   1.237 +
   1.238 +ANYTHING = [
   1.239 +    JSONObject,
   1.240 +    JSONArray,
   1.241 +    JSONString,
   1.242 +    JSONConstant,
   1.243 +    JSONNumber,
   1.244 +]
   1.245 +
   1.246 +JSONScanner = Scanner(ANYTHING)
   1.247 +
   1.248 +
   1.249 +class JSONDecoder(object):
   1.250 +    """
   1.251 +    Simple JSON <http://json.org> decoder
   1.252 +
   1.253 +    Performs the following translations in decoding by default:
   1.254 +    
   1.255 +    +---------------+-------------------+
   1.256 +    | JSON          | Python            |
   1.257 +    +===============+===================+
   1.258 +    | object        | dict              |
   1.259 +    +---------------+-------------------+
   1.260 +    | array         | list              |
   1.261 +    +---------------+-------------------+
   1.262 +    | string        | unicode           |
   1.263 +    +---------------+-------------------+
   1.264 +    | number (int)  | int, long         |
   1.265 +    +---------------+-------------------+
   1.266 +    | number (real) | float             |
   1.267 +    +---------------+-------------------+
   1.268 +    | true          | True              |
   1.269 +    +---------------+-------------------+
   1.270 +    | false         | False             |
   1.271 +    +---------------+-------------------+
   1.272 +    | null          | None              |
   1.273 +    +---------------+-------------------+
   1.274 +
   1.275 +    It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
   1.276 +    their corresponding ``float`` values, which is outside the JSON spec.
   1.277 +    """
   1.278 +
   1.279 +    _scanner = Scanner(ANYTHING)
   1.280 +    __all__ = ['__init__', 'decode', 'raw_decode']
   1.281 +
   1.282 +    def __init__(self, encoding=None, object_hook=None, parse_float=None,
   1.283 +            parse_int=None, parse_constant=None, strict=True):
   1.284 +        """
   1.285 +        ``encoding`` determines the encoding used to interpret any ``str``
   1.286 +        objects decoded by this instance (utf-8 by default).  It has no
   1.287 +        effect when decoding ``unicode`` objects.
   1.288 +        
   1.289 +        Note that currently only encodings that are a superset of ASCII work,
   1.290 +        strings of other encodings should be passed in as ``unicode``.
   1.291 +
   1.292 +        ``object_hook``, if specified, will be called with the result
   1.293 +        of every JSON object decoded and its return value will be used in
   1.294 +        place of the given ``dict``.  This can be used to provide custom
   1.295 +        deserializations (e.g. to support JSON-RPC class hinting).
   1.296 +
   1.297 +        ``parse_float``, if specified, will be called with the string
   1.298 +        of every JSON float to be decoded. By default this is equivalent to
   1.299 +        float(num_str). This can be used to use another datatype or parser
   1.300 +        for JSON floats (e.g. decimal.Decimal).
   1.301 +
   1.302 +        ``parse_int``, if specified, will be called with the string
   1.303 +        of every JSON int to be decoded. By default this is equivalent to
   1.304 +        int(num_str). This can be used to use another datatype or parser
   1.305 +        for JSON integers (e.g. float).
   1.306 +
   1.307 +        ``parse_constant``, if specified, will be called with one of the
   1.308 +        following strings: -Infinity, Infinity, NaN, null, true, false.
   1.309 +        This can be used to raise an exception if invalid JSON numbers
   1.310 +        are encountered.
   1.311 +        """
   1.312 +        self.encoding = encoding
   1.313 +        self.object_hook = object_hook
   1.314 +        self.parse_float = parse_float
   1.315 +        self.parse_int = parse_int
   1.316 +        self.parse_constant = parse_constant
   1.317 +        self.strict = strict
   1.318 +
   1.319 +    def decode(self, s, _w=WHITESPACE.match):
   1.320 +        """
   1.321 +        Return the Python representation of ``s`` (a ``str`` or ``unicode``
   1.322 +        instance containing a JSON document)
   1.323 +        """
   1.324 +        obj, end = self.raw_decode(s, idx=_w(s, 0).end())
   1.325 +        end = _w(s, end).end()
   1.326 +        if end != len(s):
   1.327 +            raise ValueError(errmsg("Extra data", s, end, len(s)))
   1.328 +        return obj
   1.329 +
   1.330 +    def raw_decode(self, s, **kw):
   1.331 +        """
   1.332 +        Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
   1.333 +        with a JSON document) and return a 2-tuple of the Python
   1.334 +        representation and the index in ``s`` where the document ended.
   1.335 +
   1.336 +        This can be used to decode a JSON document from a string that may
   1.337 +        have extraneous data at the end.
   1.338 +        """
   1.339 +        kw.setdefault('context', self)
   1.340 +        try:
   1.341 +            obj, end = self._scanner.iterscan(s, **kw).next()
   1.342 +        except StopIteration:
   1.343 +            raise ValueError("No JSON object could be decoded")
   1.344 +        return obj, end
   1.345 +
   1.346 +__all__ = ['JSONDecoder']

mercurial