From ff287a72de43e7fc6517aba9049cb23470e000b2 Mon Sep 17 00:00:00 2001 From: "Michiel W. Beijen" Date: Tue, 17 Mar 2026 18:01:18 +0100 Subject: [PATCH] gh-144975: Fix wave.Wave_write.setframerate() validation order (GH-144976) Validate the frame rate after rounding to an integer, not before. This prevents values like 0.5 from passing validation (0.5 > 0) but then rounding to 0, which would cause a confusing delayed error "sampling rate not specified" when writing frames. With this fix, setframerate(0.5) immediately raises "bad frame rate", providing clear feedback at the point of the error. --- Lib/test/test_wave.py | 37 +++++++++++++++++++ Lib/wave.py | 3 +- ...-02-18-21-45-00.gh-issue-144975.Ab3XyZ.rst | 3 ++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-18-21-45-00.gh-issue-144975.Ab3XyZ.rst diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index 4c21f1655377..05e7d3775177 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -207,6 +207,43 @@ class WaveLowLevelTest(unittest.TestCase): support.gc_collect() self.assertIsNone(cm.unraisable) + @support.subTests('arg', ( + # rounds to 0, should raise: + 0.5, + 0.4, + # Negative values should still raise: + -1, + -0.5, + -0.4, + # 0 should raise: + 0, + )) + def test_setframerate_validates_rounded_values(self, arg): + """Test that setframerate that round to 0 or negative are rejected""" + with wave.open(io.BytesIO(), 'wb') as f: + f.setnchannels(1) + f.setsampwidth(2) + with self.assertRaises(wave.Error): + f.setframerate(arg) + with self.assertRaises(wave.Error): + f.close() + + @support.subTests(('arg', 'expected'), ( + (1.4, 1), + (1.5, 2), + (1.6, 2), + (44100.4, 44100), + (44100.5, 44100), + (44100.6, 44101), + )) + def test_setframerate_rounds(self, arg, expected): + """Test that setframerate is rounded""" + with wave.open(io.BytesIO(), 'wb') as f: + f.setnchannels(1) + f.setsampwidth(2) + f.setframerate(arg) + self.assertEqual(f.getframerate(), expected) + class WaveOpen(unittest.TestCase): def test_open_pathlike(self): diff --git a/Lib/wave.py b/Lib/wave.py index 25ca9ef168e8..841da8c49e9a 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -493,9 +493,10 @@ class Wave_write: def setframerate(self, framerate): if self._datawritten: raise Error('cannot change parameters after starting to write') + framerate = int(round(framerate)) if framerate <= 0: raise Error('bad frame rate') - self._framerate = int(round(framerate)) + self._framerate = framerate def getframerate(self): if not self._framerate: diff --git a/Misc/NEWS.d/next/Library/2026-02-18-21-45-00.gh-issue-144975.Ab3XyZ.rst b/Misc/NEWS.d/next/Library/2026-02-18-21-45-00.gh-issue-144975.Ab3XyZ.rst new file mode 100644 index 000000000000..37658064c2c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-18-21-45-00.gh-issue-144975.Ab3XyZ.rst @@ -0,0 +1,3 @@ +:meth:`wave.Wave_write.setframerate` now validates the frame rate after +rounding to an integer, preventing values like ``0.5`` from being accepted +and causing confusing errors later. Patch by Michiel Beijen. -- 2.47.3