https://github.com/python/cpython/commit/ca064d9b9999657332d8c73b7b4079ce4311ccec
commit: ca064d9b9999657332d8c73b7b4079ce4311ccec
branch: main
author: Michiel W. Beijen <[email protected]>
committer: encukou <[email protected]>
date: 2026-04-15T14:21:43+02:00
summary:

gh-117716: Fix wave RIFF padding for data chunks (GH-145237)

wave.Wave_write now writes the required RIFF pad byte when the data chunk
size is odd.

Update RIFF chunk size calculations in both header writing and header
patching so they include the alignment pad byte when present.

Add a regression test in test_wave.py that verifies
odd-sized writes are padded, RIFF size is correct, and roundtrip reads
preserve frame data.

files:
A Misc/NEWS.d/next/Library/2026-02-25-22-20-00.gh-issue-117716.w6kYp9.rst
M Lib/test/test_wave.py
M Lib/wave.py

diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py
index 86886ac768f6d2..d3723c04820d9d 100644
--- a/Lib/test/test_wave.py
+++ b/Lib/test/test_wave.py
@@ -430,6 +430,30 @@ def test_setframerate_rounds(self, arg, expected):
             f.setframerate(arg)
             self.assertEqual(f.getframerate(), expected)
 
+    def test_write_odd_data_chunk_pads_and_updates_riff_size(self):
+        # gh-117716: odd-sized data chunks must be padded with one zero byte.
+        with io.BytesIO() as output:
+            with wave.open(output, mode='wb') as w:
+                w.setnchannels(1)
+                w.setsampwidth(1)
+                w.setframerate(48000)
+                w.writeframes(b'\x80')
+
+            value = output.getvalue()
+
+        self.assertEqual(value[-1], 0)
+        self.assertEqual(
+            int.from_bytes(value[4:8], byteorder='little'),
+            38,
+        )
+
+        with wave.open(io.BytesIO(value), mode='rb') as r:
+            self.assertEqual(r.getnchannels(), 1)
+            self.assertEqual(r.getsampwidth(), 1)
+            self.assertEqual(r.getframerate(), 48000)
+            self.assertEqual(r.getnframes(), 1)
+            self.assertEqual(r.readframes(-1), b'\x80')
+
 
 class WaveOpen(unittest.TestCase):
     def test_open_pathlike(self):
diff --git a/Lib/wave.py b/Lib/wave.py
index 6e84c107b9fb20..c4e1a493a7ec7f 100644
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -611,6 +611,8 @@ def close(self):
         try:
             if self._file:
                 self._ensure_header_written(0)
+                if self._datawritten & 1:
+                    self._file.write(b'\x00')
                 if self._datalength != self._datawritten:
                     self._patchheader()
                 self._file.flush()
@@ -651,7 +653,7 @@ def _write_header(self, initlength):
         has_fact = self._needs_fact_chunk()
         header_overhead = 36 + (12 if has_fact else 0)
         self._file.write(struct.pack('<L4s4sLHHLLHH',
-            header_overhead + self._datalength, b'WAVE', b'fmt ', 16,
+            header_overhead + self._datalength + (self._datalength & 1), 
b'WAVE', b'fmt ', 16,
             self._format, self._nchannels, self._framerate,
             self._nchannels * self._framerate * self._sampwidth,
             self._nchannels * self._sampwidth,
@@ -677,7 +679,7 @@ def _patchheader(self):
         curpos = self._file.tell()
         header_overhead = 36 + (12 if self._needs_fact_chunk() else 0)
         self._file.seek(self._form_length_pos, 0)
-        self._file.write(struct.pack('<L', header_overhead + 
self._datawritten))
+        self._file.write(struct.pack('<L', header_overhead + self._datawritten 
+ (self._datawritten & 1)))
         if self._fact_sample_count_pos is not None:
             self._file.seek(self._fact_sample_count_pos, 0)
             nframes = self._datawritten // (self._nchannels * self._sampwidth)
diff --git 
a/Misc/NEWS.d/next/Library/2026-02-25-22-20-00.gh-issue-117716.w6kYp9.rst 
b/Misc/NEWS.d/next/Library/2026-02-25-22-20-00.gh-issue-117716.w6kYp9.rst
new file mode 100644
index 00000000000000..77182a3f2c3303
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-02-25-22-20-00.gh-issue-117716.w6kYp9.rst
@@ -0,0 +1 @@
+Fix :mod:`wave` writing of odd-sized ``data`` chunks by appending the required 
RIFF pad byte and correcting the RIFF chunk size field accordingly.

_______________________________________________
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