[3.13] gh-131884: Fix incorrect formatting in json.dumps() when using indent and skipkeys=True (GH-132200) (GH-135061)

(cherry picked from commit ec12559eba)

Co-authored-by: Roei Ben Artzi <155478676+roeibenartzi@users.noreply.github.com>
This commit is contained in:
Serhiy Storchaka 2025-06-05 17:38:11 +03:00 committed by GitHub
parent 5a69d4fe00
commit e2a9a3fa98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 22 additions and 7 deletions

View file

@ -345,7 +345,6 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
_current_indent_level += 1 _current_indent_level += 1
newline_indent = '\n' + _indent * _current_indent_level newline_indent = '\n' + _indent * _current_indent_level
item_separator = _item_separator + newline_indent item_separator = _item_separator + newline_indent
yield newline_indent
else: else:
newline_indent = None newline_indent = None
item_separator = _item_separator item_separator = _item_separator
@ -378,6 +377,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
f'not {key.__class__.__name__}') f'not {key.__class__.__name__}')
if first: if first:
first = False first = False
if newline_indent is not None:
yield newline_indent
else: else:
yield item_separator yield item_separator
yield _encoder(key) yield _encoder(key)
@ -404,7 +405,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
else: else:
chunks = _iterencode(value, _current_indent_level) chunks = _iterencode(value, _current_indent_level)
yield from chunks yield from chunks
if newline_indent is not None: if not first and newline_indent is not None:
_current_indent_level -= 1 _current_indent_level -= 1
yield '\n' + _indent * _current_indent_level yield '\n' + _indent * _current_indent_level
yield '}' yield '}'

View file

@ -22,6 +22,14 @@ class TestDump:
self.assertIn('valid_key', o) self.assertIn('valid_key', o)
self.assertNotIn(b'invalid_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): def test_encode_truefalse(self):
self.assertEqual(self.dumps( self.assertEqual(self.dumps(
{True: False, False: True}, sort_keys=True), {True: False, False: True}, sort_keys=True),

View file

@ -0,0 +1 @@
Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* are used.

View file

@ -1514,6 +1514,12 @@ encoder_encode_key_value(PyEncoderObject *s, _PyUnicodeWriter *writer, bool *fir
if (*first) { if (*first) {
*first = false; *first = false;
if (s->indent != Py_None) {
if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) {
Py_DECREF(keystr);
return -1;
}
}
} }
else { else {
if (_PyUnicodeWriter_WriteStr(writer, item_separator) < 0) { 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 // update item separator with a borrowed reference
current_item_separator = separator_indent; current_item_separator = separator_indent;
if (_PyUnicodeWriter_WriteStr(writer, new_newline_indent) < 0) {
goto bail;
}
} }
if (s->sort_keys || !PyDict_CheckExact(dct)) { if (s->sort_keys || !PyDict_CheckExact(dct)) {
@ -1632,10 +1635,12 @@ encoder_listencode_dict(PyEncoderObject *s, _PyUnicodeWriter *writer,
Py_CLEAR(new_newline_indent); Py_CLEAR(new_newline_indent);
Py_CLEAR(separator_indent); Py_CLEAR(separator_indent);
if (!first) {
if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) { if (_PyUnicodeWriter_WriteStr(writer, newline_indent) < 0) {
goto bail; goto bail;
} }
} }
}
if (_PyUnicodeWriter_WriteChar(writer, '}')) if (_PyUnicodeWriter_WriteChar(writer, '}'))
goto bail; goto bail;