Author: Armin Rigo <[email protected]>
Branch: py3.5
Changeset: r89632:b947344ca26e
Date: 2017-01-17 12:33 +0100
http://bitbucket.org/pypy/pypy/changeset/b947344ca26e/
Log: Move the error reporting from json.decode() in line with Python 3's
expectations
diff --git a/lib-python/3/json/__init__.py b/lib-python/3/json/__init__.py
--- a/lib-python/3/json/__init__.py
+++ b/lib-python/3/json/__init__.py
@@ -322,7 +322,8 @@
if (cls is None and object_hook is None and
parse_int is None and parse_float is None and
parse_constant is None and object_pairs_hook is None and not kw):
- return _pypyjson.loads(s) if _pypyjson else _default_decoder.decode(s)
+ return (_pypyjson.loads(s, JSONDecodeError)
+ if _pypyjson else _default_decoder.decode(s))
if cls is None:
cls = JSONDecoder
if object_hook is not None:
diff --git a/pypy/module/_pypyjson/interp_decoder.py
b/pypy/module/_pypyjson/interp_decoder.py
--- a/pypy/module/_pypyjson/interp_decoder.py
+++ b/pypy/module/_pypyjson/interp_decoder.py
@@ -3,7 +3,7 @@
from rpython.rlib.objectmodel import specialize, always_inline
from rpython.rlib import rfloat, runicode
from rpython.rtyper.lltypesystem import lltype, rffi
-from pypy.interpreter.error import oefmt
+from pypy.interpreter.error import oefmt, OperationError
from pypy.interpreter import unicodehelper
OVF_DIGITS = len(str(sys.maxint))
@@ -42,6 +42,11 @@
ll_res.chars[i] = cast_primitive(UniChar, ch)
return hlunicode(ll_res)
+class DecoderError(Exception):
+ def __init__(self, msg, pos):
+ self.msg = msg
+ self.pos = pos
+
TYPE_UNKNOWN = 0
TYPE_STRING = 1
class JSONDecoder(object):
@@ -76,10 +81,6 @@
break
return i
- @specialize.arg(1)
- def _raise(self, msg, *args):
- raise oefmt(self.space.w_ValueError, msg, *args)
-
def decode_any(self, i):
i = self.skip_whitespace(i)
ch = self.ll_chars[i]
@@ -106,8 +107,9 @@
elif ch.isdigit():
return self.decode_numeric(i)
else:
- self._raise("No JSON object could be decoded: unexpected '%s' at
char %d",
- ch, self.pos)
+ raise DecoderError(
+ "No JSON object could be decoded: unexpected '%s' at" % ch,
+ self.pos)
def decode_null(self, i):
if (self.ll_chars[i] == 'u' and
@@ -115,7 +117,7 @@
self.ll_chars[i+2] == 'l'):
self.pos = i+3
return self.space.w_None
- self._raise("Error when decoding null at char %d", i)
+ raise DecoderError("Error when decoding null at", i)
def decode_true(self, i):
if (self.ll_chars[i] == 'r' and
@@ -123,7 +125,7 @@
self.ll_chars[i+2] == 'e'):
self.pos = i+3
return self.space.w_True
- self._raise("Error when decoding true at char %d", i)
+ raise DecoderError("Error when decoding true at", i)
def decode_false(self, i):
if (self.ll_chars[i] == 'a' and
@@ -132,7 +134,7 @@
self.ll_chars[i+3] == 'e'):
self.pos = i+4
return self.space.w_False
- self._raise("Error when decoding false at char %d", i)
+ raise DecoderError("Error when decoding false at", i)
def decode_infinity(self, i, sign=1):
if (self.ll_chars[i] == 'n' and
@@ -144,14 +146,14 @@
self.ll_chars[i+6] == 'y'):
self.pos = i+7
return self.space.wrap(rfloat.INFINITY * sign)
- self._raise("Error when decoding Infinity at char %d", i)
+ raise DecoderError("Error when decoding Infinity at", i)
def decode_nan(self, i):
if (self.ll_chars[i] == 'a' and
self.ll_chars[i+1] == 'N'):
self.pos = i+2
return self.space.wrap(rfloat.NAN)
- self._raise("Error when decoding NaN at char %d", i)
+ raise DecoderError("Error when decoding NaN at", i)
def decode_numeric(self, i):
start = i
@@ -161,7 +163,7 @@
ch = self.ll_chars[i]
if ch == '.':
if not self.ll_chars[i+1].isdigit():
- self._raise("Expected digit at char %d", i+1)
+ raise DecoderError("Expected digit at", i+1)
return self.decode_float(start)
elif ch == 'e' or ch == 'E':
return self.decode_float(start)
@@ -215,7 +217,7 @@
break
count = i - start
if count == 0:
- self._raise("Expected digit at char %d", i)
+ raise DecoderError("Expected digit at", i)
# if the number has more digits than OVF_DIGITS, it might have
# overflowed
ovf_maybe = (count >= OVF_DIGITS)
@@ -242,10 +244,10 @@
elif ch == ',':
pass
elif ch == '\0':
- self._raise("Unterminated array starting at char %d", start)
+ raise DecoderError("Unterminated array starting at", start)
else:
- self._raise("Unexpected '%s' when decoding array (char %d)",
- ch, self.pos)
+ raise DecoderError("Unexpected '%s' when decoding array" % ch,
+ self.pos)
def decode_object(self, i):
start = i
@@ -261,13 +263,13 @@
self.last_type = TYPE_UNKNOWN
w_name = self.decode_any(i)
if self.last_type != TYPE_STRING:
- self._raise("Key name must be string for object starting at
char %d", start)
+ raise DecoderError("Key name must be string for object
starting at", start)
w_name = self.memo.setdefault(self.space.unicode_w(w_name), w_name)
i = self.skip_whitespace(self.pos)
ch = self.ll_chars[i]
if ch != ':':
- self._raise("No ':' found at char %d", i)
+ raise DecoderError("No ':' found at", i)
i += 1
i = self.skip_whitespace(i)
#
@@ -282,10 +284,10 @@
elif ch == ',':
pass
elif ch == '\0':
- self._raise("Unterminated object starting at char %d", start)
+ raise DecoderError("Unterminated object starting at", start)
else:
- self._raise("Unexpected '%s' when decoding object (char %d)",
- ch, self.pos)
+ raise DecoderError("Unexpected '%s' when decoding object" % ch,
+ self.pos)
def decode_string(self, i):
@@ -315,7 +317,7 @@
self.pos = i-1
return self.decode_string_escaped(start, content_so_far)
elif ch < '\x20':
- self._raise("Invalid control character at char %d", self.pos-1)
+ raise DecoderError("Invalid control character at", self.pos-1)
def decode_string_escaped(self, start, content_so_far):
@@ -335,7 +337,7 @@
elif ch == '\\':
i = self.decode_escape_sequence(i, builder)
elif ch == '\0':
- self._raise("Unterminated string starting at char %d", start)
+ raise DecoderError("Unterminated string starting at", start)
else:
builder.append_multiple_char(ch, 1) # we should implement
append_char
@@ -354,7 +356,7 @@
elif ch == 'u':
return self.decode_escape_sequence_unicode(i, builder)
else:
- self._raise("Invalid \\escape: %s (char %d)", ch, self.pos-1)
+ raise DecoderError("Invalid \\escape: %s" % ch, self.pos-1)
return i
def decode_escape_sequence_unicode(self, i, builder):
@@ -370,9 +372,7 @@
val = self.decode_surrogate_pair(i, val)
i += 6
except ValueError:
- self._raise("Invalid \uXXXX escape (char %d)", i-1)
- return # help the annotator to know that we'll never go beyond
- # this point
+ raise DecoderError("Invalid \\uXXXX escape", i-1)
#
uchr = runicode.code_to_unichr(val) # may be a surrogate pair again
utf8_ch = unicodehelper.encode_utf8(
@@ -389,7 +389,7 @@
lowsurr = int(hexdigits, 16) # the possible ValueError is caugth by
the caller
return 0x10000 + (((highsurr - 0xd800) << 10) | (lowsurr - 0xdc00))
-def loads(space, w_s):
+def loads(space, w_s, w_errorcls=None):
if space.isinstance_w(w_s, space.w_bytes):
raise oefmt(space.w_TypeError, "Expected string, got %T", w_s)
s = space.str_w(w_s)
@@ -399,9 +399,13 @@
i = decoder.skip_whitespace(decoder.pos)
if i < len(s):
start = i
- end = len(s) - 1
- raise oefmt(space.w_ValueError,
- "Extra data: char %d - %d", start, end)
+ raise DecoderError('Extra data', start)
return w_res
+ except DecoderError as e:
+ if w_errorcls is None:
+ w_errorcls = space.w_ValueError
+ w_e = space.call_function(w_errorcls, space.wrap(e.msg), w_s,
+ space.wrap(e.pos))
+ raise OperationError(w_errorcls, w_e)
finally:
decoder.close()
diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py
b/pypy/module/_pypyjson/test/test__pypyjson.py
--- a/pypy/module/_pypyjson/test/test__pypyjson.py
+++ b/pypy/module/_pypyjson/test/test__pypyjson.py
@@ -222,3 +222,10 @@
(a, b), (c, d) = sorted(rval[0]), sorted(rval[1])
assert a is c
assert b is d
+
+ def test_custom_error_class(self):
+ import _pypyjson
+ class MyError(Exception):
+ pass
+ exc = raises(MyError, _pypyjson.loads, 'nul', MyError)
+ assert exc.value.args == ('Error when decoding null at', 'nul', 1)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit