+++ /dev/null
-"""Implementation of JSONDecoder\r
-"""\r
-import re\r
-import sys\r
-import struct\r
-\r
-from json import scanner\r
-try:\r
- from _json import scanstring as c_scanstring\r
-except ImportError:\r
- c_scanstring = None\r
-\r
-__all__ = ['JSONDecoder']\r
-\r
-FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL\r
-\r
-def _floatconstants():\r
- _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')\r
- if sys.byteorder != 'big':\r
- _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]\r
- nan, inf = struct.unpack('dd', _BYTES)\r
- return nan, inf, -inf\r
-\r
-NaN, PosInf, NegInf = _floatconstants()\r
-\r
-\r
-def linecol(doc, pos):\r
- lineno = doc.count('\n', 0, pos) + 1\r
- if lineno == 1:\r
- colno = pos + 1\r
- else:\r
- colno = pos - doc.rindex('\n', 0, pos)\r
- return lineno, colno\r
-\r
-\r
-def errmsg(msg, doc, pos, end=None):\r
- # Note that this function is called from _json\r
- lineno, colno = linecol(doc, pos)\r
- if end is None:\r
- fmt = '{0}: line {1} column {2} (char {3})'\r
- return fmt.format(msg, lineno, colno, pos)\r
- #fmt = '%s: line %d column %d (char %d)'\r
- #return fmt % (msg, lineno, colno, pos)\r
- endlineno, endcolno = linecol(doc, end)\r
- fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'\r
- return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)\r
- #fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'\r
- #return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)\r
-\r
-\r
-_CONSTANTS = {\r
- '-Infinity': NegInf,\r
- 'Infinity': PosInf,\r
- 'NaN': NaN,\r
-}\r
-\r
-STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)\r
-BACKSLASH = {\r
- '"': u'"', '\\': u'\\', '/': u'/',\r
- 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',\r
-}\r
-\r
-DEFAULT_ENCODING = "utf-8"\r
-\r
-def _decode_uXXXX(s, pos):\r
- esc = s[pos + 1:pos + 5]\r
- if len(esc) == 4 and esc[1] not in 'xX':\r
- try:\r
- return int(esc, 16)\r
- except ValueError:\r
- pass\r
- msg = "Invalid \\uXXXX escape"\r
- raise ValueError(errmsg(msg, s, pos))\r
-\r
-def py_scanstring(s, end, encoding=None, strict=True,\r
- _b=BACKSLASH, _m=STRINGCHUNK.match):\r
- """Scan the string s for a JSON string. End is the index of the\r
- character in s after the quote that started the JSON string.\r
- Unescapes all valid JSON string escape sequences and raises ValueError\r
- on attempt to decode an invalid string. If strict is False then literal\r
- control characters are allowed in the string.\r
-\r
- Returns a tuple of the decoded string and the index of the character in s\r
- after the end quote."""\r
- if encoding is None:\r
- encoding = DEFAULT_ENCODING\r
- chunks = []\r
- _append = chunks.append\r
- begin = end - 1\r
- while 1:\r
- chunk = _m(s, end)\r
- if chunk is None:\r
- raise ValueError(\r
- errmsg("Unterminated string starting at", s, begin))\r
- end = chunk.end()\r
- content, terminator = chunk.groups()\r
- # Content is contains zero or more unescaped string characters\r
- if content:\r
- if not isinstance(content, unicode):\r
- content = unicode(content, encoding)\r
- _append(content)\r
- # Terminator is the end of string, a literal control character,\r
- # or a backslash denoting that an escape sequence follows\r
- if terminator == '"':\r
- break\r
- elif terminator != '\\':\r
- if strict:\r
- #msg = "Invalid control character %r at" % (terminator,)\r
- msg = "Invalid control character {0!r} at".format(terminator)\r
- raise ValueError(errmsg(msg, s, end))\r
- else:\r
- _append(terminator)\r
- continue\r
- try:\r
- esc = s[end]\r
- except IndexError:\r
- raise ValueError(\r
- errmsg("Unterminated string starting at", s, begin))\r
- # If not a unicode escape sequence, must be in the lookup table\r
- if esc != 'u':\r
- try:\r
- char = _b[esc]\r
- except KeyError:\r
- msg = "Invalid \\escape: " + repr(esc)\r
- raise ValueError(errmsg(msg, s, end))\r
- end += 1\r
- else:\r
- # Unicode escape sequence\r
- uni = _decode_uXXXX(s, end)\r
- end += 5\r
- # Check for surrogate pair on UCS-4 systems\r
- if sys.maxunicode > 65535 and \\r
- 0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':\r
- uni2 = _decode_uXXXX(s, end + 1)\r
- if 0xdc00 <= uni2 <= 0xdfff:\r
- uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))\r
- end += 6\r
- char = unichr(uni)\r
- # Append the unescaped character\r
- _append(char)\r
- return u''.join(chunks), end\r
-\r
-\r
-# Use speedup if available\r
-scanstring = c_scanstring or py_scanstring\r
-\r
-WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)\r
-WHITESPACE_STR = ' \t\n\r'\r
-\r
-def JSONObject(s_and_end, encoding, strict, scan_once, object_hook,\r
- object_pairs_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):\r
- s, end = s_and_end\r
- pairs = []\r
- pairs_append = pairs.append\r
- # Use a slice to prevent IndexError from being raised, the following\r
- # check will raise a more specific ValueError if the string is empty\r
- nextchar = s[end:end + 1]\r
- # Normally we expect nextchar == '"'\r
- if nextchar != '"':\r
- if nextchar in _ws:\r
- end = _w(s, end).end()\r
- nextchar = s[end:end + 1]\r
- # Trivial empty object\r
- if nextchar == '}':\r
- if object_pairs_hook is not None:\r
- result = object_pairs_hook(pairs)\r
- return result, end + 1\r
- pairs = {}\r
- if object_hook is not None:\r
- pairs = object_hook(pairs)\r
- return pairs, end + 1\r
- elif nextchar != '"':\r
- raise ValueError(errmsg(\r
- "Expecting property name enclosed in double quotes", s, end))\r
- end += 1\r
- while True:\r
- key, end = scanstring(s, end, encoding, strict)\r
-\r
- # To skip some function call overhead we optimize the fast paths where\r
- # the JSON key separator is ": " or just ":".\r
- if s[end:end + 1] != ':':\r
- end = _w(s, end).end()\r
- if s[end:end + 1] != ':':\r
- raise ValueError(errmsg("Expecting ':' delimiter", s, end))\r
- end += 1\r
-\r
- try:\r
- if s[end] in _ws:\r
- end += 1\r
- if s[end] in _ws:\r
- end = _w(s, end + 1).end()\r
- except IndexError:\r
- pass\r
-\r
- try:\r
- value, end = scan_once(s, end)\r
- except StopIteration:\r
- raise ValueError(errmsg("Expecting object", s, end))\r
- pairs_append((key, value))\r
-\r
- try:\r
- nextchar = s[end]\r
- if nextchar in _ws:\r
- end = _w(s, end + 1).end()\r
- nextchar = s[end]\r
- except IndexError:\r
- nextchar = ''\r
- end += 1\r
-\r
- if nextchar == '}':\r
- break\r
- elif nextchar != ',':\r
- raise ValueError(errmsg("Expecting ',' delimiter", s, end - 1))\r
-\r
- try:\r
- nextchar = s[end]\r
- if nextchar in _ws:\r
- end += 1\r
- nextchar = s[end]\r
- if nextchar in _ws:\r
- end = _w(s, end + 1).end()\r
- nextchar = s[end]\r
- except IndexError:\r
- nextchar = ''\r
-\r
- end += 1\r
- if nextchar != '"':\r
- raise ValueError(errmsg(\r
- "Expecting property name enclosed in double quotes", s, end - 1))\r
- if object_pairs_hook is not None:\r
- result = object_pairs_hook(pairs)\r
- return result, end\r
- pairs = dict(pairs)\r
- if object_hook is not None:\r
- pairs = object_hook(pairs)\r
- return pairs, end\r
-\r
-def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):\r
- s, end = s_and_end\r
- values = []\r
- nextchar = s[end:end + 1]\r
- if nextchar in _ws:\r
- end = _w(s, end + 1).end()\r
- nextchar = s[end:end + 1]\r
- # Look-ahead for trivial empty array\r
- if nextchar == ']':\r
- return values, end + 1\r
- _append = values.append\r
- while True:\r
- try:\r
- value, end = scan_once(s, end)\r
- except StopIteration:\r
- raise ValueError(errmsg("Expecting object", s, end))\r
- _append(value)\r
- nextchar = s[end:end + 1]\r
- if nextchar in _ws:\r
- end = _w(s, end + 1).end()\r
- nextchar = s[end:end + 1]\r
- end += 1\r
- if nextchar == ']':\r
- break\r
- elif nextchar != ',':\r
- raise ValueError(errmsg("Expecting ',' delimiter", s, end))\r
- try:\r
- if s[end] in _ws:\r
- end += 1\r
- if s[end] in _ws:\r
- end = _w(s, end + 1).end()\r
- except IndexError:\r
- pass\r
-\r
- return values, end\r
-\r
-class JSONDecoder(object):\r
- """Simple JSON <http://json.org> decoder\r
-\r
- Performs the following translations in decoding by default:\r
-\r
- +---------------+-------------------+\r
- | JSON | Python |\r
- +===============+===================+\r
- | object | dict |\r
- +---------------+-------------------+\r
- | array | list |\r
- +---------------+-------------------+\r
- | string | unicode |\r
- +---------------+-------------------+\r
- | number (int) | int, long |\r
- +---------------+-------------------+\r
- | number (real) | float |\r
- +---------------+-------------------+\r
- | true | True |\r
- +---------------+-------------------+\r
- | false | False |\r
- +---------------+-------------------+\r
- | null | None |\r
- +---------------+-------------------+\r
-\r
- It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as\r
- their corresponding ``float`` values, which is outside the JSON spec.\r
-\r
- """\r
-\r
- def __init__(self, encoding=None, object_hook=None, parse_float=None,\r
- parse_int=None, parse_constant=None, strict=True,\r
- object_pairs_hook=None):\r
- """``encoding`` determines the encoding used to interpret any ``str``\r
- objects decoded by this instance (utf-8 by default). It has no\r
- effect when decoding ``unicode`` objects.\r
-\r
- Note that currently only encodings that are a superset of ASCII work,\r
- strings of other encodings should be passed in as ``unicode``.\r
-\r
- ``object_hook``, if specified, will be called with the result\r
- of every JSON object decoded and its return value will be used in\r
- place of the given ``dict``. This can be used to provide custom\r
- deserializations (e.g. to support JSON-RPC class hinting).\r
-\r
- ``object_pairs_hook``, if specified will be called with the result of\r
- every JSON object decoded with an ordered list of pairs. The return\r
- value of ``object_pairs_hook`` will be used instead of the ``dict``.\r
- This feature can be used to implement custom decoders that rely on the\r
- order that the key and value pairs are decoded (for example,\r
- collections.OrderedDict will remember the order of insertion). If\r
- ``object_hook`` is also defined, the ``object_pairs_hook`` takes\r
- priority.\r
-\r
- ``parse_float``, if specified, will be called with the string\r
- of every JSON float to be decoded. By default this is equivalent to\r
- float(num_str). This can be used to use another datatype or parser\r
- for JSON floats (e.g. decimal.Decimal).\r
-\r
- ``parse_int``, if specified, will be called with the string\r
- of every JSON int to be decoded. By default this is equivalent to\r
- int(num_str). This can be used to use another datatype or parser\r
- for JSON integers (e.g. float).\r
-\r
- ``parse_constant``, if specified, will be called with one of the\r
- following strings: -Infinity, Infinity, NaN.\r
- This can be used to raise an exception if invalid JSON numbers\r
- are encountered.\r
-\r
- If ``strict`` is false (true is the default), then control\r
- characters will be allowed inside strings. Control characters in\r
- this context are those with character codes in the 0-31 range,\r
- including ``'\\t'`` (tab), ``'\\n'``, ``'\\r'`` and ``'\\0'``.\r
-\r
- """\r
- self.encoding = encoding\r
- self.object_hook = object_hook\r
- self.object_pairs_hook = object_pairs_hook\r
- self.parse_float = parse_float or float\r
- self.parse_int = parse_int or int\r
- self.parse_constant = parse_constant or _CONSTANTS.__getitem__\r
- self.strict = strict\r
- self.parse_object = JSONObject\r
- self.parse_array = JSONArray\r
- self.parse_string = scanstring\r
- self.scan_once = scanner.make_scanner(self)\r
-\r
- def decode(self, s, _w=WHITESPACE.match):\r
- """Return the Python representation of ``s`` (a ``str`` or ``unicode``\r
- instance containing a JSON document)\r
-\r
- """\r
- obj, end = self.raw_decode(s, idx=_w(s, 0).end())\r
- end = _w(s, end).end()\r
- if end != len(s):\r
- raise ValueError(errmsg("Extra data", s, end, len(s)))\r
- return obj\r
-\r
- def raw_decode(self, s, idx=0):\r
- """Decode a JSON document from ``s`` (a ``str`` or ``unicode``\r
- beginning with a JSON document) and return a 2-tuple of the Python\r
- representation and the index in ``s`` where the document ended.\r
-\r
- This can be used to decode a JSON document from a string that may\r
- have extraneous data at the end.\r
-\r
- """\r
- try:\r
- obj, end = self.scan_once(s, idx)\r
- except StopIteration:\r
- raise ValueError("No JSON object could be decoded")\r
- return obj, end\r