From f4f3f190f5cf86a9bd1c471d31c7058782403836 Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Thu, 10 Jul 2025 23:26:03 -0400 Subject: [PATCH 1/7] gh-136523: fix Wave_write.__del__ raise after wb open --- Lib/test/support/os_helper.py | 20 ++++++++++++++++++++ Lib/test/test_wave.py | 16 ++++++++++++++++ Lib/wave.py | 12 ++++++------ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 2c45fe2369ec36..4fcaf1aa54fd4e 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -169,6 +169,26 @@ def make_bad_fd(): unlink(TESTFN) +@contextlib.contextmanager +def unwritable_filepath(): + """ + Create a filepath that is not writable by the current user. + """ + import tempfile + fd, path = tempfile.mkstemp() + original_permissions = stat.S_IMODE(os.lstat(path).st_mode) + os.close(fd) + + try: + os.chmod(path, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) + yield path + finally: + try: + os.chmod(path, original_permissions) + os.remove(path) + except OSError as e: + pass + _can_symlink = None diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index 6c3362857fc2ba..f0e0b17ed376be 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,6 +1,7 @@ import unittest from test import audiotests from test import support +from test.support.os_helper import unwritable_filepath import io import struct import sys @@ -196,6 +197,21 @@ def test_read_wrong_sample_width(self): with self.assertRaisesRegex(wave.Error, 'bad sample width'): wave.open(io.BytesIO(b)) + def test_write_to_protected_file(self): + # gh-136523: Wave_write.__del__ should not throw + stderr = io.StringIO() + sys.stderr = stderr + try: + try: + with unwritable_filepath() as path: + with wave.open(path, "wb"): + pass + except PermissionError: + pass + self.assertEqual(stderr.getvalue(), "") + finally: + sys.stderr = sys.__stderr__ + if __name__ == '__main__': unittest.main() diff --git a/Lib/wave.py b/Lib/wave.py index 929609fa52409d..3ed1a697489811 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -429,15 +429,15 @@ class Wave_write: def __init__(self, f): self._i_opened_the_file = None - if isinstance(f, str): - f = builtins.open(f, 'wb') - self._i_opened_the_file = f try: - self.initfp(f) + if isinstance(f, str): + f = builtins.open(f, 'wb') + self._i_opened_the_file = f except: - if self._i_opened_the_file: - f.close() + f = None raise + finally: + self.initfp(f) def initfp(self, file): self._file = file From d4a598624ce4c68cacdb1be5f336fe14707a2a6b Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 03:39:16 +0000 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst diff --git a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst new file mode 100644 index 00000000000000..3e1267b2666826 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst @@ -0,0 +1 @@ +Fix :func:`wave.Wave_write.__del__` raising :exc:`AttributeError` when attempting to open an unwritable file. From 66e64b655d898c4b8b3d759a8858603f1ff4c7f9 Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Fri, 11 Jul 2025 00:52:42 -0400 Subject: [PATCH 3/7] skip test when no chmod (wasi), fix doc lint --- Lib/test/test_wave.py | 3 ++- .../Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index f0e0b17ed376be..d2611c294d4f9c 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,7 +1,7 @@ import unittest from test import audiotests from test import support -from test.support.os_helper import unwritable_filepath +from test.support.os_helper import unwritable_filepath, skip_unless_working_chmod import io import struct import sys @@ -197,6 +197,7 @@ def test_read_wrong_sample_width(self): with self.assertRaisesRegex(wave.Error, 'bad sample width'): wave.open(io.BytesIO(b)) + @skip_unless_working_chmod def test_write_to_protected_file(self): # gh-136523: Wave_write.__del__ should not throw stderr = io.StringIO() diff --git a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst index 3e1267b2666826..9bf4a75e5c779b 100644 --- a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst +++ b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst @@ -1 +1 @@ -Fix :func:`wave.Wave_write.__del__` raising :exc:`AttributeError` when attempting to open an unwritable file. +Fix ``wave.Wave_write.__del__`` raising :exc:`AttributeError` when attempting to open an unwritable file. From 288f5278ae3a8aea569f0e333f9849cb5d2dfeee Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Sat, 12 Jul 2025 14:41:22 -0400 Subject: [PATCH 4/7] simplify patch, fix test robustness to running env --- Lib/test/support/os_helper.py | 20 ------------------ Lib/test/test_wave.py | 21 +++++++------------ Lib/wave.py | 14 +++++++------ ...-07-11-03-39-15.gh-issue-136523.s7caKL.rst | 2 +- 4 files changed, 17 insertions(+), 40 deletions(-) diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 4fcaf1aa54fd4e..2c45fe2369ec36 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -169,26 +169,6 @@ def make_bad_fd(): unlink(TESTFN) -@contextlib.contextmanager -def unwritable_filepath(): - """ - Create a filepath that is not writable by the current user. - """ - import tempfile - fd, path = tempfile.mkstemp() - original_permissions = stat.S_IMODE(os.lstat(path).st_mode) - os.close(fd) - - try: - os.chmod(path, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) - yield path - finally: - try: - os.chmod(path, original_permissions) - os.remove(path) - except OSError as e: - pass - _can_symlink = None diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index d2611c294d4f9c..a8b9cf379d9b19 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,7 +1,7 @@ import unittest from test import audiotests from test import support -from test.support.os_helper import unwritable_filepath, skip_unless_working_chmod +from test.support import os_helper import io import struct import sys @@ -197,21 +197,16 @@ def test_read_wrong_sample_width(self): with self.assertRaisesRegex(wave.Error, 'bad sample width'): wave.open(io.BytesIO(b)) - @skip_unless_working_chmod - def test_write_to_protected_file(self): + def test_write_to_protected_location(self): # gh-136523: Wave_write.__del__ should not throw - stderr = io.StringIO() - sys.stderr = stderr - try: + with support.catch_unraisable_exception() as cm: try: - with unwritable_filepath() as path: - with wave.open(path, "wb"): - pass - except PermissionError: + with os_helper.temp_dir() as path: + wave.open(path, "wb") + except IsADirectoryError: pass - self.assertEqual(stderr.getvalue(), "") - finally: - sys.stderr = sys.__stderr__ + support.gc_collect() + self.assertIsNone(cm.unraisable) if __name__ == '__main__': diff --git a/Lib/wave.py b/Lib/wave.py index 3ed1a697489811..5af745e2217ec3 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -427,17 +427,19 @@ class Wave_write: _datawritten -- the size of the audio samples actually written """ + _file = None + def __init__(self, f): self._i_opened_the_file = None + if isinstance(f, str): + f = builtins.open(f, 'wb') + self._i_opened_the_file = f try: - if isinstance(f, str): - f = builtins.open(f, 'wb') - self._i_opened_the_file = f + self.initfp(f) except: - f = None + if self._i_opened_the_file: + f.close() raise - finally: - self.initfp(f) def initfp(self, file): self._file = file diff --git a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst index 9bf4a75e5c779b..7b87b74ffc398c 100644 --- a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst +++ b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst @@ -1 +1 @@ -Fix ``wave.Wave_write.__del__`` raising :exc:`AttributeError` when attempting to open an unwritable file. +Fix :class:`wave.Wave_write` emitting an unraisable :exc:`AttributeError` when attempting to open an unwritable file. From c33b9080c5a95f53e5d5763b5c361b6d3ff44133 Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:12:13 -0400 Subject: [PATCH 5/7] fix test on Windows by catching OSError --- Lib/test/test_wave.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index a8b9cf379d9b19..c9d62dd3eb57ea 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -2,6 +2,7 @@ from test import audiotests from test import support from test.support import os_helper +import contextlib import io import struct import sys @@ -200,11 +201,9 @@ def test_read_wrong_sample_width(self): def test_write_to_protected_location(self): # gh-136523: Wave_write.__del__ should not throw with support.catch_unraisable_exception() as cm: - try: + with contextlib.suppress(OSError): with os_helper.temp_dir() as path: wave.open(path, "wb") - except IsADirectoryError: - pass support.gc_collect() self.assertIsNone(cm.unraisable) From 78ac6b2e6d87cbb2203f52969219133e60ae5cb9 Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:40:37 -0400 Subject: [PATCH 6/7] test updates: assert open raises, use curdir, rename --- Lib/test/test_wave.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index c9d62dd3eb57ea..226b1aa84bd73c 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,9 +1,8 @@ import unittest from test import audiotests from test import support -from test.support import os_helper -import contextlib import io +import os import struct import sys import wave @@ -198,12 +197,11 @@ def test_read_wrong_sample_width(self): with self.assertRaisesRegex(wave.Error, 'bad sample width'): wave.open(io.BytesIO(b)) - def test_write_to_protected_location(self): + def test_open_in_write_raises(self): # gh-136523: Wave_write.__del__ should not throw with support.catch_unraisable_exception() as cm: - with contextlib.suppress(OSError): - with os_helper.temp_dir() as path: - wave.open(path, "wb") + with self.assertRaises(OSError): + wave.open(os.curdir, "wb") support.gc_collect() self.assertIsNone(cm.unraisable) From 9c10b37090967be084250d05a4cb269cc8529e41 Mon Sep 17 00:00:00 2001 From: inventshah <39803835+inventshah@users.noreply.github.com> Date: Sat, 12 Jul 2025 16:01:47 -0400 Subject: [PATCH 7/7] update NEWS --- .../next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst index 7b87b74ffc398c..71ec66a37ef4c3 100644 --- a/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst +++ b/Misc/NEWS.d/next/Library/2025-07-11-03-39-15.gh-issue-136523.s7caKL.rst @@ -1 +1 @@ -Fix :class:`wave.Wave_write` emitting an unraisable :exc:`AttributeError` when attempting to open an unwritable file. +Fix :class:`wave.Wave_write` emitting an unraisable when open raises. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy