Skip to content

gh-131884: Fix incorrect formatting in json.dumps() when using indent and skipkeys=True #132200

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 '}'
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_json/test_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix formatting issues in :func:`json.dump` when both *indent* and *skipkeys* are used.
13 changes: 8 additions & 5 deletions Modules/_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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;
Expand Down
Loading