Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-98306: Support JSON encoding of NaNs and infinities as null #115246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Prev Previous commit
Next Next commit
Change 'null' to 'as_null'
  • Loading branch information
mdickinson committed May 12, 2024
commit 79ce75e59e4251657ebfb1dce5bd2e94c595e1e3
38 changes: 19 additions & 19 deletions Doc/library/json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ Basic Usage
If *allow_nan* is false (default: ``True``), then it will be a
:exc:`ValueError` to serialize out of range :class:`float` values (``nan``,
``inf``, ``-inf``) in strict compliance with the JSON specification. If
*allow_nan* is the string ``'null'``, NaNs and infinities will be converted
to a JSON ``null``, matching the behavior of JavaScript's
``JSON.stringify``. If *allow_nan* is true but not equal to ``'null'`` then
NaNs and infinities are converted to non-quote-delimited strings ``NaN``,
``Infinity`` and ``-Infinity`` in the JSON output. Note that this represents
an extension of the JSON specification, and is not compliant with standard
JSON.
*allow_nan* is the string ``'as_null'``, NaNs and infinities will be
converted to a JSON ``null``, matching the behavior of JavaScript's
``JSON.stringify``. If *allow_nan* is true but not equal to ``'as_null'``
then NaNs and infinities are converted to non-quote-delimited strings
``NaN``, ``Infinity`` and ``-Infinity`` in the JSON output. Note that this
represents an extension of the JSON specification, and is not compliant with
standard JSON.

If *indent* is a non-negative integer or string, then JSON array elements and
object members will be pretty-printed with that indent level. An indent level
Expand Down Expand Up @@ -215,8 +215,8 @@ Basic Usage
All optional parameters are now :ref:`keyword-only <keyword-only_parameter>`.

.. versionchanged:: 3.14
Added support for ``allow_nan='null'``. Passing any string value
other than ``'null'`` for ``allow_nan`` now raises a
Added support for ``allow_nan='as_null'``. Passing any string value
other than ``'as_null'`` for ``allow_nan`` now raises a
:warning:`DeprecationWarning`.

.. note::
Expand Down Expand Up @@ -461,15 +461,15 @@ Encoders and Decoders
prevent an infinite recursion (which would cause a :exc:`RecursionError`).
Otherwise, no such check takes place.

If *allow_nan* is the string ``'null'``, then NaNs and infinities are
If *allow_nan* is the string ``'as_null'``, then NaNs and infinities are
encoded as JSON ``null`` values. This matches the behavior of JavaScript's
``JSON.stringify``. If *allow_nan* is true but not equal to ``'null'``, then
``NaN``, ``Infinity``, and ``-Infinity`` will be encoded as corresponding
non-quote-delimited strings in the JSON output. This is the default
behavior. This behavior represents an extension of the JSON specification,
but is consistent with some JavaScript based encoders and decoders (as well
as Python's own decoder). If *allow_nan* is false, it will be a
:exc:`ValueError` to encode such floats.
``JSON.stringify``. If *allow_nan* is true but not equal to ``'as_null'``,
then ``NaN``, ``Infinity``, and ``-Infinity`` will be encoded as
corresponding non-quote-delimited strings in the JSON output. This is the
default behavior. This behavior represents an extension of the JSON
specification, but is consistent with some JavaScript based encoders and
decoders (as well as Python's own decoder). If *allow_nan* is false, it
will be a :exc:`ValueError` to encode such floats.

If *sort_keys* is true (default: ``False``), then the output of dictionaries
will be sorted by key; this is useful for regression tests to ensure that
Expand Down Expand Up @@ -502,8 +502,8 @@ Encoders and Decoders
All parameters are now :ref:`keyword-only <keyword-only_parameter>`.

.. versionchanged:: 3.14
Added support for ``allow_nan='null'``. Passing any string value
other than ``'null'`` for ``allow_nan`` now raises a
Added support for ``allow_nan='as_null'``. Passing any string value
other than ``'as_null'`` for *allow_nan* now raises a
:warning:`DeprecationWarning`.

.. method:: default(o)
Expand Down
8 changes: 0 additions & 8 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -761,14 +761,6 @@ itertools
than the specified batch size.
(Contributed by Raymond Hettinger in :gh:`113202`.)

json
----

* Add support for ``allow_nan='null'`` when encoding to JSON. This converts
floating-point infinities and NaNs to a JSON ``null``, for compatibility
with ECMAScript's ``JSON.stringify``.
(Contributed by Mark Dickinson in :gh:`115246`.)

marshal
-------

Expand Down
9 changes: 8 additions & 1 deletion Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ New Modules
Improved Modules
================

json
----

* Add support for ``allow_nan='as_null'`` when encoding to JSON. This converts
floating-point infinities and NaNs to a JSON ``null``, for compatibility
with ECMAScript's ``JSON.stringify``.
(Contributed by Mark Dickinson in :gh:`115246`.)


Optimizations
=============
Expand Down Expand Up @@ -181,4 +189,3 @@ Deprecated

Removed
-------

1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(arguments)
STRUCT_FOR_ID(argv)
STRUCT_FOR_ID(as_integer_ratio)
STRUCT_FOR_ID(as_null)
STRUCT_FOR_ID(asend)
STRUCT_FOR_ID(ast)
STRUCT_FOR_ID(athrow)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True,
self.ensure_ascii = ensure_ascii
self.check_circular = check_circular

if isinstance(allow_nan, str) and allow_nan != "null":
if isinstance(allow_nan, str) and allow_nan != 'as_null':
warnings.warn(
"in the future, allow_nan will no longer accept strings "
"other than 'null'. Use a boolean instead.",
"other than 'as_null'. Use a boolean instead.",
DeprecationWarning,
stacklevel=3,
)
Expand Down Expand Up @@ -246,7 +246,7 @@ def floatstr(o, allow_nan=self.allow_nan,
else:
return _repr(o)

if allow_nan == 'null':
if allow_nan == 'as_null':
return 'null'

elif not allow_nan:
Expand Down
10 changes: 5 additions & 5 deletions Lib/test/test_json/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,29 @@ def test_allow_nan(self):
self.assertRaisesRegex(ValueError, msg, self.dumps, [val], allow_nan=False)

def test_allow_nan_null(self):
# when allow_nan is "null", infinities and NaNs are converted to "null"
# when allow_nan is 'as_null', infinities and NaNs convert to 'null'
for val in [float('inf'), float('-inf'), float('nan')]:
with self.subTest(val=val):
out = self.dumps([val], allow_nan="null")
out = self.dumps([val], allow_nan='as_null')
res = self.loads(out)
self.assertEqual(res, [None])

# and finite values are treated as normal
for val in [1.25, -23, -0.0, 0.0]:
with self.subTest(val=val):
out = self.dumps([val], allow_nan="null")
out = self.dumps([val], allow_nan='as_null')
res = self.loads(out)
self.assertEqual(res, [val])

# testing a mixture
vals = [-1.3, 1e100, -math.inf, 1234, -0.0, math.nan]
out = self.dumps(vals, allow_nan="null")
out = self.dumps(vals, allow_nan='as_null')
res = self.loads(out)
self.assertEqual(res, [-1.3, 1e100, None, 1234, -0.0, None])

def test_allow_nan_string_deprecation(self):
with self.assertWarns(DeprecationWarning):
self.dumps(2.3, allow_nan="true")
self.dumps(2.3, allow_nan='true')

def test_allow_nan_non_boolean(self):
# check that exception gets propagated as expected
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Add support for ``allow_nan='null'`` when encoding an object to a JSON
Add support for ``allow_nan='as_null'`` when encoding an object to a JSON
string. This converts floating-point infinities and NaNs to a JSON ``null``.
3 changes: 2 additions & 1 deletion Modules/_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,8 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
// 0 to disallow nans and infinities
// 1 to convert nans and infinities into corresponding JSON strings
// 2 to convert nans and infinities to a JSON null
if (PyUnicode_Check(allow_nan_obj) && _PyUnicode_Equal(allow_nan_obj, &_Py_ID(null))) {
if (PyUnicode_Check(allow_nan_obj) &&
_PyUnicode_Equal(allow_nan_obj, &_Py_ID(as_null))) {
allow_nan = 2;
} else {
allow_nan = PyObject_IsTrue(allow_nan_obj);
Expand Down
Loading