https://github.com/python/cpython/commit/5f28aa2f372339ba0c70373b96d33ec4d2879e04
commit: 5f28aa2f372339ba0c70373b96d33ec4d2879e04
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-01-12T10:40:06+02:00
summary:

gh-143346: Fix calculation of the line width for wrapped Base64 in plistlib 
(GH-143347)

It was incorrect in case of mixed tabs and spaces in indentation.

files:
A Misc/NEWS.d/next/Library/2026-01-02-12-55-52.gh-issue-143346.iTekce.rst
M Lib/plistlib.py
M Lib/test/test_plistlib.py

diff --git a/Lib/plistlib.py b/Lib/plistlib.py
index 655c51eea3da5d..5b2b4e42c95a83 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -384,7 +384,7 @@ def write_bytes(self, data):
         self._indent_level -= 1
         maxlinelength = max(
             16,
-            76 - len(self.indent.replace(b"\t", b" " * 8) * 
self._indent_level))
+            76 - len((self.indent * self._indent_level).expandtabs()))
 
         for line in _encode_base64(data, maxlinelength).split(b"\n"):
             if line:
diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
index de2a2fd1fc34bf..d9216be4d95658 100644
--- a/Lib/test/test_plistlib.py
+++ b/Lib/test/test_plistlib.py
@@ -509,6 +509,69 @@ def test_bytes(self):
         data2 = plistlib.dumps(pl2)
         self.assertEqual(data, data2)
 
+    def test_bytes_indent(self):
+        header = (
+            b'<?xml version="1.0" encoding="UTF-8"?>\n'
+            b'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd";>\n'
+            b'<plist version="1.0">\n')
+        data = [{'bytes': bytes(range(50))}]
+        pl = plistlib.dumps(data)
+        self.assertEqual(pl, header +
+            b'<array>\n'
+            b'\t<dict>\n'
+            b'\t\t<key>bytes</key>\n'
+            b'\t\t<data>\n'
+            
b'\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss\n'
+            b'\t\tLS4vMDE=\n'
+            b'\t\t</data>\n'
+            b'\t</dict>\n'
+            b'</array>\n'
+            b'</plist>\n')
+
+        def dumps_with_indent(data, indent):
+            fp = BytesIO()
+            writer = plistlib._PlistWriter(fp, indent=indent)
+            writer.write(data)
+            return fp.getvalue()
+
+        pl = dumps_with_indent(data, b' ')
+        self.assertEqual(pl, header +
+            b'<array>\n'
+            b' <dict>\n'
+            b'  <key>bytes</key>\n'
+            b'  <data>\n'
+            b'  
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDE=\n'
+            b'  </data>\n'
+            b' </dict>\n'
+            b'</array>\n'
+            b'</plist>\n')
+
+        pl = dumps_with_indent(data, b' \t')
+        self.assertEqual(pl, header +
+            b'<array>\n'
+            b' \t<dict>\n'
+            b' \t \t<key>bytes</key>\n'
+            b' \t \t<data>\n'
+            b' \t 
\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss\n'
+            b' \t \tLS4vMDE=\n'
+            b' \t \t</data>\n'
+            b' \t</dict>\n'
+            b'</array>\n'
+            b'</plist>\n')
+
+        pl = dumps_with_indent(data, b'\t   ')
+        self.assertEqual(pl, header +
+            b'<array>\n'
+            b'\t   <dict>\n'
+            b'\t   \t   <key>bytes</key>\n'
+            b'\t   \t   <data>\n'
+            b'\t   \t   
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygp\n'
+            b'\t   \t   KissLS4vMDE=\n'
+            b'\t   \t   </data>\n'
+            b'\t   </dict>\n'
+            b'</array>\n'
+            b'</plist>\n')
+
     def test_loads_str_with_xml_fmt(self):
         pl = self._create()
         b = plistlib.dumps(pl)
@@ -581,7 +644,6 @@ def test_appleformatting(self):
                 self.assertEqual(data, TESTDATA[fmt],
                     "generated data was not identical to Apple's output")
 
-
     def test_appleformattingfromliteral(self):
         self.maxDiff = None
         for fmt in ALL_FORMATS:
diff --git 
a/Misc/NEWS.d/next/Library/2026-01-02-12-55-52.gh-issue-143346.iTekce.rst 
b/Misc/NEWS.d/next/Library/2026-01-02-12-55-52.gh-issue-143346.iTekce.rst
new file mode 100644
index 00000000000000..93c45eefe373d3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-02-12-55-52.gh-issue-143346.iTekce.rst
@@ -0,0 +1,2 @@
+Fix incorrect wrapping of the Base64 data in :class:`!plistlib._PlistWriter`
+when the indent contains a mix of tabs and spaces.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to