Skip to content

Commit

Permalink
Raise JSONDecodeError in place of ValueError on failed decode (#498)
Browse files Browse the repository at this point in the history
So as to match the behaviour of Python's json library.
Fixes #497
  • Loading branch information
JustAnotherArchivist committed Feb 5, 2022
1 parent d5b701f commit 316d384
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 33 deletions.
3 changes: 2 additions & 1 deletion python/JSONtoObj.c
Expand Up @@ -38,6 +38,7 @@ Numeric decoder derived from from TCL library

#include <Python.h>
#include <ultrajson.h>
#include "ujson.h"


//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
Expand Down Expand Up @@ -187,7 +188,7 @@ PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs)
/*
FIXME: It's possible to give a much nicer error message here with actual failing element in input etc*/

PyErr_Format (PyExc_ValueError, "%s", decoder.errorStr);
PyErr_Format (JSONDecodeError, "%s", decoder.errorStr);

if (ret)
{
Expand Down
13 changes: 13 additions & 0 deletions python/ujson.c
Expand Up @@ -38,6 +38,7 @@ Numeric decoder derived from from TCL library

#include <Python.h>
#include "version.h"
#include "ujson.h"

/* objToJSON */
PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs);
Expand All @@ -51,6 +52,8 @@ PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs);
/* JSONFileToObj */
PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs);

PyObject* JSONDecodeError;


#define ENCODER_HELP_TEXT "Use ensure_ascii=false to output UTF-8. " \
"Set encode_html_chars=True to encode < > & as unicode escape sequences. "\
Expand Down Expand Up @@ -188,5 +191,15 @@ PyMODINIT_FUNC PyInit_ujson(void)
PyErr_Clear();
#endif

JSONDecodeError = PyErr_NewException("ujson.JSONDecodeError", PyExc_ValueError, NULL);
Py_XINCREF(JSONDecodeError);
if (PyModule_AddObject(module, "JSONDecodeError", JSONDecodeError) < 0)
{
Py_XDECREF(JSONDecodeError);
Py_CLEAR(JSONDecodeError);
Py_DECREF(module);
return NULL;
}

return module;
}
1 change: 1 addition & 0 deletions python/ujson.h
@@ -0,0 +1 @@
extern PyObject* JSONDecodeError;
68 changes: 36 additions & 32 deletions tests/test_ujson.py
Expand Up @@ -555,46 +555,46 @@ def test_decode_numeric_int_exp(test_input):
@pytest.mark.parametrize(
"test_input, expected",
[
('{{1337:""}}', ValueError), # broken dict key type leak test
('{{"key":"}', ValueError), # broken dict leak test
('{{"key":"}', ValueError), # broken dict leak test
("[[[true", ValueError), # broken list leak test
('{{1337:""}}', ujson.JSONDecodeError), # broken dict key type leak test
('{{"key":"}', ujson.JSONDecodeError), # broken dict leak test
('{{"key":"}', ujson.JSONDecodeError), # broken dict leak test
("[[[true", ujson.JSONDecodeError), # broken list leak test
],
)
def test_decode_range_raises(test_input, expected):
for x in range(1000):
with pytest.raises(ValueError):
with pytest.raises(expected):
ujson.decode(test_input)


@pytest.mark.parametrize(
"test_input, expected",
[
("fdsa sda v9sa fdsa", ValueError), # jibberish
("[", ValueError), # broken array start
("{", ValueError), # broken object start
("]", ValueError), # broken array end
("}", ValueError), # broken object end
('{"one":1,}', ValueError), # object trailing comma fail
('"TESTING', ValueError), # string unterminated
('"TESTING\\"', ValueError), # string bad escape
("tru", ValueError), # true broken
("fa", ValueError), # false broken
("n", ValueError), # null broken
("{{{{31337}}}}", ValueError), # dict with no key
('{{{{"key"}}}}', ValueError), # dict with no colon or value
('{{{{"key":}}}}', ValueError), # dict with no value
("[31337,]", ValueError), # array trailing comma fail
("[,31337]", ValueError), # array leading comma fail
("[,]", ValueError), # array only comma fail
("[]]", ValueError), # array unmatched bracket fail
("18446744073709551616", ValueError), # too big value
("-90223372036854775809", ValueError), # too small value
("18446744073709551616", ValueError), # very too big value
("-90223372036854775809", ValueError), # very too small value
("{}\n\t a", ValueError), # with trailing non whitespaces
("[18446744073709551616]", ValueError), # array with big int
('{"age", 44}', ValueError), # read bad object syntax
("fdsa sda v9sa fdsa", ujson.JSONDecodeError), # jibberish
("[", ujson.JSONDecodeError), # broken array start
("{", ujson.JSONDecodeError), # broken object start
("]", ujson.JSONDecodeError), # broken array end
("}", ujson.JSONDecodeError), # broken object end
('{"one":1,}', ujson.JSONDecodeError), # object trailing comma fail
('"TESTING', ujson.JSONDecodeError), # string unterminated
('"TESTING\\"', ujson.JSONDecodeError), # string bad escape
("tru", ujson.JSONDecodeError), # true broken
("fa", ujson.JSONDecodeError), # false broken
("n", ujson.JSONDecodeError), # null broken
("{{{{31337}}}}", ujson.JSONDecodeError), # dict with no key
('{{{{"key"}}}}', ujson.JSONDecodeError), # dict with no colon or value
('{{{{"key":}}}}', ujson.JSONDecodeError), # dict with no value
("[31337,]", ujson.JSONDecodeError), # array trailing comma fail
("[,31337]", ujson.JSONDecodeError), # array leading comma fail
("[,]", ujson.JSONDecodeError), # array only comma fail
("[]]", ujson.JSONDecodeError), # array unmatched bracket fail
("18446744073709551616", ujson.JSONDecodeError), # too big value
("-90223372036854775809", ujson.JSONDecodeError), # too small value
("18446744073709551616", ujson.JSONDecodeError), # very too big value
("-90223372036854775809", ujson.JSONDecodeError), # very too small value
("{}\n\t a", ujson.JSONDecodeError), # with trailing non whitespaces
("[18446744073709551616]", ujson.JSONDecodeError), # array with big int
('{"age", 44}', ujson.JSONDecodeError), # read bad object syntax
],
)
def test_decode_raises(test_input, expected):
Expand All @@ -605,15 +605,19 @@ def test_decode_raises(test_input, expected):
@pytest.mark.parametrize(
"test_input, expected",
[
("[", ValueError), # array depth too big
("{", ValueError), # object depth too big
("[", ujson.JSONDecodeError), # array depth too big
("{", ujson.JSONDecodeError), # object depth too big
],
)
def test_decode_raises_for_long_input(test_input, expected):
with pytest.raises(expected):
ujson.decode(test_input * (1024 * 1024))


def test_decode_exception_is_value_error():
assert issubclass(ujson.JSONDecodeError, ValueError)


@pytest.mark.parametrize(
"test_input, expected",
[
Expand Down

0 comments on commit 316d384

Please sign in to comment.