--- /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