https://github.com/python/cpython/commit/e2a9a3fa98e45c9cf821a403a77281ee06d34e3c
commit: e2a9a3fa98e45c9cf821a403a77281ee06d34e3c
branch: 3.13
author: Serhiy Storchaka <storch...@gmail.com>
committer: serhiy-storchaka <storch...@gmail.com>
date: 2025-06-05T14:38:11Z
summary:

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

(cherry picked from commit ec12559ebafca01ded22c9013de64abe535c838d)

Co-authored-by: Roei Ben Artzi <155478676+roeibenar...@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2025-04-07-06-41-54.gh-issue-131884.ym9BJN.rst
M Lib/json/encoder.py
M Lib/test/test_json/test_dump.py
M Modules/_json.c

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;
+            }
         }
     }
 

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to