]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-117716: Fix wave RIFF padding for data chunks (GH-145237)
authorMichiel W. Beijen <mb@x14.nl>
Wed, 15 Apr 2026 12:21:43 +0000 (14:21 +0200)
committerGitHub <noreply@github.com>
Wed, 15 Apr 2026 12:21:43 +0000 (14:21 +0200)
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.

Lib/test/test_wave.py
Lib/wave.py
Misc/NEWS.d/next/Library/2026-02-25-22-20-00.gh-issue-117716.w6kYp9.rst [new file with mode: 0644]

index 86886ac768f6d24407f31cb1baa7c39e6a0416f8..d3723c04820d9d4ad81c09c952b27034c6f781bf 100644 (file)
@@ -430,6 +430,30 @@ class WaveLowLevelTest(unittest.TestCase):
             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):
index 6e84c107b9fb20fb5419f4946effe1bc8fe998a6..c4e1a493a7ec7f57403ab6387e09fe5b05dc7696 100644 (file)
@@ -611,6 +611,8 @@ class Wave_write:
         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 @@ class Wave_write:
         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 @@ class Wave_write:
         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 (file)
index 0000000..77182a3
--- /dev/null
@@ -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.