From a1b491de320d502ec65e19f9f22b89fcd5a72252 Mon Sep 17 00:00:00 2001 From: Roei Ben Artzi Date: Mon, 7 Apr 2025 07:53:10 +0300 Subject: [PATCH 1/4] Fix JSON encoder to handle indentation with `skipkeys` option in dumps --- Lib/json/encoder.py | 5 +++-- Modules/_json.c | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index b804224098e14f..ba68ace5a0257f 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -348,7 +348,6 @@ def _iterencode_dict(dct, _current_indent_level): _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 @@ -381,6 +380,8 @@ def _iterencode_dict(dct, _current_indent_level): 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) @@ -413,7 +414,7 @@ def _iterencode_dict(dct, _current_indent_level): except BaseException as exc: exc.add_note(f'when serializing {type(dct).__name__} item {key!r}') raise - 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/Modules/_json.c b/Modules/_json.c index cd8e697916226b..4aefd98708adc1 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1616,6 +1616,12 @@ encoder_encode_key_value(PyEncoderObject *s, PyUnicodeWriter *writer, bool *firs if (*first) { *first = false; + if (s->indent != Py_None) { + if (write_newline_indent(writer, indent_level, indent_cache) < 0) { + Py_DECREF(keystr); + return -1; + } + } } else { if (PyUnicodeWriter_WriteStr(writer, item_separator) < 0) { @@ -1683,11 +1689,8 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, if (s->indent != Py_None) { indent_level++; separator = get_item_separator(s, indent_level, indent_cache); - if (separator == NULL || - write_newline_indent(writer, indent_level, indent_cache) < 0) - { + if (separator == NULL) goto bail; - } } if (s->sort_keys || !PyDict_CheckExact(dct)) { @@ -1727,7 +1730,7 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer, goto bail; Py_CLEAR(ident); } - if (s->indent != Py_None) { + if (s->indent != Py_None && !first) { indent_level--; if (write_newline_indent(writer, indent_level, indent_cache) < 0) { goto bail; From b7cd0acd6161d3a5a6cd106cd7212e271132dcdc Mon Sep 17 00:00:00 2001 From: Roei Ben Artzi Date: Mon, 7 Apr 2025 07:58:15 +0300 Subject: [PATCH 2/4] Add tests --- Lib/test/test_json/test_dump.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py index 13b40020781bae..39470754003bb6 100644 --- a/Lib/test/test_json/test_dump.py +++ b/Lib/test/test_json/test_dump.py @@ -22,6 +22,14 @@ def test_dump_skipkeys(self): 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), From d6b12d42bd0cd9fcd543477ee8df3776a6dd0a92 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 06:41:55 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst 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 00000000000000..59e0d68ca38139 --- /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 `json.dumps()` when both `indent` and `skipkeys=True` are used. From 2756a1416454832ae36865708ed8c0e732c49bc8 Mon Sep 17 00:00:00 2001 From: Roei Ben Artzi Date: Mon, 7 Apr 2025 09:48:07 +0300 Subject: [PATCH 4/4] fix news entry --- .../next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 59e0d68ca38139..d9e2eae02dce1f 100644 --- 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 @@ -1 +1 @@ -Fix formatting issues in `json.dumps()` when both `indent` and `skipkeys=True` are used. +Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* are used.