diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index bb5f584e32266a..08ef39d159284d 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -345,7 +345,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 @@ -378,6 +377,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) @@ -404,7 +405,7 @@ def _iterencode_dict(dct, _current_indent_level): 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 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), 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..d9e2eae02dce1f --- /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 e33ef1f5eea92f..cc41b9ab867a28 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; + } } }