From e2a9a3fa98e45c9cf821a403a77281ee06d34e3c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 5 Jun 2025 17:38:11 +0300 Subject: [PATCH] [3.13] gh-131884: Fix incorrect formatting in json.dumps() when using indent and skipkeys=True (GH-132200) (GH-135061) (cherry picked from commit ec12559ebafca01ded22c9013de64abe535c838d) Co-authored-by: Roei Ben Artzi <155478676+roeibenartzi@users.noreply.github.com> --- Lib/json/encoder.py | 5 +++-- Lib/test/test_json/test_dump.py | 8 ++++++++ ...2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst | 1 + Modules/_json.c | 15 ++++++++++----- 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index bb5f584e3226..08ef39d15928 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -345,7 +345,6 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, _current_indent_level += 1 newline_indent = '\n' + _indent * _current_indent_level item_separator = _item_separator + newline_indent - yield newline_indent else: newline_indent = None item_separator = _item_separator @@ -378,6 +377,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, f'not {key.__class__.__name__}') if first: first = False + if newline_indent is not None: + yield newline_indent else: yield item_separator yield _encoder(key) @@ -404,7 +405,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, else: chunks = _iterencode(value, _current_indent_level) yield from chunks - if newline_indent is not None: + if not first and newline_indent is not None: _current_indent_level -= 1 yield '\n' + _indent * _current_indent_level yield '}' diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index 13b40020781b..39470754003b 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -22,6 +22,14 @@ class TestDump: self.assertIn('valid_key', o) self.assertNotIn(b'invalid_key', o) + def test_dump_skipkeys_indent_empty(self): + v = {b'invalid_key': False} + self.assertEqual(self.json.dumps(v, skipkeys=True, indent=4), '{}') + + def test_skipkeys_indent(self): + v = {b'invalid_key': False, 'valid_key': True} + self.assertEqual(self.json.dumps(v, skipkeys=True, indent=4), '{\n "valid_key": true\n}') + def test_encode_truefalse(self): self.assertEqual(self.dumps( {True: False, False: True}, sort_keys=True), diff --git a/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst b/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst new file mode 100644 index 000000000000..d9e2eae02dce --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst @@ -0,0 +1 @@ +Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* are used. diff --git a/Modules/_json.c b/Modules/_json.c index e33ef1f5eea9..cc41b9ab867a 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1514,6 +1514,12 @@ encoder_encode_key_value(PyEncoderObject *s, _PyUnicodeWriter *writer, bool *fir if (*first) { *first = false; + if (s->indent != Py_None) { + if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { + Py_DECREF(keystr); + return -1; + } + } } else { if (_PyUnicodeWriter_WriteStr(writer, item_separator) < 0) { @@ -1586,9 +1592,6 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, } // update item separator with a borrowed reference current_item_separator = separator_indent; - if (_PyUnicodeWriter_WriteStr(writer, new_newline_indent) < 0) { - goto bail; - } } if (s->sort_keys || !PyDict_CheckExact(dct)) { @@ -1632,8 +1635,10 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer, Py_CLEAR(new_newline_indent); Py_CLEAR(separator_indent); - if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { - goto bail; + if (!first) { + if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { + goto bail; + } } } -- 2.47.3