summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2025-06-05 14:38:09 (GMT)
committerGitHub <noreply@github.com>2025-06-05 14:38:09 (GMT)
commit97822b8199c84f4900c74b0ab01ce08a13d1c99e (patch)
tree74625456f1e704b0df33f898bc23eb5da5f24e85
parente11d4a18e15e79b34473389c44ec1f54dda3a508 (diff)
downloadcpython-97822b8199c84f4900c74b0ab01ce08a13d1c99e.zip
cpython-97822b8199c84f4900c74b0ab01ce08a13d1c99e.tar.gz
cpython-97822b8199c84f4900c74b0ab01ce08a13d1c99e.tar.bz2
[3.14] gh-131884: Fix incorrect formatting in json.dumps() when using indent and skipkeys=True (GH-132200) (GH-135060)
(cherry picked from commit ec12559ebafca01ded22c9013de64abe535c838d) Co-authored-by: Roei Ben Artzi <155478676+roeibenartzi@users.noreply.github.com>
-rw-r--r--Lib/json/encoder.py5
-rw-r--r--Lib/test/test_json/test_dump.py8
-rw-r--r--Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst1
-rw-r--r--Modules/_json.c13
4 files changed, 20 insertions, 7 deletions
diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py
index 0166385..bc446e0 100644
--- a/Lib/json/encoder.py
+++ b/Lib/json/encoder.py
@@ -348,7 +348,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
@@ -381,6 +380,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)
@@ -413,7 +414,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
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/Lib/test/test_json/test_dump.py b/Lib/test/test_json/test_dump.py
index 13b4002..3947075 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 0000000..d9e2eae
--- /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 89b0a41..646bb6c 100644
--- a/Modules/_json.c
+++ b/Modules/_json.c
@@ -1609,6 +1609,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) {
@@ -1676,11 +1682,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)) {
@@ -1720,7 +1723,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;