simplejson 4.1.0__tar.gz → 4.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simplejson-4.1.0 → simplejson-4.1.2}/CHANGES.txt +24 -0
- {simplejson-4.1.0/simplejson.egg-info → simplejson-4.1.2}/PKG-INFO +1 -1
- {simplejson-4.1.0 → simplejson-4.1.2}/conf.py +2 -2
- {simplejson-4.1.0 → simplejson-4.1.2}/setup.py +1 -1
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/__init__.py +1 -1
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/_speedups.c +88 -26
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/_speedups_scan.h +6 -2
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/decoder.py +5 -3
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/encoder.py +33 -4
- simplejson-4.1.2/simplejson/tests/test_decimal.py +133 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_decode.py +27 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_fail.py +2 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_scanstring.py +46 -0
- {simplejson-4.1.0 → simplejson-4.1.2/simplejson.egg-info}/PKG-INFO +1 -1
- simplejson-4.1.0/simplejson/tests/test_decimal.py +0 -71
- {simplejson-4.1.0 → simplejson-4.1.2}/LICENSE.txt +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/MANIFEST.in +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/README.rst +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/index.rst +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/pyproject.toml +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/scripts/make_docs.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/setup.cfg +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/compat.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/errors.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/ordered_dict.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/raw_json.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/scanner.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/__init__.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/_cibw_runner.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/_helpers.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_bigint_as_string.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_bitsize_int_as_string.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_check_circular.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_default.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_dump.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_encode_basestring_ascii.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_encode_for_html.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_errors.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_float.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_for_json.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_free_threading.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_indent.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_item_sort_key.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_iterable.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_namedtuple.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_pass1.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_pass2.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_pass3.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_raw_json.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_recursion.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_separators.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_speedups.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_str_subclass.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_subclass.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_subinterpreters.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_tool.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_tuple.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tests/test_unicode.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson/tool.py +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson.egg-info/SOURCES.txt +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson.egg-info/dependency_links.txt +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/simplejson.egg-info/top_level.txt +0 -0
- {simplejson-4.1.0 → simplejson-4.1.2}/tsan_stress_simplejson.py +0 -0
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
Version 4.1.2 released 2026-08-26
|
|
2
|
+
|
|
3
|
+
* Fix control character error position when content precedes the control char
|
|
4
|
+
https://github.com/simplejson/simplejson/pull/379
|
|
5
|
+
* Report the offending char, not the backslash, for invalid \X escapes
|
|
6
|
+
https://github.com/simplejson/simplejson/pull/380
|
|
7
|
+
* Handle non-finite Decimals like floats in the encoder
|
|
8
|
+
https://github.com/simplejson/simplejson/pull/381
|
|
9
|
+
* Parse failures due to trailing commas are now reported consistently
|
|
10
|
+
across implementations
|
|
11
|
+
https://github.com/simplejson/simplejson/pull/382
|
|
12
|
+
|
|
13
|
+
Version 4.1.1 released 2026-04-24
|
|
14
|
+
|
|
15
|
+
* The ``build_wheels_py27`` CI job now also builds Python 2.7 wheels
|
|
16
|
+
for Windows AMD64 and Windows x86, joining the existing Py2.7
|
|
17
|
+
manylinux1 / manylinux2010 x86_64 wheels. This unblocks offline /
|
|
18
|
+
``--no-index`` installs on Py2.7-on-Windows (the original
|
|
19
|
+
reporter's case), which previously had no matching binary wheel on
|
|
20
|
+
PyPI, fell through to the sdist, and failed on the PEP 517
|
|
21
|
+
isolated-build step complaining that ``setuptools>=42`` was not in
|
|
22
|
+
the wheelhouse.
|
|
23
|
+
https://github.com/simplejson/simplejson/issues/377
|
|
24
|
+
|
|
1
25
|
Version 4.1.0 released 2026-04-22
|
|
2
26
|
|
|
3
27
|
* The C extension now accelerates encoding when ``indent=`` is set.
|
|
@@ -36,7 +36,7 @@ master_doc = 'index'
|
|
|
36
36
|
|
|
37
37
|
# General substitutions.
|
|
38
38
|
project = 'simplejson'
|
|
39
|
-
copyright = '
|
|
39
|
+
copyright = '2026, Bob Ippolito'
|
|
40
40
|
|
|
41
41
|
# The default replacements for |version| and |release|, also used in various
|
|
42
42
|
# other places throughout the built documents.
|
|
@@ -44,7 +44,7 @@ copyright = '2025, Bob Ippolito'
|
|
|
44
44
|
# The short X.Y version.
|
|
45
45
|
version = '4.1'
|
|
46
46
|
# The full version, including alpha/beta/rc tags.
|
|
47
|
-
release = '4.1.
|
|
47
|
+
release = '4.1.2'
|
|
48
48
|
|
|
49
49
|
# There are two options for replacing |today|: either, you set today to some
|
|
50
50
|
# non-false value, then it is used:
|
|
@@ -12,7 +12,7 @@ from distutils.errors import CCompilerError, DistutilsExecError, \
|
|
|
12
12
|
|
|
13
13
|
IS_PYPY = hasattr(sys, 'pypy_translation_info')
|
|
14
14
|
IS_GRAALPY = getattr(getattr(sys, "implementation", None), "name", None) == "graalpy"
|
|
15
|
-
VERSION = '4.1.
|
|
15
|
+
VERSION = '4.1.2'
|
|
16
16
|
DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python"
|
|
17
17
|
|
|
18
18
|
with open('README.rst', 'r') as f:
|
|
@@ -118,7 +118,7 @@ Serializing multiple objects to JSON lines (newline-delimited JSON)::
|
|
|
118
118
|
|
|
119
119
|
"""
|
|
120
120
|
from __future__ import absolute_import
|
|
121
|
-
__version__ = '4.1.
|
|
121
|
+
__version__ = '4.1.2'
|
|
122
122
|
__all__ = [
|
|
123
123
|
'dump', 'dumps', 'load', 'loads',
|
|
124
124
|
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
|
|
@@ -429,6 +429,8 @@ static PyObject *
|
|
|
429
429
|
encoder_long_to_str(PyObject *obj);
|
|
430
430
|
static PyObject *
|
|
431
431
|
encoder_encode_float(PyEncoderObject *s, PyObject *obj);
|
|
432
|
+
static PyObject *
|
|
433
|
+
encoder_encode_decimal(PyEncoderObject *s, PyObject *obj);
|
|
432
434
|
static int
|
|
433
435
|
encoder_accumulate_newline_indent(PyEncoderObject *s, _speedups_state *state,
|
|
434
436
|
JSON_Accu *rval, Py_ssize_t indent_level);
|
|
@@ -1062,7 +1064,7 @@ encoder_stringify_key(PyEncoderObject *s, PyObject *key)
|
|
|
1062
1064
|
return encoder_long_to_str(key);
|
|
1063
1065
|
}
|
|
1064
1066
|
else if (s->use_decimal && PyObject_TypeCheck(key, (PyTypeObject *)s->Decimal)) {
|
|
1065
|
-
return
|
|
1067
|
+
return encoder_encode_decimal(s, key);
|
|
1066
1068
|
}
|
|
1067
1069
|
if (s->skipkeys) {
|
|
1068
1070
|
Py_INCREF(Py_None);
|
|
@@ -1365,7 +1367,7 @@ scanstring_str(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1365
1367
|
default: c = 0;
|
|
1366
1368
|
}
|
|
1367
1369
|
if (c == 0) {
|
|
1368
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1370
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1369
1371
|
goto bail;
|
|
1370
1372
|
}
|
|
1371
1373
|
}
|
|
@@ -1564,7 +1566,7 @@ scanstring_unicode(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1564
1566
|
default: c = 0;
|
|
1565
1567
|
}
|
|
1566
1568
|
if (c == 0) {
|
|
1567
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1569
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1568
1570
|
goto bail;
|
|
1569
1571
|
}
|
|
1570
1572
|
}
|
|
@@ -1755,7 +1757,7 @@ scanstring_unicode(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1755
1757
|
default: c = 0;
|
|
1756
1758
|
}
|
|
1757
1759
|
if (c == 0) {
|
|
1758
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1760
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1759
1761
|
goto bail;
|
|
1760
1762
|
}
|
|
1761
1763
|
}
|
|
@@ -2785,33 +2787,38 @@ _encoded_const(_speedups_state *state, PyObject *obj)
|
|
|
2785
2787
|
}
|
|
2786
2788
|
}
|
|
2787
2789
|
|
|
2790
|
+
static PyObject *
|
|
2791
|
+
encoder_encode_nonfinite(PyEncoderObject *s, int sign)
|
|
2792
|
+
{
|
|
2793
|
+
_speedups_state *state = get_speedups_state(s->module_ref);
|
|
2794
|
+
|
|
2795
|
+
if (!s->allow_or_ignore_nan) {
|
|
2796
|
+
PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
|
|
2797
|
+
return NULL;
|
|
2798
|
+
}
|
|
2799
|
+
if (s->allow_or_ignore_nan & JSON_IGNORE_NAN) {
|
|
2800
|
+
return _encoded_const(state, Py_None);
|
|
2801
|
+
}
|
|
2802
|
+
/* JSON_ALLOW_NAN is set */
|
|
2803
|
+
if (sign > 0) {
|
|
2804
|
+
Py_INCREF(state->JSON_Infinity);
|
|
2805
|
+
return state->JSON_Infinity;
|
|
2806
|
+
}
|
|
2807
|
+
if (sign < 0) {
|
|
2808
|
+
Py_INCREF(state->JSON_NegInfinity);
|
|
2809
|
+
return state->JSON_NegInfinity;
|
|
2810
|
+
}
|
|
2811
|
+
Py_INCREF(state->JSON_NaN);
|
|
2812
|
+
return state->JSON_NaN;
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2788
2815
|
static PyObject *
|
|
2789
2816
|
encoder_encode_float(PyEncoderObject *s, PyObject *obj)
|
|
2790
2817
|
{
|
|
2791
2818
|
/* Return the JSON representation of a PyFloat */
|
|
2792
|
-
_speedups_state *state = get_speedups_state(s->module_ref);
|
|
2793
2819
|
double i = PyFloat_AS_DOUBLE(obj);
|
|
2794
2820
|
if (!Py_IS_FINITE(i)) {
|
|
2795
|
-
|
|
2796
|
-
PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
|
|
2797
|
-
return NULL;
|
|
2798
|
-
}
|
|
2799
|
-
if (s->allow_or_ignore_nan & JSON_IGNORE_NAN) {
|
|
2800
|
-
return _encoded_const(state, Py_None);
|
|
2801
|
-
}
|
|
2802
|
-
/* JSON_ALLOW_NAN is set */
|
|
2803
|
-
else if (i > 0) {
|
|
2804
|
-
Py_INCREF(state->JSON_Infinity);
|
|
2805
|
-
return state->JSON_Infinity;
|
|
2806
|
-
}
|
|
2807
|
-
else if (i < 0) {
|
|
2808
|
-
Py_INCREF(state->JSON_NegInfinity);
|
|
2809
|
-
return state->JSON_NegInfinity;
|
|
2810
|
-
}
|
|
2811
|
-
else {
|
|
2812
|
-
Py_INCREF(state->JSON_NaN);
|
|
2813
|
-
return state->JSON_NaN;
|
|
2814
|
-
}
|
|
2821
|
+
return encoder_encode_nonfinite(s, i > 0 ? 1 : (i < 0 ? -1 : 0));
|
|
2815
2822
|
}
|
|
2816
2823
|
/* Use a better float format here? */
|
|
2817
2824
|
if (PyFloat_CheckExact(obj)) {
|
|
@@ -2830,6 +2837,61 @@ encoder_encode_float(PyEncoderObject *s, PyObject *obj)
|
|
|
2830
2837
|
}
|
|
2831
2838
|
}
|
|
2832
2839
|
|
|
2840
|
+
static PyObject *
|
|
2841
|
+
encoder_encode_decimal(PyEncoderObject *s, PyObject *obj)
|
|
2842
|
+
{
|
|
2843
|
+
/* Return the JSON representation of a Decimal. Finite Decimals are
|
|
2844
|
+
stringified as-is (preserving precision and trailing zeros); non-finite
|
|
2845
|
+
Decimals (NaN, sNaN, Infinity) are routed through the same
|
|
2846
|
+
allow_nan/ignore_nan handling as floats so they never emit invalid
|
|
2847
|
+
JSON. See https://github.com/simplejson/simplejson/issues/149
|
|
2848
|
+
|
|
2849
|
+
str(Decimal) carries a leading '-' sign at most (never '+'); a finite
|
|
2850
|
+
value's first significant character is always a digit while a non-finite
|
|
2851
|
+
value's is a letter ('N'/'s' for [s]NaN, 'I' for Infinity). So a single
|
|
2852
|
+
str() plus a one/two character check distinguishes the two. */
|
|
2853
|
+
PyObject *str = PyObject_Str(obj);
|
|
2854
|
+
Py_ssize_t len;
|
|
2855
|
+
int negative;
|
|
2856
|
+
JSON_UNICHR c;
|
|
2857
|
+
#if PY_MAJOR_VERSION >= 3
|
|
2858
|
+
int kind;
|
|
2859
|
+
void *data;
|
|
2860
|
+
#else
|
|
2861
|
+
const char *data;
|
|
2862
|
+
#endif
|
|
2863
|
+
if (str == NULL)
|
|
2864
|
+
return NULL;
|
|
2865
|
+
#if PY_MAJOR_VERSION >= 3
|
|
2866
|
+
if (PyUnicode_READY(str)) {
|
|
2867
|
+
Py_DECREF(str);
|
|
2868
|
+
return NULL;
|
|
2869
|
+
}
|
|
2870
|
+
len = PyUnicode_GET_LENGTH(str);
|
|
2871
|
+
kind = PyUnicode_KIND(str);
|
|
2872
|
+
data = PyUnicode_DATA(str);
|
|
2873
|
+
negative = (len > 0 && PyUnicode_READ(kind, data, 0) == '-');
|
|
2874
|
+
c = (JSON_UNICHR)0;
|
|
2875
|
+
if (len > (negative ? 1 : 0))
|
|
2876
|
+
c = PyUnicode_READ(kind, data, negative ? 1 : 0);
|
|
2877
|
+
#else
|
|
2878
|
+
len = PyString_GET_SIZE(str);
|
|
2879
|
+
data = PyString_AS_STRING(str);
|
|
2880
|
+
negative = (len > 0 && data[0] == '-');
|
|
2881
|
+
c = (JSON_UNICHR)0;
|
|
2882
|
+
if (len > (negative ? 1 : 0))
|
|
2883
|
+
c = (unsigned char)data[negative ? 1 : 0];
|
|
2884
|
+
#endif
|
|
2885
|
+
if (IS_DIGIT(c)) {
|
|
2886
|
+
/* First significant character is a digit: finite Decimal, emit as-is. */
|
|
2887
|
+
return str;
|
|
2888
|
+
}
|
|
2889
|
+
/* Non-finite Decimal; use the same allow_nan/ignore_nan path as floats
|
|
2890
|
+
without creating a temporary float. */
|
|
2891
|
+
Py_DECREF(str);
|
|
2892
|
+
return encoder_encode_nonfinite(s, c == 'I' ? (negative ? -1 : 1) : 0);
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2833
2895
|
static PyObject *
|
|
2834
2896
|
encoder_encode_string(PyEncoderObject *s, PyObject *obj)
|
|
2835
2897
|
{
|
|
@@ -3090,7 +3152,7 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
|
|
|
3090
3152
|
Py_LeaveRecursiveCall();
|
|
3091
3153
|
}
|
|
3092
3154
|
else if (s->use_decimal && PyObject_TypeCheck(obj, (PyTypeObject *)s->Decimal)) {
|
|
3093
|
-
PyObject *encoded =
|
|
3155
|
+
PyObject *encoded = encoder_encode_decimal(s, obj);
|
|
3094
3156
|
if (encoded != NULL)
|
|
3095
3157
|
rv = _steal_accumulate(state, rval, encoded);
|
|
3096
3158
|
}
|
|
@@ -204,6 +204,7 @@ JSON_SCAN_FN(_parse_object)(PyScannerObject *s, PyObject *pystr,
|
|
|
204
204
|
/* only loop if the object is non-empty */
|
|
205
205
|
if (idx <= end_idx && JSON_SCAN_READ(idx) != '}') {
|
|
206
206
|
int trailing_delimiter = 0;
|
|
207
|
+
Py_ssize_t comma_idx = 0;
|
|
207
208
|
while (idx <= end_idx) {
|
|
208
209
|
trailing_delimiter = 0;
|
|
209
210
|
|
|
@@ -271,6 +272,7 @@ JSON_SCAN_FN(_parse_object)(PyScannerObject *s, PyObject *pystr,
|
|
|
271
272
|
raise_errmsg(state, ERR_OBJECT_DELIMITER, pystr, idx);
|
|
272
273
|
goto bail;
|
|
273
274
|
}
|
|
275
|
+
comma_idx = idx;
|
|
274
276
|
idx++;
|
|
275
277
|
|
|
276
278
|
/* skip whitespace after , delimiter */
|
|
@@ -279,7 +281,7 @@ JSON_SCAN_FN(_parse_object)(PyScannerObject *s, PyObject *pystr,
|
|
|
279
281
|
|
|
280
282
|
/* check for trailing comma before } */
|
|
281
283
|
if (idx <= end_idx && JSON_SCAN_READ(idx) == '}') {
|
|
282
|
-
raise_errmsg(state, ERR_TRAILING_COMMA_OBJECT, pystr,
|
|
284
|
+
raise_errmsg(state, ERR_TRAILING_COMMA_OBJECT, pystr, comma_idx);
|
|
283
285
|
goto bail;
|
|
284
286
|
}
|
|
285
287
|
}
|
|
@@ -351,6 +353,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
351
353
|
/* only loop if the array is non-empty */
|
|
352
354
|
if (idx <= end_idx && JSON_SCAN_READ(idx) != ']') {
|
|
353
355
|
int trailing_delimiter = 0;
|
|
356
|
+
Py_ssize_t comma_idx = 0;
|
|
354
357
|
while (idx <= end_idx) {
|
|
355
358
|
trailing_delimiter = 0;
|
|
356
359
|
/* read any JSON term and de-tuplefy the (rval, idx) */
|
|
@@ -377,6 +380,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
377
380
|
raise_errmsg(state, ERR_ARRAY_DELIMITER, pystr, idx);
|
|
378
381
|
goto bail;
|
|
379
382
|
}
|
|
383
|
+
comma_idx = idx;
|
|
380
384
|
idx++;
|
|
381
385
|
|
|
382
386
|
/* skip whitespace after , */
|
|
@@ -385,7 +389,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
385
389
|
|
|
386
390
|
/* check for trailing comma before ] */
|
|
387
391
|
if (idx <= end_idx && JSON_SCAN_READ(idx) == ']') {
|
|
388
|
-
raise_errmsg(state, ERR_TRAILING_COMMA_ARRAY, pystr,
|
|
392
|
+
raise_errmsg(state, ERR_TRAILING_COMMA_ARRAY, pystr, comma_idx);
|
|
389
393
|
goto bail;
|
|
390
394
|
}
|
|
391
395
|
}
|
|
@@ -102,7 +102,7 @@ def py_scanstring(s, end, encoding=None, strict=True,
|
|
|
102
102
|
elif terminator != '\\':
|
|
103
103
|
if strict:
|
|
104
104
|
msg = "Invalid control character %r at"
|
|
105
|
-
raise JSONDecodeError(msg, s,
|
|
105
|
+
raise JSONDecodeError(msg, s, end - 1)
|
|
106
106
|
else:
|
|
107
107
|
_append(terminator)
|
|
108
108
|
continue
|
|
@@ -213,6 +213,7 @@ def JSONObject(state, encoding, strict, scan_once, object_hook,
|
|
|
213
213
|
break
|
|
214
214
|
elif nextchar != ',':
|
|
215
215
|
raise JSONDecodeError("Expecting ',' delimiter or '}'", s, end - 1)
|
|
216
|
+
comma_idx = end - 1
|
|
216
217
|
|
|
217
218
|
try:
|
|
218
219
|
nextchar = s[end]
|
|
@@ -230,7 +231,7 @@ def JSONObject(state, encoding, strict, scan_once, object_hook,
|
|
|
230
231
|
if nextchar == '}':
|
|
231
232
|
raise JSONDecodeError(
|
|
232
233
|
"Illegal trailing comma before end of object",
|
|
233
|
-
s,
|
|
234
|
+
s, comma_idx)
|
|
234
235
|
raise JSONDecodeError(
|
|
235
236
|
"Expecting property name enclosed in double quotes",
|
|
236
237
|
s, end - 1)
|
|
@@ -271,6 +272,7 @@ def JSONArray(state, scan_once, array_hook=None,
|
|
|
271
272
|
break
|
|
272
273
|
elif nextchar != ',':
|
|
273
274
|
raise JSONDecodeError("Expecting ',' delimiter or ']'", s, end - 1)
|
|
275
|
+
comma_idx = end - 1
|
|
274
276
|
|
|
275
277
|
try:
|
|
276
278
|
if s[end] in _ws:
|
|
@@ -283,7 +285,7 @@ def JSONArray(state, scan_once, array_hook=None,
|
|
|
283
285
|
if s[end:end + 1] == ']':
|
|
284
286
|
raise JSONDecodeError(
|
|
285
287
|
"Illegal trailing comma before end of array",
|
|
286
|
-
s,
|
|
288
|
+
s, comma_idx)
|
|
287
289
|
|
|
288
290
|
if array_hook is not None:
|
|
289
291
|
values = array_hook(values)
|
|
@@ -43,6 +43,35 @@ del i
|
|
|
43
43
|
|
|
44
44
|
FLOAT_REPR = repr
|
|
45
45
|
|
|
46
|
+
_NAN = float('nan')
|
|
47
|
+
_INFINITY = float('inf')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _encode_decimal(d, _floatstr):
|
|
51
|
+
"""Return the JSON representation of a ``Decimal``.
|
|
52
|
+
|
|
53
|
+
Finite Decimals are stringified as-is, preserving precision and trailing
|
|
54
|
+
zeros. Non-finite Decimals (``NaN``, ``sNaN``, ``Infinity``) are routed
|
|
55
|
+
through the same ``allow_nan``/``ignore_nan`` handling as floats so they
|
|
56
|
+
never emit invalid JSON (see #149).
|
|
57
|
+
|
|
58
|
+
``str(Decimal)`` yields a leading ``-`` sign at most (never ``+``); a
|
|
59
|
+
finite value's first significant character is always a digit, while a
|
|
60
|
+
non-finite value's is a letter (``N``, ``s`` or ``I``). So a single
|
|
61
|
+
``str()`` plus a one/two character check distinguishes the two.
|
|
62
|
+
"""
|
|
63
|
+
s = str(d)
|
|
64
|
+
c0 = s[:1]
|
|
65
|
+
c = s[1:2] if c0 == '-' else c0
|
|
66
|
+
if '0' <= c <= '9':
|
|
67
|
+
# First significant character is a digit: finite Decimal.
|
|
68
|
+
return s
|
|
69
|
+
# Non-finite Decimal. ``N``/``s`` -> NaN (float(Decimal('sNaN')) raises,
|
|
70
|
+
# so map both quiet and signaling NaN to a plain NaN); ``I`` -> Infinity.
|
|
71
|
+
if c == 'I':
|
|
72
|
+
return _floatstr(-_INFINITY if c0 == '-' else _INFINITY)
|
|
73
|
+
return _floatstr(_NAN)
|
|
74
|
+
|
|
46
75
|
# dict-like types that should be encoded as JSON objects.
|
|
47
76
|
# frozendict is a builtin added in CPython 3.15 (PEP 814).
|
|
48
77
|
if sys.version_info >= (3, 15):
|
|
@@ -536,7 +565,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
|
|
536
565
|
elif isinstance(value, float):
|
|
537
566
|
yield buf + _floatstr(value)
|
|
538
567
|
elif _use_decimal and isinstance(value, Decimal):
|
|
539
|
-
yield buf +
|
|
568
|
+
yield buf + _encode_decimal(value, _floatstr)
|
|
540
569
|
else:
|
|
541
570
|
yield buf
|
|
542
571
|
for_json = _for_json and call_method(value, 'for_json')
|
|
@@ -596,7 +625,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
|
|
596
625
|
key = int(key)
|
|
597
626
|
key = str(key)
|
|
598
627
|
elif _use_decimal and isinstance(key, Decimal):
|
|
599
|
-
key =
|
|
628
|
+
key = _encode_decimal(key, _floatstr)
|
|
600
629
|
elif _skipkeys:
|
|
601
630
|
key = None
|
|
602
631
|
else:
|
|
@@ -668,7 +697,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
|
|
668
697
|
elif isinstance(value, float):
|
|
669
698
|
yield _floatstr(value)
|
|
670
699
|
elif _use_decimal and isinstance(value, Decimal):
|
|
671
|
-
yield
|
|
700
|
+
yield _encode_decimal(value, _floatstr)
|
|
672
701
|
else:
|
|
673
702
|
for_json = _for_json and call_method(value, 'for_json')
|
|
674
703
|
if for_json:
|
|
@@ -744,7 +773,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
|
|
|
744
773
|
for chunk in _iterencode_dict(o, _current_indent_level):
|
|
745
774
|
yield chunk
|
|
746
775
|
elif _use_decimal and isinstance(o, Decimal):
|
|
747
|
-
yield
|
|
776
|
+
yield _encode_decimal(o, _floatstr)
|
|
748
777
|
else:
|
|
749
778
|
while _iterable_as_array:
|
|
750
779
|
# Markers are not checked here because it is valid for
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from unittest import TestCase
|
|
4
|
+
from simplejson.compat import StringIO, reload_module
|
|
5
|
+
|
|
6
|
+
import simplejson as json
|
|
7
|
+
|
|
8
|
+
class TestDecimal(TestCase):
|
|
9
|
+
NUMS = "1.0", "10.00", "1.1", "1234567890.1234567890", "500"
|
|
10
|
+
def dumps(self, obj, **kw):
|
|
11
|
+
sio = StringIO()
|
|
12
|
+
json.dump(obj, sio, **kw)
|
|
13
|
+
res = json.dumps(obj, **kw)
|
|
14
|
+
self.assertEqual(res, sio.getvalue())
|
|
15
|
+
return res
|
|
16
|
+
|
|
17
|
+
def loads(self, s, **kw):
|
|
18
|
+
sio = StringIO(s)
|
|
19
|
+
res = json.loads(s, **kw)
|
|
20
|
+
self.assertEqual(res, json.load(sio, **kw))
|
|
21
|
+
return res
|
|
22
|
+
|
|
23
|
+
def test_decimal_encode(self):
|
|
24
|
+
for d in map(Decimal, self.NUMS):
|
|
25
|
+
self.assertEqual(self.dumps(d, use_decimal=True), str(d))
|
|
26
|
+
|
|
27
|
+
def test_decimal_decode(self):
|
|
28
|
+
for s in self.NUMS:
|
|
29
|
+
self.assertEqual(self.loads(s, parse_float=Decimal), Decimal(s))
|
|
30
|
+
|
|
31
|
+
def test_stringify_key(self):
|
|
32
|
+
for d in map(Decimal, self.NUMS):
|
|
33
|
+
v = {d: d}
|
|
34
|
+
self.assertEqual(
|
|
35
|
+
self.loads(
|
|
36
|
+
self.dumps(v, use_decimal=True), parse_float=Decimal),
|
|
37
|
+
{str(d): d})
|
|
38
|
+
|
|
39
|
+
def test_decimal_roundtrip(self):
|
|
40
|
+
for d in map(Decimal, self.NUMS):
|
|
41
|
+
# The type might not be the same (int and Decimal) but they
|
|
42
|
+
# should still compare equal.
|
|
43
|
+
for v in [d, [d], {'': d}]:
|
|
44
|
+
self.assertEqual(
|
|
45
|
+
self.loads(
|
|
46
|
+
self.dumps(v, use_decimal=True), parse_float=Decimal),
|
|
47
|
+
v)
|
|
48
|
+
|
|
49
|
+
def test_decimal_defaults(self):
|
|
50
|
+
d = Decimal('1.1')
|
|
51
|
+
# use_decimal=True is the default
|
|
52
|
+
self.assertRaises(TypeError, json.dumps, d, use_decimal=False)
|
|
53
|
+
self.assertEqual('1.1', json.dumps(d))
|
|
54
|
+
self.assertEqual('1.1', json.dumps(d, use_decimal=True))
|
|
55
|
+
self.assertRaises(TypeError, json.dump, d, StringIO(),
|
|
56
|
+
use_decimal=False)
|
|
57
|
+
sio = StringIO()
|
|
58
|
+
json.dump(d, sio)
|
|
59
|
+
self.assertEqual('1.1', sio.getvalue())
|
|
60
|
+
sio = StringIO()
|
|
61
|
+
json.dump(d, sio, use_decimal=True)
|
|
62
|
+
self.assertEqual('1.1', sio.getvalue())
|
|
63
|
+
|
|
64
|
+
def test_decimal_reload(self):
|
|
65
|
+
# Simulate a subinterpreter that reloads the Python modules but not
|
|
66
|
+
# the C code https://github.com/simplejson/simplejson/issues/34
|
|
67
|
+
global Decimal
|
|
68
|
+
Decimal = reload_module(decimal).Decimal
|
|
69
|
+
import simplejson.encoder
|
|
70
|
+
simplejson.encoder.Decimal = Decimal
|
|
71
|
+
self.test_decimal_roundtrip()
|
|
72
|
+
|
|
73
|
+
def test_decimal_nan_allow(self):
|
|
74
|
+
# Non-finite Decimals should be treated the same as the matching
|
|
75
|
+
# float, i.e. emit the JavaScript literals when allow_nan is true.
|
|
76
|
+
# https://github.com/simplejson/simplejson/issues/149
|
|
77
|
+
self.assertEqual(json.dumps(Decimal('NaN'), allow_nan=True), 'NaN')
|
|
78
|
+
self.assertEqual(
|
|
79
|
+
json.dumps(Decimal('Infinity'), allow_nan=True), 'Infinity')
|
|
80
|
+
self.assertEqual(
|
|
81
|
+
json.dumps(Decimal('-Infinity'), allow_nan=True), '-Infinity')
|
|
82
|
+
# sNaN is also not finite and must not leak through as a literal.
|
|
83
|
+
self.assertEqual(json.dumps(Decimal('sNaN'), allow_nan=True), 'NaN')
|
|
84
|
+
|
|
85
|
+
def test_decimal_nan_ignore(self):
|
|
86
|
+
# ignore_nan should emit null for non-finite Decimals, matching float.
|
|
87
|
+
# https://github.com/simplejson/simplejson/issues/149
|
|
88
|
+
for d in (Decimal('NaN'), Decimal('sNaN'),
|
|
89
|
+
Decimal('Infinity'), Decimal('-Infinity')):
|
|
90
|
+
self.assertEqual(json.dumps(d, ignore_nan=True), 'null')
|
|
91
|
+
self.assertEqual(
|
|
92
|
+
json.dumps([Decimal('0.33'), Decimal('NaN'), Decimal('0.20')],
|
|
93
|
+
ignore_nan=True),
|
|
94
|
+
'[0.33, null, 0.20]')
|
|
95
|
+
|
|
96
|
+
def test_decimal_nan_deny(self):
|
|
97
|
+
# allow_nan=False must raise for non-finite Decimals rather than
|
|
98
|
+
# silently emit invalid JSON.
|
|
99
|
+
# https://github.com/simplejson/simplejson/issues/149
|
|
100
|
+
for d in (Decimal('NaN'), Decimal('sNaN'),
|
|
101
|
+
Decimal('Infinity'), Decimal('-Infinity')):
|
|
102
|
+
self.assertRaises(ValueError, json.dumps, d, allow_nan=False)
|
|
103
|
+
# allow_nan defaults to False, so a plain dumps must raise too.
|
|
104
|
+
self.assertRaises(ValueError, json.dumps, Decimal('NaN'))
|
|
105
|
+
|
|
106
|
+
def test_decimal_nan_as_value(self):
|
|
107
|
+
# Non-finite Decimals nested inside containers must be handled too.
|
|
108
|
+
self.assertEqual(
|
|
109
|
+
json.dumps({'a': Decimal('NaN')}, allow_nan=True), '{"a": NaN}')
|
|
110
|
+
self.assertEqual(
|
|
111
|
+
json.dumps({'a': Decimal('Infinity')}, ignore_nan=True),
|
|
112
|
+
'{"a": null}')
|
|
113
|
+
self.assertRaises(
|
|
114
|
+
ValueError, json.dumps, [Decimal('NaN')], allow_nan=False)
|
|
115
|
+
|
|
116
|
+
def test_decimal_nan_as_key(self):
|
|
117
|
+
# A non-finite Decimal used as a dict key follows the same rules.
|
|
118
|
+
self.assertEqual(
|
|
119
|
+
json.dumps({Decimal('NaN'): 1}, allow_nan=True), '{"NaN": 1}')
|
|
120
|
+
self.assertEqual(
|
|
121
|
+
json.dumps({Decimal('Infinity'): 1}, ignore_nan=True),
|
|
122
|
+
'{"null": 1}')
|
|
123
|
+
self.assertRaises(
|
|
124
|
+
ValueError, json.dumps, {Decimal('NaN'): 1}, allow_nan=False)
|
|
125
|
+
|
|
126
|
+
def test_decimal_finite_unaffected(self):
|
|
127
|
+
# The fix must not perturb finite Decimals, including zero and
|
|
128
|
+
# values that would lose trailing zeros if routed through float().
|
|
129
|
+
for s in ('0', '0.00', '-0', '1.50', '1E-30', '1234567890.1234567890'):
|
|
130
|
+
d = Decimal(s)
|
|
131
|
+
self.assertEqual(json.dumps(d), str(d))
|
|
132
|
+
self.assertEqual(json.dumps(d, ignore_nan=True), str(d))
|
|
133
|
+
self.assertEqual(json.dumps(d, allow_nan=True), str(d))
|
|
@@ -165,6 +165,33 @@ class TestDecode(TestCase):
|
|
|
165
165
|
self.assertIn(expected_msg, str(e),
|
|
166
166
|
"Wrong error for %r: %s" % (doc, e))
|
|
167
167
|
|
|
168
|
+
def test_trailing_comma_position(self):
|
|
169
|
+
# The reported position is the comma itself, not the closing
|
|
170
|
+
# bracket and not whatever whitespace happens to precede it.
|
|
171
|
+
# Same positions as the stdlib json module. Runs on both the C
|
|
172
|
+
# and the pure-Python scanner.
|
|
173
|
+
test_cases = [
|
|
174
|
+
('[42,]', 3),
|
|
175
|
+
('[42, ]', 3),
|
|
176
|
+
('[42,\n]', 3),
|
|
177
|
+
('[123 , ]', 6),
|
|
178
|
+
('{"spam":42,}', 10),
|
|
179
|
+
('{"spam":42 , }', 11),
|
|
180
|
+
('{"spam":42,\n}', 10),
|
|
181
|
+
('[[1, 2],]', 7),
|
|
182
|
+
('{"a": {"b": 1,},}', 13),
|
|
183
|
+
]
|
|
184
|
+
for doc, expected_pos in test_cases:
|
|
185
|
+
try:
|
|
186
|
+
json.loads(doc)
|
|
187
|
+
self.fail("Expected JSONDecodeError for %r" % (doc,))
|
|
188
|
+
except json.JSONDecodeError as e:
|
|
189
|
+
self.assertEqual(doc[e.pos], ',',
|
|
190
|
+
"Position %d of %r is %r, not the comma"
|
|
191
|
+
% (e.pos, doc, doc[e.pos]))
|
|
192
|
+
self.assertEqual(e.pos, expected_pos,
|
|
193
|
+
"Wrong position for %r: %d" % (doc, e.pos))
|
|
194
|
+
|
|
168
195
|
def test_nonascii_digits_rejected(self):
|
|
169
196
|
# Non-ASCII digits (e.g. fullwidth digits) must not be accepted
|
|
170
197
|
# as JSON numbers. Matches CPython gh-125687.
|
|
@@ -158,6 +158,8 @@ class TestFail(TestCase):
|
|
|
158
158
|
('[,', "Expecting value", 1),
|
|
159
159
|
('--', 'Expecting value', 0),
|
|
160
160
|
('"\x18d', "Invalid control character %r", 1),
|
|
161
|
+
# control char after content: pos must point to the control char, not start of content
|
|
162
|
+
('"d\x18"', "Invalid control character %r", 2),
|
|
161
163
|
]
|
|
162
164
|
for data, msg, idx in test_cases:
|
|
163
165
|
try:
|
|
@@ -290,3 +290,49 @@ class TestScanString(TestCase):
|
|
|
290
290
|
self.assertEqual(
|
|
291
291
|
scanstring(u'\\ud834\\udd1e"', 0, None, True),
|
|
292
292
|
(u'\U0001d11e', 13))
|
|
293
|
+
|
|
294
|
+
def test_single_char_escape_error_parity(self):
|
|
295
|
+
# Regression: for an invalid single-character escape (``\X``), the C
|
|
296
|
+
# scanstring reported the position of the backslash, while
|
|
297
|
+
# py_scanstring reports the position of the offending escape
|
|
298
|
+
# character itself. Because the "Invalid \\X escape sequence %r"
|
|
299
|
+
# message renders %r as the character at that position, the C path
|
|
300
|
+
# produced the nonsensical "Invalid \\X escape sequence '\\'" (naming
|
|
301
|
+
# the backslash, which is always a valid escape introducer) instead
|
|
302
|
+
# of naming the actual bad character. This asserts exact parity
|
|
303
|
+
# (position and rendered message) between the two backends, matching
|
|
304
|
+
# the \\uXXXX parity already covered by test_escape_error_parity.
|
|
305
|
+
if simplejson.decoder.c_scanstring is None:
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
def get_exc(scanstring, s):
|
|
309
|
+
try:
|
|
310
|
+
scanstring(s, 0, None, True)
|
|
311
|
+
except json.JSONDecodeError as e:
|
|
312
|
+
return (e.pos, str(e).split(': line ')[0])
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
# Each case: (input, escape_char_index). The rendered message must
|
|
316
|
+
# name the character at escape_char_index, never the backslash.
|
|
317
|
+
cases = [
|
|
318
|
+
(u'\\x41"', 1),
|
|
319
|
+
(u'a\\q"', 2),
|
|
320
|
+
(u'\\ "', 1),
|
|
321
|
+
(u'prefix\\z"', 7),
|
|
322
|
+
(u'\\!"', 1),
|
|
323
|
+
]
|
|
324
|
+
for s, esc_idx in cases:
|
|
325
|
+
expected = (
|
|
326
|
+
esc_idx,
|
|
327
|
+
"Invalid \\X escape sequence %r" % (s[esc_idx],))
|
|
328
|
+
py = get_exc(simplejson.decoder.py_scanstring, s)
|
|
329
|
+
c = get_exc(simplejson.decoder.c_scanstring, s)
|
|
330
|
+
self.assertEqual(py, expected,
|
|
331
|
+
'py_scanstring(%r) expected %r, got %r' %
|
|
332
|
+
(s, expected, py))
|
|
333
|
+
self.assertEqual(c, expected,
|
|
334
|
+
'c_scanstring(%r) expected %r, got %r' %
|
|
335
|
+
(s, expected, c))
|
|
336
|
+
# The reported position must land on the bad char, not the '\\'.
|
|
337
|
+
self.assertNotEqual(s[c[0]], u'\\',
|
|
338
|
+
'c_scanstring(%r) pointed at the backslash' % (s,))
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import decimal
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
from unittest import TestCase
|
|
4
|
-
from simplejson.compat import StringIO, reload_module
|
|
5
|
-
|
|
6
|
-
import simplejson as json
|
|
7
|
-
|
|
8
|
-
class TestDecimal(TestCase):
|
|
9
|
-
NUMS = "1.0", "10.00", "1.1", "1234567890.1234567890", "500"
|
|
10
|
-
def dumps(self, obj, **kw):
|
|
11
|
-
sio = StringIO()
|
|
12
|
-
json.dump(obj, sio, **kw)
|
|
13
|
-
res = json.dumps(obj, **kw)
|
|
14
|
-
self.assertEqual(res, sio.getvalue())
|
|
15
|
-
return res
|
|
16
|
-
|
|
17
|
-
def loads(self, s, **kw):
|
|
18
|
-
sio = StringIO(s)
|
|
19
|
-
res = json.loads(s, **kw)
|
|
20
|
-
self.assertEqual(res, json.load(sio, **kw))
|
|
21
|
-
return res
|
|
22
|
-
|
|
23
|
-
def test_decimal_encode(self):
|
|
24
|
-
for d in map(Decimal, self.NUMS):
|
|
25
|
-
self.assertEqual(self.dumps(d, use_decimal=True), str(d))
|
|
26
|
-
|
|
27
|
-
def test_decimal_decode(self):
|
|
28
|
-
for s in self.NUMS:
|
|
29
|
-
self.assertEqual(self.loads(s, parse_float=Decimal), Decimal(s))
|
|
30
|
-
|
|
31
|
-
def test_stringify_key(self):
|
|
32
|
-
for d in map(Decimal, self.NUMS):
|
|
33
|
-
v = {d: d}
|
|
34
|
-
self.assertEqual(
|
|
35
|
-
self.loads(
|
|
36
|
-
self.dumps(v, use_decimal=True), parse_float=Decimal),
|
|
37
|
-
{str(d): d})
|
|
38
|
-
|
|
39
|
-
def test_decimal_roundtrip(self):
|
|
40
|
-
for d in map(Decimal, self.NUMS):
|
|
41
|
-
# The type might not be the same (int and Decimal) but they
|
|
42
|
-
# should still compare equal.
|
|
43
|
-
for v in [d, [d], {'': d}]:
|
|
44
|
-
self.assertEqual(
|
|
45
|
-
self.loads(
|
|
46
|
-
self.dumps(v, use_decimal=True), parse_float=Decimal),
|
|
47
|
-
v)
|
|
48
|
-
|
|
49
|
-
def test_decimal_defaults(self):
|
|
50
|
-
d = Decimal('1.1')
|
|
51
|
-
# use_decimal=True is the default
|
|
52
|
-
self.assertRaises(TypeError, json.dumps, d, use_decimal=False)
|
|
53
|
-
self.assertEqual('1.1', json.dumps(d))
|
|
54
|
-
self.assertEqual('1.1', json.dumps(d, use_decimal=True))
|
|
55
|
-
self.assertRaises(TypeError, json.dump, d, StringIO(),
|
|
56
|
-
use_decimal=False)
|
|
57
|
-
sio = StringIO()
|
|
58
|
-
json.dump(d, sio)
|
|
59
|
-
self.assertEqual('1.1', sio.getvalue())
|
|
60
|
-
sio = StringIO()
|
|
61
|
-
json.dump(d, sio, use_decimal=True)
|
|
62
|
-
self.assertEqual('1.1', sio.getvalue())
|
|
63
|
-
|
|
64
|
-
def test_decimal_reload(self):
|
|
65
|
-
# Simulate a subinterpreter that reloads the Python modules but not
|
|
66
|
-
# the C code https://github.com/simplejson/simplejson/issues/34
|
|
67
|
-
global Decimal
|
|
68
|
-
Decimal = reload_module(decimal).Decimal
|
|
69
|
-
import simplejson.encoder
|
|
70
|
-
simplejson.encoder.Decimal = Decimal
|
|
71
|
-
self.test_decimal_roundtrip()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|