simplejson 4.1.1__tar.gz → 4.2.0__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.1 → simplejson-4.2.0}/CHANGES.txt +35 -0
- {simplejson-4.1.1/simplejson.egg-info → simplejson-4.2.0}/PKG-INFO +5 -5
- {simplejson-4.1.1 → simplejson-4.2.0}/README.rst +2 -2
- {simplejson-4.1.1 → simplejson-4.2.0}/conf.py +3 -3
- {simplejson-4.1.1 → simplejson-4.2.0}/index.rst +5 -1
- {simplejson-4.1.1 → simplejson-4.2.0}/setup.py +3 -3
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/__init__.py +2 -2
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/_speedups.c +189 -51
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/_speedups_scan.h +11 -3
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/decoder.py +5 -3
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/encoder.py +59 -15
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_bitsize_int_as_string.py +97 -0
- simplejson-4.2.0/simplejson/tests/test_custom_method_cycles.py +71 -0
- simplejson-4.2.0/simplejson/tests/test_decimal.py +133 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_decode.py +27 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_fail.py +28 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_scanstring.py +47 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_speedups.py +1 -8
- {simplejson-4.1.1 → simplejson-4.2.0/simplejson.egg-info}/PKG-INFO +5 -5
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson.egg-info/SOURCES.txt +1 -0
- simplejson-4.1.1/simplejson/tests/test_decimal.py +0 -71
- {simplejson-4.1.1 → simplejson-4.2.0}/LICENSE.txt +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/MANIFEST.in +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/pyproject.toml +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/scripts/make_docs.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/setup.cfg +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/compat.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/errors.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/ordered_dict.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/raw_json.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/scanner.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/__init__.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/_cibw_runner.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/_helpers.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_bigint_as_string.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_check_circular.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_default.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_dump.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_encode_basestring_ascii.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_encode_for_html.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_errors.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_float.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_for_json.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_free_threading.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_indent.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_item_sort_key.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_iterable.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_namedtuple.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_pass1.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_pass2.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_pass3.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_raw_json.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_recursion.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_separators.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_str_subclass.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_subclass.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_subinterpreters.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_tool.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_tuple.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tests/test_unicode.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson/tool.py +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson.egg-info/dependency_links.txt +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/simplejson.egg-info/top_level.txt +0 -0
- {simplejson-4.1.1 → simplejson-4.2.0}/tsan_stress_simplejson.py +0 -0
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
Version 4.2.0 released 2026-10-02
|
|
2
|
+
|
|
3
|
+
* Start building wheels for Python 3.15 and drop Python 3.8 support
|
|
4
|
+
via upgrade to cibuildwheel v4.2.0
|
|
5
|
+
https://github.com/simplejson/simplejson/pull/385
|
|
6
|
+
* Build and test riscv64 Linux wheels on native RISE runners
|
|
7
|
+
https://github.com/simplejson/simplejson/pull/389
|
|
8
|
+
* The C scanner now reports the same error message as the pure Python
|
|
9
|
+
decoder for an invalid first key in an object
|
|
10
|
+
https://github.com/simplejson/simplejson/pull/388
|
|
11
|
+
* ``int_as_string_bitcount`` now supports thresholds of 64 bits and above
|
|
12
|
+
(including 128-bit integers) in both the C and pure Python encoders;
|
|
13
|
+
the C encoder previously rejected them
|
|
14
|
+
https://github.com/simplejson/simplejson/pull/387
|
|
15
|
+
* Circular references introduced through ``for_json()`` or ``_asdict()``
|
|
16
|
+
now raise ``ValueError("Circular reference detected")`` instead of
|
|
17
|
+
recursing without bound
|
|
18
|
+
https://github.com/simplejson/simplejson/pull/390
|
|
19
|
+
* CI now tests Python 3.15 (including free-threaded 3.15t), PyPy 3.12,
|
|
20
|
+
CPython 3.14.8 debug builds, and uses current GitHub Actions and
|
|
21
|
+
cibuildwheel v4.2.1
|
|
22
|
+
https://github.com/simplejson/simplejson/pull/386
|
|
23
|
+
|
|
24
|
+
Version 4.1.2 released 2026-08-26
|
|
25
|
+
|
|
26
|
+
* Fix control character error position when content precedes the control char
|
|
27
|
+
https://github.com/simplejson/simplejson/pull/379
|
|
28
|
+
* Report the offending char, not the backslash, for invalid \X escapes
|
|
29
|
+
https://github.com/simplejson/simplejson/pull/380
|
|
30
|
+
* Handle non-finite Decimals like floats in the encoder
|
|
31
|
+
https://github.com/simplejson/simplejson/pull/381
|
|
32
|
+
* Parse failures due to trailing commas are now reported consistently
|
|
33
|
+
across implementations
|
|
34
|
+
https://github.com/simplejson/simplejson/pull/382
|
|
35
|
+
|
|
1
36
|
Version 4.1.1 released 2026-04-24
|
|
2
37
|
|
|
3
38
|
* The ``build_wheels_py27`` CI job now also builds Python 2.7 wheels
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplejson
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Simple, fast, extensible JSON encoder/decoder for Python
|
|
5
5
|
Home-page: https://github.com/simplejson/simplejson
|
|
6
6
|
Author: Bob Ippolito
|
|
@@ -14,18 +14,18 @@ Classifier: Programming Language :: Python
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 2
|
|
15
15
|
Classifier: Programming Language :: Python :: 2.7
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
24
24
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
25
|
Classifier: Programming Language :: Python :: Implementation :: GraalPy
|
|
26
26
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
27
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
-
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*
|
|
28
|
+
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
|
|
29
29
|
License-File: LICENSE.txt
|
|
30
30
|
Dynamic: author
|
|
31
31
|
Dynamic: author-email
|
|
@@ -42,7 +42,7 @@ simplejson
|
|
|
42
42
|
----------
|
|
43
43
|
|
|
44
44
|
simplejson is a simple, fast, complete, correct and extensible
|
|
45
|
-
JSON <http://json.org> encoder and decoder for Python 3.
|
|
45
|
+
JSON <http://json.org> encoder and decoder for Python 3.9+
|
|
46
46
|
with legacy support for Python 2.7. It is pure Python code
|
|
47
47
|
with no dependencies, but includes an optional C extension
|
|
48
48
|
for a serious speed boost.
|
|
@@ -53,7 +53,7 @@ https://simplejson.readthedocs.io/
|
|
|
53
53
|
simplejson is the externally maintained development version of the
|
|
54
54
|
json library included with Python (since 2.6). This version is tested
|
|
55
55
|
with Python 3.14 (including free-threaded builds) and maintains
|
|
56
|
-
backwards compatibility with Python 3.
|
|
56
|
+
backwards compatibility with Python 3.9+. A legacy Python 2.7 wheel
|
|
57
57
|
is also published.
|
|
58
58
|
|
|
59
59
|
The encoder can be specialized to provide serialization in any kind of
|
|
@@ -2,7 +2,7 @@ simplejson
|
|
|
2
2
|
----------
|
|
3
3
|
|
|
4
4
|
simplejson is a simple, fast, complete, correct and extensible
|
|
5
|
-
JSON <http://json.org> encoder and decoder for Python 3.
|
|
5
|
+
JSON <http://json.org> encoder and decoder for Python 3.9+
|
|
6
6
|
with legacy support for Python 2.7. It is pure Python code
|
|
7
7
|
with no dependencies, but includes an optional C extension
|
|
8
8
|
for a serious speed boost.
|
|
@@ -13,7 +13,7 @@ https://simplejson.readthedocs.io/
|
|
|
13
13
|
simplejson is the externally maintained development version of the
|
|
14
14
|
json library included with Python (since 2.6). This version is tested
|
|
15
15
|
with Python 3.14 (including free-threaded builds) and maintains
|
|
16
|
-
backwards compatibility with Python 3.
|
|
16
|
+
backwards compatibility with Python 3.9+. A legacy Python 2.7 wheel
|
|
17
17
|
is also published.
|
|
18
18
|
|
|
19
19
|
The encoder can be specialized to provide serialization in any kind of
|
|
@@ -36,15 +36,15 @@ 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.
|
|
43
43
|
#
|
|
44
44
|
# The short X.Y version.
|
|
45
|
-
version = '4.
|
|
45
|
+
version = '4.2'
|
|
46
46
|
# The full version, including alpha/beta/rc tags.
|
|
47
|
-
release = '4.
|
|
47
|
+
release = '4.2.0'
|
|
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:
|
|
@@ -21,7 +21,7 @@ for a serious speed boost.
|
|
|
21
21
|
:mod:`simplejson` exposes an API familiar to users of the standard library
|
|
22
22
|
:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
|
|
23
23
|
version of the :mod:`json` library, but maintains
|
|
24
|
-
compatibility with Python 3.
|
|
24
|
+
compatibility with Python 3.9 through 3.15 (including free-threaded
|
|
25
25
|
builds) as well as legacy Python 2.7.
|
|
26
26
|
|
|
27
27
|
Development of simplejson happens on Github:
|
|
@@ -310,6 +310,10 @@ Basic Usage
|
|
|
310
310
|
:class:`int` ``2**n`` and higher or lower than ``-2**n`` will be encoded as strings. This is to
|
|
311
311
|
avoid the rounding that happens in Javascript otherwise. Note that this
|
|
312
312
|
option loses type information, so use with extreme caution.
|
|
313
|
+
Thresholds of 64 bits and above are supported by both encoders, including
|
|
314
|
+
128-bit integers. Very large thresholds do not allocate a power-of-two
|
|
315
|
+
boundary.
|
|
316
|
+
|
|
313
317
|
See also *bigint_as_string* (which is equivalent to `int_as_string_bitcount=53`).
|
|
314
318
|
|
|
315
319
|
.. note::
|
|
@@ -12,13 +12,13 @@ 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.
|
|
15
|
+
VERSION = '4.2.0'
|
|
16
16
|
DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python"
|
|
17
17
|
|
|
18
18
|
with open('README.rst', 'r') as f:
|
|
19
19
|
LONG_DESCRIPTION = f.read()
|
|
20
20
|
|
|
21
|
-
PYTHON_REQUIRES = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*'
|
|
21
|
+
PYTHON_REQUIRES = '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*'
|
|
22
22
|
|
|
23
23
|
CLASSIFIERS = [
|
|
24
24
|
'Development Status :: 5 - Production/Stable',
|
|
@@ -28,13 +28,13 @@ CLASSIFIERS = [
|
|
|
28
28
|
'Programming Language :: Python :: 2',
|
|
29
29
|
'Programming Language :: Python :: 2.7',
|
|
30
30
|
'Programming Language :: Python :: 3',
|
|
31
|
-
'Programming Language :: Python :: 3.8',
|
|
32
31
|
'Programming Language :: Python :: 3.9',
|
|
33
32
|
'Programming Language :: Python :: 3.10',
|
|
34
33
|
'Programming Language :: Python :: 3.11',
|
|
35
34
|
'Programming Language :: Python :: 3.12',
|
|
36
35
|
'Programming Language :: Python :: 3.13',
|
|
37
36
|
'Programming Language :: Python :: 3.14',
|
|
37
|
+
'Programming Language :: Python :: 3.15',
|
|
38
38
|
'Programming Language :: Python :: Implementation :: CPython',
|
|
39
39
|
'Programming Language :: Python :: Implementation :: GraalPy',
|
|
40
40
|
'Programming Language :: Python :: Implementation :: PyPy',
|
|
@@ -5,7 +5,7 @@ interchange format.
|
|
|
5
5
|
:mod:`simplejson` exposes an API familiar to users of the standard library
|
|
6
6
|
:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
|
|
7
7
|
version of the :mod:`json` library contained in Python 2.6+, supporting
|
|
8
|
-
Python 2.7 and Python 3.
|
|
8
|
+
Python 2.7 and Python 3.9+, and has significant performance advantages,
|
|
9
9
|
even without using the optional C extension for speedups.
|
|
10
10
|
|
|
11
11
|
Encoding basic Python object hierarchies::
|
|
@@ -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.
|
|
121
|
+
__version__ = '4.2.0'
|
|
122
122
|
__all__ = [
|
|
123
123
|
'dump', 'dumps', 'load', 'loads',
|
|
124
124
|
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
|
|
@@ -301,6 +301,7 @@ typedef struct _PyEncoderObject {
|
|
|
301
301
|
int iterable_as_array;
|
|
302
302
|
PyObject *max_long_size;
|
|
303
303
|
PyObject *min_long_size;
|
|
304
|
+
PyObject *large_int_bitcount;
|
|
304
305
|
PyObject *item_sort_key;
|
|
305
306
|
PyObject *item_sort_kw;
|
|
306
307
|
int for_json;
|
|
@@ -327,6 +328,7 @@ typedef struct _PyEncoderObject {
|
|
|
327
328
|
X(item_sort_key) \
|
|
328
329
|
X(max_long_size) \
|
|
329
330
|
X(min_long_size) \
|
|
331
|
+
X(large_int_bitcount) \
|
|
330
332
|
X(Decimal)
|
|
331
333
|
|
|
332
334
|
static PyMemberDef encoder_members[] = {
|
|
@@ -429,6 +431,8 @@ static PyObject *
|
|
|
429
431
|
encoder_long_to_str(PyObject *obj);
|
|
430
432
|
static PyObject *
|
|
431
433
|
encoder_encode_float(PyEncoderObject *s, PyObject *obj);
|
|
434
|
+
static PyObject *
|
|
435
|
+
encoder_encode_decimal(PyEncoderObject *s, PyObject *obj);
|
|
432
436
|
static int
|
|
433
437
|
encoder_accumulate_newline_indent(PyEncoderObject *s, _speedups_state *state,
|
|
434
438
|
JSON_Accu *rval, Py_ssize_t indent_level);
|
|
@@ -706,22 +710,46 @@ maybe_quote_bigint(PyEncoderObject* s, PyObject *encoded, PyObject *obj)
|
|
|
706
710
|
int ge, le;
|
|
707
711
|
PyObject *quoted;
|
|
708
712
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
713
|
+
if (s->large_int_bitcount != NULL) {
|
|
714
|
+
size_t nbits;
|
|
715
|
+
PyObject *bits;
|
|
716
|
+
nbits = _PyLong_NumBits(obj);
|
|
717
|
+
if (nbits == (size_t)-1 && PyErr_Occurred()) {
|
|
718
|
+
Py_DECREF(encoded);
|
|
719
|
+
return NULL;
|
|
720
|
+
}
|
|
721
|
+
bits = PyLong_FromSize_t(nbits);
|
|
722
|
+
if (bits == NULL) {
|
|
723
|
+
Py_DECREF(encoded);
|
|
724
|
+
return NULL;
|
|
725
|
+
}
|
|
726
|
+
ge = PyObject_RichCompareBool(bits, s->large_int_bitcount, Py_GT);
|
|
727
|
+
Py_DECREF(bits);
|
|
728
|
+
if (ge < 0) {
|
|
729
|
+
Py_DECREF(encoded);
|
|
730
|
+
return NULL;
|
|
731
|
+
}
|
|
732
|
+
if (!ge)
|
|
733
|
+
return encoded;
|
|
717
734
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
735
|
+
else {
|
|
736
|
+
/* int_as_string_bitcount is not set: fast path, return as-is. */
|
|
737
|
+
if (s->max_long_size == Py_None || s->min_long_size == Py_None)
|
|
738
|
+
return encoded;
|
|
739
|
+
|
|
740
|
+
ge = PyObject_RichCompareBool(obj, s->max_long_size, Py_GE);
|
|
741
|
+
if (ge < 0) {
|
|
742
|
+
Py_DECREF(encoded);
|
|
743
|
+
return NULL;
|
|
744
|
+
}
|
|
745
|
+
le = PyObject_RichCompareBool(obj, s->min_long_size, Py_LE);
|
|
746
|
+
if (le < 0) {
|
|
747
|
+
Py_DECREF(encoded);
|
|
748
|
+
return NULL;
|
|
749
|
+
}
|
|
750
|
+
if (!(ge || le))
|
|
751
|
+
return encoded;
|
|
722
752
|
}
|
|
723
|
-
if (!(ge || le))
|
|
724
|
-
return encoded;
|
|
725
753
|
|
|
726
754
|
#if PY_MAJOR_VERSION >= 3
|
|
727
755
|
quoted = PyUnicode_FromFormat("\"%U\"", encoded);
|
|
@@ -1062,7 +1090,7 @@ encoder_stringify_key(PyEncoderObject *s, PyObject *key)
|
|
|
1062
1090
|
return encoder_long_to_str(key);
|
|
1063
1091
|
}
|
|
1064
1092
|
else if (s->use_decimal && PyObject_TypeCheck(key, (PyTypeObject *)s->Decimal)) {
|
|
1065
|
-
return
|
|
1093
|
+
return encoder_encode_decimal(s, key);
|
|
1066
1094
|
}
|
|
1067
1095
|
if (s->skipkeys) {
|
|
1068
1096
|
Py_INCREF(Py_None);
|
|
@@ -1365,7 +1393,7 @@ scanstring_str(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1365
1393
|
default: c = 0;
|
|
1366
1394
|
}
|
|
1367
1395
|
if (c == 0) {
|
|
1368
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1396
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1369
1397
|
goto bail;
|
|
1370
1398
|
}
|
|
1371
1399
|
}
|
|
@@ -1564,7 +1592,7 @@ scanstring_unicode(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1564
1592
|
default: c = 0;
|
|
1565
1593
|
}
|
|
1566
1594
|
if (c == 0) {
|
|
1567
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1595
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1568
1596
|
goto bail;
|
|
1569
1597
|
}
|
|
1570
1598
|
}
|
|
@@ -1755,7 +1783,7 @@ scanstring_unicode(_speedups_state *state, PyObject *pystr, Py_ssize_t end,
|
|
|
1755
1783
|
default: c = 0;
|
|
1756
1784
|
}
|
|
1757
1785
|
if (c == 0) {
|
|
1758
|
-
raise_errmsg(state, ERR_STRING_ESC1, pystr, end -
|
|
1786
|
+
raise_errmsg(state, ERR_STRING_ESC1, pystr, end - 1);
|
|
1759
1787
|
goto bail;
|
|
1760
1788
|
}
|
|
1761
1789
|
}
|
|
@@ -2651,10 +2679,11 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
|
2651
2679
|
goto bail;
|
|
2652
2680
|
if (PyInt_Check(int_as_string_bitcount) || PyLong_Check(int_as_string_bitcount)) {
|
|
2653
2681
|
static const unsigned long long_long_bitsize = sizeof(long long) * CHAR_BIT;
|
|
2654
|
-
|
|
2682
|
+
int overflow = 0;
|
|
2683
|
+
long int_as_string_bitcount_val = PyLong_AsLongAndOverflow(int_as_string_bitcount, &overflow);
|
|
2655
2684
|
if (int_as_string_bitcount_val == -1 && PyErr_Occurred())
|
|
2656
2685
|
goto bail;
|
|
2657
|
-
if (int_as_string_bitcount_val > 0 && int_as_string_bitcount_val < (long)long_long_bitsize) {
|
|
2686
|
+
if (!overflow && int_as_string_bitcount_val > 0 && int_as_string_bitcount_val < (long)long_long_bitsize) {
|
|
2658
2687
|
int n = (int)int_as_string_bitcount_val;
|
|
2659
2688
|
/* Compute 2^n as unsigned (well-defined for n < 64) and
|
|
2660
2689
|
* -(2^n) as signed without UB. Naive "-1LL << n" is a
|
|
@@ -2670,10 +2699,19 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
|
2670
2699
|
goto bail;
|
|
2671
2700
|
}
|
|
2672
2701
|
}
|
|
2702
|
+
else if (overflow > 0 || int_as_string_bitcount_val > 0) {
|
|
2703
|
+
/* Compare magnitudes without allocating a 2**n boundary. */
|
|
2704
|
+
s->large_int_bitcount = PyNumber_Long(int_as_string_bitcount);
|
|
2705
|
+
if (s->large_int_bitcount == NULL)
|
|
2706
|
+
goto bail;
|
|
2707
|
+
Py_INCREF(Py_None);
|
|
2708
|
+
s->max_long_size = Py_None;
|
|
2709
|
+
Py_INCREF(Py_None);
|
|
2710
|
+
s->min_long_size = Py_None;
|
|
2711
|
+
}
|
|
2673
2712
|
else {
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
int_as_string_bitcount_val, long_long_bitsize);
|
|
2713
|
+
PyErr_SetString(PyExc_TypeError,
|
|
2714
|
+
"int_as_string_bitcount must be a positive integer");
|
|
2677
2715
|
goto bail;
|
|
2678
2716
|
}
|
|
2679
2717
|
}
|
|
@@ -2785,33 +2823,38 @@ _encoded_const(_speedups_state *state, PyObject *obj)
|
|
|
2785
2823
|
}
|
|
2786
2824
|
}
|
|
2787
2825
|
|
|
2826
|
+
static PyObject *
|
|
2827
|
+
encoder_encode_nonfinite(PyEncoderObject *s, int sign)
|
|
2828
|
+
{
|
|
2829
|
+
_speedups_state *state = get_speedups_state(s->module_ref);
|
|
2830
|
+
|
|
2831
|
+
if (!s->allow_or_ignore_nan) {
|
|
2832
|
+
PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant");
|
|
2833
|
+
return NULL;
|
|
2834
|
+
}
|
|
2835
|
+
if (s->allow_or_ignore_nan & JSON_IGNORE_NAN) {
|
|
2836
|
+
return _encoded_const(state, Py_None);
|
|
2837
|
+
}
|
|
2838
|
+
/* JSON_ALLOW_NAN is set */
|
|
2839
|
+
if (sign > 0) {
|
|
2840
|
+
Py_INCREF(state->JSON_Infinity);
|
|
2841
|
+
return state->JSON_Infinity;
|
|
2842
|
+
}
|
|
2843
|
+
if (sign < 0) {
|
|
2844
|
+
Py_INCREF(state->JSON_NegInfinity);
|
|
2845
|
+
return state->JSON_NegInfinity;
|
|
2846
|
+
}
|
|
2847
|
+
Py_INCREF(state->JSON_NaN);
|
|
2848
|
+
return state->JSON_NaN;
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2788
2851
|
static PyObject *
|
|
2789
2852
|
encoder_encode_float(PyEncoderObject *s, PyObject *obj)
|
|
2790
2853
|
{
|
|
2791
2854
|
/* Return the JSON representation of a PyFloat */
|
|
2792
|
-
_speedups_state *state = get_speedups_state(s->module_ref);
|
|
2793
2855
|
double i = PyFloat_AS_DOUBLE(obj);
|
|
2794
2856
|
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
|
-
}
|
|
2857
|
+
return encoder_encode_nonfinite(s, i > 0 ? 1 : (i < 0 ? -1 : 0));
|
|
2815
2858
|
}
|
|
2816
2859
|
/* Use a better float format here? */
|
|
2817
2860
|
if (PyFloat_CheckExact(obj)) {
|
|
@@ -2830,6 +2873,61 @@ encoder_encode_float(PyEncoderObject *s, PyObject *obj)
|
|
|
2830
2873
|
}
|
|
2831
2874
|
}
|
|
2832
2875
|
|
|
2876
|
+
static PyObject *
|
|
2877
|
+
encoder_encode_decimal(PyEncoderObject *s, PyObject *obj)
|
|
2878
|
+
{
|
|
2879
|
+
/* Return the JSON representation of a Decimal. Finite Decimals are
|
|
2880
|
+
stringified as-is (preserving precision and trailing zeros); non-finite
|
|
2881
|
+
Decimals (NaN, sNaN, Infinity) are routed through the same
|
|
2882
|
+
allow_nan/ignore_nan handling as floats so they never emit invalid
|
|
2883
|
+
JSON. See https://github.com/simplejson/simplejson/issues/149
|
|
2884
|
+
|
|
2885
|
+
str(Decimal) carries a leading '-' sign at most (never '+'); a finite
|
|
2886
|
+
value's first significant character is always a digit while a non-finite
|
|
2887
|
+
value's is a letter ('N'/'s' for [s]NaN, 'I' for Infinity). So a single
|
|
2888
|
+
str() plus a one/two character check distinguishes the two. */
|
|
2889
|
+
PyObject *str = PyObject_Str(obj);
|
|
2890
|
+
Py_ssize_t len;
|
|
2891
|
+
int negative;
|
|
2892
|
+
JSON_UNICHR c;
|
|
2893
|
+
#if PY_MAJOR_VERSION >= 3
|
|
2894
|
+
int kind;
|
|
2895
|
+
void *data;
|
|
2896
|
+
#else
|
|
2897
|
+
const char *data;
|
|
2898
|
+
#endif
|
|
2899
|
+
if (str == NULL)
|
|
2900
|
+
return NULL;
|
|
2901
|
+
#if PY_MAJOR_VERSION >= 3
|
|
2902
|
+
if (PyUnicode_READY(str)) {
|
|
2903
|
+
Py_DECREF(str);
|
|
2904
|
+
return NULL;
|
|
2905
|
+
}
|
|
2906
|
+
len = PyUnicode_GET_LENGTH(str);
|
|
2907
|
+
kind = PyUnicode_KIND(str);
|
|
2908
|
+
data = PyUnicode_DATA(str);
|
|
2909
|
+
negative = (len > 0 && PyUnicode_READ(kind, data, 0) == '-');
|
|
2910
|
+
c = (JSON_UNICHR)0;
|
|
2911
|
+
if (len > (negative ? 1 : 0))
|
|
2912
|
+
c = PyUnicode_READ(kind, data, negative ? 1 : 0);
|
|
2913
|
+
#else
|
|
2914
|
+
len = PyString_GET_SIZE(str);
|
|
2915
|
+
data = PyString_AS_STRING(str);
|
|
2916
|
+
negative = (len > 0 && data[0] == '-');
|
|
2917
|
+
c = (JSON_UNICHR)0;
|
|
2918
|
+
if (len > (negative ? 1 : 0))
|
|
2919
|
+
c = (unsigned char)data[negative ? 1 : 0];
|
|
2920
|
+
#endif
|
|
2921
|
+
if (IS_DIGIT(c)) {
|
|
2922
|
+
/* First significant character is a digit: finite Decimal, emit as-is. */
|
|
2923
|
+
return str;
|
|
2924
|
+
}
|
|
2925
|
+
/* Non-finite Decimal; use the same allow_nan/ignore_nan path as floats
|
|
2926
|
+
without creating a temporary float. */
|
|
2927
|
+
Py_DECREF(str);
|
|
2928
|
+
return encoder_encode_nonfinite(s, c == 'I' ? (negative ? -1 : 1) : 0);
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2833
2931
|
static PyObject *
|
|
2834
2932
|
encoder_encode_string(PyEncoderObject *s, PyObject *obj)
|
|
2835
2933
|
{
|
|
@@ -2917,7 +3015,8 @@ encoder_markers_pop(PyEncoderObject *s, PyObject *ident)
|
|
|
2917
3015
|
|
|
2918
3016
|
/* Helper for the for_json / _asdict paths in encoder_listencode_obj.
|
|
2919
3017
|
* Steals the reference to `newobj` (returned by _call_json_method),
|
|
2920
|
-
*
|
|
3018
|
+
* tracks the original object's circular-reference marker and recursion
|
|
3019
|
+
* depth, and dispatches to the right
|
|
2921
3020
|
* sub-encoder:
|
|
2922
3021
|
* - if as_dict is 0, encodes newobj as a generic JSON value via
|
|
2923
3022
|
* encoder_listencode_obj (for_json contract: return any JSON-
|
|
@@ -2928,14 +3027,20 @@ encoder_markers_pop(PyEncoderObject *s, PyObject *ident)
|
|
|
2928
3027
|
* Cleans up on every exit path. */
|
|
2929
3028
|
static int
|
|
2930
3029
|
encoder_steal_encode(PyEncoderObject *s, JSON_Accu *rval,
|
|
2931
|
-
PyObject *newobj, Py_ssize_t indent_level,
|
|
3030
|
+
PyObject *obj, PyObject *newobj, Py_ssize_t indent_level,
|
|
2932
3031
|
int as_dict)
|
|
2933
3032
|
{
|
|
2934
3033
|
int rv;
|
|
3034
|
+
PyObject *ident = NULL;
|
|
2935
3035
|
if (newobj == NULL)
|
|
2936
3036
|
return -1;
|
|
3037
|
+
if (encoder_markers_push(s, obj, &ident)) {
|
|
3038
|
+
Py_DECREF(newobj);
|
|
3039
|
+
return -1;
|
|
3040
|
+
}
|
|
2937
3041
|
if (Py_EnterRecursiveCall(" while encoding a JSON object")) {
|
|
2938
3042
|
Py_DECREF(newobj);
|
|
3043
|
+
Py_XDECREF(ident);
|
|
2939
3044
|
return -1;
|
|
2940
3045
|
}
|
|
2941
3046
|
if (as_dict) {
|
|
@@ -2952,6 +3057,12 @@ encoder_steal_encode(PyEncoderObject *s, JSON_Accu *rval,
|
|
|
2952
3057
|
}
|
|
2953
3058
|
Py_DECREF(newobj);
|
|
2954
3059
|
Py_LeaveRecursiveCall();
|
|
3060
|
+
if (rv == 0) {
|
|
3061
|
+
if (encoder_markers_pop(s, ident) < 0)
|
|
3062
|
+
rv = -1;
|
|
3063
|
+
} else {
|
|
3064
|
+
Py_XDECREF(ident);
|
|
3065
|
+
}
|
|
2955
3066
|
return rv;
|
|
2956
3067
|
}
|
|
2957
3068
|
|
|
@@ -3059,12 +3170,39 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
|
|
|
3059
3170
|
rv = _steal_accumulate(state, rval, cstr);
|
|
3060
3171
|
}
|
|
3061
3172
|
else if (PyInt_Check(obj) || PyLong_Check(obj)) {
|
|
3062
|
-
PyObject *encoded
|
|
3173
|
+
PyObject *integer, *encoded;
|
|
3174
|
+
/* Large thresholds inspect the same normalized value we serialize. */
|
|
3175
|
+
if (s->large_int_bitcount != NULL) {
|
|
3176
|
+
#if PY_MAJOR_VERSION < 3
|
|
3177
|
+
/* Match int(value) in the Python encoder before widening for
|
|
3178
|
+
* _PyLong_NumBits: long(value) would invoke __long__ instead. */
|
|
3179
|
+
PyObject *normalized = PyNumber_Int(obj);
|
|
3180
|
+
if (normalized == NULL)
|
|
3181
|
+
return -1;
|
|
3182
|
+
/* PyNumber_Int may retain a long subclass. Copy its payload
|
|
3183
|
+
* instead of invoking a second conversion hook. */
|
|
3184
|
+
if (PyInt_Check(normalized))
|
|
3185
|
+
integer = PyLong_FromLong(PyInt_AS_LONG(normalized));
|
|
3186
|
+
else
|
|
3187
|
+
integer = PyLong_Type.tp_as_number->nb_long(normalized);
|
|
3188
|
+
Py_DECREF(normalized);
|
|
3189
|
+
#else
|
|
3190
|
+
integer = PyNumber_Long(obj);
|
|
3191
|
+
#endif
|
|
3192
|
+
}
|
|
3193
|
+
else {
|
|
3194
|
+
integer = obj;
|
|
3195
|
+
Py_INCREF(integer);
|
|
3196
|
+
}
|
|
3197
|
+
if (integer == NULL)
|
|
3198
|
+
return -1;
|
|
3199
|
+
encoded = encoder_long_to_str(integer);
|
|
3063
3200
|
if (encoded != NULL) {
|
|
3064
|
-
encoded = maybe_quote_bigint(s, encoded,
|
|
3201
|
+
encoded = maybe_quote_bigint(s, encoded, integer);
|
|
3065
3202
|
if (encoded != NULL)
|
|
3066
3203
|
rv = _steal_accumulate(state, rval, encoded);
|
|
3067
3204
|
}
|
|
3205
|
+
Py_DECREF(integer);
|
|
3068
3206
|
}
|
|
3069
3207
|
else if (PyFloat_Check(obj)) {
|
|
3070
3208
|
PyObject *encoded = encoder_encode_float(s, obj);
|
|
@@ -3072,10 +3210,10 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
|
|
|
3072
3210
|
rv = _steal_accumulate(state, rval, encoded);
|
|
3073
3211
|
}
|
|
3074
3212
|
else if (s->for_json && _call_json_method(obj, state->JSON_attr_for_json, &newobj)) {
|
|
3075
|
-
rv = encoder_steal_encode(s, rval, newobj, indent_level, /*as_dict=*/0);
|
|
3213
|
+
rv = encoder_steal_encode(s, rval, obj, newobj, indent_level, /*as_dict=*/0);
|
|
3076
3214
|
}
|
|
3077
3215
|
else if (s->namedtuple_as_object && _call_json_method(obj, state->JSON_attr_asdict, &newobj)) {
|
|
3078
|
-
rv = encoder_steal_encode(s, rval, newobj, indent_level, /*as_dict=*/1);
|
|
3216
|
+
rv = encoder_steal_encode(s, rval, obj, newobj, indent_level, /*as_dict=*/1);
|
|
3079
3217
|
}
|
|
3080
3218
|
else if (PyList_Check(obj) || (s->tuple_as_array && PyTuple_Check(obj))) {
|
|
3081
3219
|
if (Py_EnterRecursiveCall(" while encoding a JSON object"))
|
|
@@ -3090,7 +3228,7 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
|
|
|
3090
3228
|
Py_LeaveRecursiveCall();
|
|
3091
3229
|
}
|
|
3092
3230
|
else if (s->use_decimal && PyObject_TypeCheck(obj, (PyTypeObject *)s->Decimal)) {
|
|
3093
|
-
PyObject *encoded =
|
|
3231
|
+
PyObject *encoded = encoder_encode_decimal(s, obj);
|
|
3094
3232
|
if (encoded != NULL)
|
|
3095
3233
|
rv = _steal_accumulate(state, rval, encoded);
|
|
3096
3234
|
}
|
|
@@ -204,12 +204,17 @@ 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
|
|
|
210
211
|
/* read key */
|
|
211
212
|
if (JSON_SCAN_READ(idx) != '"') {
|
|
212
|
-
|
|
213
|
+
/* '}' is still legal where the first key would go. */
|
|
214
|
+
raise_errmsg(state,
|
|
215
|
+
did_parse ? ERR_OBJECT_PROPERTY
|
|
216
|
+
: ERR_OBJECT_PROPERTY_FIRST,
|
|
217
|
+
pystr, idx);
|
|
213
218
|
goto bail;
|
|
214
219
|
}
|
|
215
220
|
key = JSON_SCAN_SCANSTRING_CALL(idx + 1, &next_idx);
|
|
@@ -271,6 +276,7 @@ JSON_SCAN_FN(_parse_object)(PyScannerObject *s, PyObject *pystr,
|
|
|
271
276
|
raise_errmsg(state, ERR_OBJECT_DELIMITER, pystr, idx);
|
|
272
277
|
goto bail;
|
|
273
278
|
}
|
|
279
|
+
comma_idx = idx;
|
|
274
280
|
idx++;
|
|
275
281
|
|
|
276
282
|
/* skip whitespace after , delimiter */
|
|
@@ -279,7 +285,7 @@ JSON_SCAN_FN(_parse_object)(PyScannerObject *s, PyObject *pystr,
|
|
|
279
285
|
|
|
280
286
|
/* check for trailing comma before } */
|
|
281
287
|
if (idx <= end_idx && JSON_SCAN_READ(idx) == '}') {
|
|
282
|
-
raise_errmsg(state, ERR_TRAILING_COMMA_OBJECT, pystr,
|
|
288
|
+
raise_errmsg(state, ERR_TRAILING_COMMA_OBJECT, pystr, comma_idx);
|
|
283
289
|
goto bail;
|
|
284
290
|
}
|
|
285
291
|
}
|
|
@@ -351,6 +357,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
351
357
|
/* only loop if the array is non-empty */
|
|
352
358
|
if (idx <= end_idx && JSON_SCAN_READ(idx) != ']') {
|
|
353
359
|
int trailing_delimiter = 0;
|
|
360
|
+
Py_ssize_t comma_idx = 0;
|
|
354
361
|
while (idx <= end_idx) {
|
|
355
362
|
trailing_delimiter = 0;
|
|
356
363
|
/* read any JSON term and de-tuplefy the (rval, idx) */
|
|
@@ -377,6 +384,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
377
384
|
raise_errmsg(state, ERR_ARRAY_DELIMITER, pystr, idx);
|
|
378
385
|
goto bail;
|
|
379
386
|
}
|
|
387
|
+
comma_idx = idx;
|
|
380
388
|
idx++;
|
|
381
389
|
|
|
382
390
|
/* skip whitespace after , */
|
|
@@ -385,7 +393,7 @@ JSON_SCAN_FN(_parse_array)(PyScannerObject *s, PyObject *pystr,
|
|
|
385
393
|
|
|
386
394
|
/* check for trailing comma before ] */
|
|
387
395
|
if (idx <= end_idx && JSON_SCAN_READ(idx) == ']') {
|
|
388
|
-
raise_errmsg(state, ERR_TRAILING_COMMA_ARRAY, pystr,
|
|
396
|
+
raise_errmsg(state, ERR_TRAILING_COMMA_ARRAY, pystr, comma_idx);
|
|
389
397
|
goto bail;
|
|
390
398
|
}
|
|
391
399
|
}
|