From 8abf17d0ca759ea94d9cb792ebc3ab27380f30e1 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 10:25:34 +0000 Subject: [PATCH 01/18] gh-131461: fix ResourceWarning when writing a unwritable gzipfile --- Lib/gzip.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index c9c088783bea65..37fd7caeac0029 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -245,8 +245,18 @@ def __init__(self, filename=None, mode=None, self.fileobj = fileobj - if self.mode == WRITE: - self._write_gzip_header(compresslevel) + try: + if self.mode == WRITE: + self._write_gzip_header(compresslevel) + except BaseException: + # Avoid a ResourceWarning if the write fails, eg read-only file or KI + try: + if self.myfileobj is not None: + self.myfileobj.close() + finally: + self.fileobj = None + self.fileobj = None + raise @property def mtime(self): From 04f17eeb227dbd85646080bad6e546ffdc622260 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 14:26:21 +0000 Subject: [PATCH 02/18] remove redundant self.fileobj = None --- Lib/gzip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index 37fd7caeac0029..e650b1dea2bb18 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -255,7 +255,6 @@ def __init__(self, filename=None, mode=None, self.myfileobj.close() finally: self.fileobj = None - self.fileobj = None raise @property From 99d4a47bb1f52255927db238064763d91ac8d672 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 14:26:48 +0000 Subject: [PATCH 03/18] Update Lib/gzip.py --- Lib/gzip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index e650b1dea2bb18..5eb9a754ce9802 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -255,7 +255,6 @@ def __init__(self, filename=None, mode=None, self.myfileobj.close() finally: self.fileobj = None - raise @property def mtime(self): From 59ed5121638a5f9a58017fe88494656a807576ba Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 14:27:14 +0000 Subject: [PATCH 04/18] Update Lib/gzip.py --- Lib/gzip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index 5eb9a754ce9802..673cd1fb8a2c91 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -255,7 +255,6 @@ def __init__(self, filename=None, mode=None, self.myfileobj.close() finally: self.fileobj = None - @property def mtime(self): """Last modification time read from stream, or None""" From 2ac99bd79cfec592e64d79b6b830ef1ca44847fd Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:37:02 +0000 Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst b/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst new file mode 100644 index 00000000000000..e77ce129a6e7ef --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst @@ -0,0 +1 @@ +fix :exc:`ResourceWarning` when constructing a :class:`gzip.GzipFile` in write mode with a broken file object From 0a9c81b852f6fa50b38d458ae9919d65b89c53e0 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 14:46:57 +0000 Subject: [PATCH 06/18] remember to raise --- Lib/gzip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/gzip.py b/Lib/gzip.py index 673cd1fb8a2c91..486148c3e42fd5 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -255,6 +255,7 @@ def __init__(self, filename=None, mode=None, self.myfileobj.close() finally: self.fileobj = None + raise @property def mtime(self): """Last modification time read from stream, or None""" From cef776c4963e14effe9e79862ec4be538ce8da88 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 15:06:10 +0000 Subject: [PATCH 07/18] Update Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst Co-authored-by: Victor Stinner --- .../next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst b/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst index e77ce129a6e7ef..735b3ca429ca75 100644 --- a/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst +++ b/Misc/NEWS.d/next/Library/2025-03-19-14-36-54.gh-issue-131461.uDUmdY.rst @@ -1 +1 @@ -fix :exc:`ResourceWarning` when constructing a :class:`gzip.GzipFile` in write mode with a broken file object +Fix :exc:`ResourceWarning` when constructing a :class:`gzip.GzipFile` in write mode with a broken file object. From 49f5a19ee89f061f97bbdb37a087244eed992301 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 15:06:32 +0000 Subject: [PATCH 08/18] Update Lib/gzip.py --- Lib/gzip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/gzip.py b/Lib/gzip.py index 486148c3e42fd5..e650b1dea2bb18 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -256,6 +256,7 @@ def __init__(self, filename=None, mode=None, finally: self.fileobj = None raise + @property def mtime(self): """Last modification time read from stream, or None""" From d6276899631ac44b237d6dc9440766b2f29df523 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 15:16:45 +0000 Subject: [PATCH 09/18] Update Lib/gzip.py --- Lib/gzip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index e650b1dea2bb18..48c3774542f441 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -249,7 +249,7 @@ def __init__(self, filename=None, mode=None, if self.mode == WRITE: self._write_gzip_header(compresslevel) except BaseException: - # Avoid a ResourceWarning if the write fails, eg read-only file or KI + # Avoid a ResourceWarning if the write fails, eg read-only file or KeyboardInterrupt try: if self.myfileobj is not None: self.myfileobj.close() From c9cdbf1532090fcc9f2fdfb2f292a1c00b2137e2 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 16:31:40 +0000 Subject: [PATCH 10/18] assert no resource warning on tarfile failing to write on open --- Lib/test/test_tarfile.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 3e4cf8dd56db08..a091c90a48b139 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -19,6 +19,7 @@ from test import support from test.support import os_helper from test.support import script_helper +from test.support import warnings_helper # Check for our compression modules. try: @@ -1638,10 +1639,13 @@ def write(self, data): raise exctype f = BadFile() - with self.assertRaises(exctype): - tar = tarfile.open(tmpname, self.mode, fileobj=f, - format=tarfile.PAX_FORMAT, - pax_headers={'non': 'empty'}) + with ( + warnings_helper.check_no_resource_warning(), + self.assertRaises(exctype), + ): + tarfile.open(tmpname, self.mode, fileobj=f, + format=tarfile.PAX_FORMAT, + pax_headers={'non': 'empty'}) self.assertFalse(f.closed) def test_missing_fileobj(self): From 2217713d0c57ad6843f0ac8c2bdc28c6300a9a74 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 17:10:22 +0000 Subject: [PATCH 11/18] refactor gzip closing --- Lib/gzip.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index 48c3774542f441..e1a9ef141e01dc 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -250,11 +250,7 @@ def __init__(self, filename=None, mode=None, self._write_gzip_header(compresslevel) except BaseException: # Avoid a ResourceWarning if the write fails, eg read-only file or KeyboardInterrupt - try: - if self.myfileobj is not None: - self.myfileobj.close() - finally: - self.fileobj = None + self._close() raise @property @@ -396,11 +392,14 @@ def close(self): elif self.mode == READ: self._buffer.close() finally: - self.fileobj = None - myfileobj = self.myfileobj - if myfileobj: - self.myfileobj = None - myfileobj.close() + self._close() + + def _close(self): + self.fileobj = None + myfileobj = self.myfileobj + if myfileobj is not None: + self.myfileobj = None + myfileobj.close() def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): self._check_not_closed() From 97ca4d37332379ca0c7c9766a73a9127ecf1528d Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 17:12:38 +0000 Subject: [PATCH 12/18] Update Lib/gzip.py Co-authored-by: Victor Stinner --- Lib/gzip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index e1a9ef141e01dc..0b46e09219d702 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -248,7 +248,7 @@ def __init__(self, filename=None, mode=None, try: if self.mode == WRITE: self._write_gzip_header(compresslevel) - except BaseException: + except: # Avoid a ResourceWarning if the write fails, eg read-only file or KeyboardInterrupt self._close() raise From c9661e13ebc4dc486112f86841c564597c5b96bf Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 17:32:13 +0000 Subject: [PATCH 13/18] Update Lib/gzip.py Co-authored-by: Victor Stinner --- Lib/gzip.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index 0b46e09219d702..004b945c44c968 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -249,7 +249,8 @@ def __init__(self, filename=None, mode=None, if self.mode == WRITE: self._write_gzip_header(compresslevel) except: - # Avoid a ResourceWarning if the write fails, eg read-only file or KeyboardInterrupt + # Avoid a ResourceWarning if the write fails, + # eg read-only file or KeyboardInterrupt self._close() raise From 8e54c5bba3dec414c74ee48af2f333685636bee0 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 17:34:05 +0000 Subject: [PATCH 14/18] Update Lib/test/test_tarfile.py --- Lib/test/test_tarfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index a091c90a48b139..fcbaf854cc294f 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1640,7 +1640,7 @@ def write(self, data): f = BadFile() with ( - warnings_helper.check_no_resource_warning(), + warnings_helper.check_no_resource_warning(self), self.assertRaises(exctype), ): tarfile.open(tmpname, self.mode, fileobj=f, From 15a3058c926920d19efbfeafae120db220f36ee0 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 19 Mar 2025 17:43:52 +0000 Subject: [PATCH 15/18] fix resource warning in GzipFile if anything in the ctor fails such as zlib.compressobj --- Lib/gzip.py | 85 +++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/Lib/gzip.py b/Lib/gzip.py index 004b945c44c968..2a6eea1b3939b7 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -202,50 +202,51 @@ def __init__(self, filename=None, mode=None, raise ValueError("Invalid mode: {!r}".format(mode)) if mode and 'b' not in mode: mode += 'b' - if fileobj is None: - fileobj = self.myfileobj = builtins.open(filename, mode or 'rb') - if filename is None: - filename = getattr(fileobj, 'name', '') - if not isinstance(filename, (str, bytes)): - filename = '' - else: - filename = os.fspath(filename) - origmode = mode - if mode is None: - mode = getattr(fileobj, 'mode', 'rb') - - - if mode.startswith('r'): - self.mode = READ - raw = _GzipReader(fileobj) - self._buffer = io.BufferedReader(raw) - self.name = filename - - elif mode.startswith(('w', 'a', 'x')): - if origmode is None: - import warnings - warnings.warn( - "GzipFile was opened for writing, but this will " - "change in future Python releases. " - "Specify the mode argument for opening it for writing.", - FutureWarning, 2) - self.mode = WRITE - self._init_write(filename) - self.compress = zlib.compressobj(compresslevel, - zlib.DEFLATED, - -zlib.MAX_WBITS, - zlib.DEF_MEM_LEVEL, - 0) - self._write_mtime = mtime - self._buffer_size = _WRITE_BUFFER_SIZE - self._buffer = io.BufferedWriter(_WriteBufferStream(self), - buffer_size=self._buffer_size) - else: - raise ValueError("Invalid mode: {!r}".format(mode)) - - self.fileobj = fileobj try: + if fileobj is None: + fileobj = self.myfileobj = builtins.open(filename, mode or 'rb') + if filename is None: + filename = getattr(fileobj, 'name', '') + if not isinstance(filename, (str, bytes)): + filename = '' + else: + filename = os.fspath(filename) + origmode = mode + if mode is None: + mode = getattr(fileobj, 'mode', 'rb') + + + if mode.startswith('r'): + self.mode = READ + raw = _GzipReader(fileobj) + self._buffer = io.BufferedReader(raw) + self.name = filename + + elif mode.startswith(('w', 'a', 'x')): + if origmode is None: + import warnings + warnings.warn( + "GzipFile was opened for writing, but this will " + "change in future Python releases. " + "Specify the mode argument for opening it for writing.", + FutureWarning, 2) + self.mode = WRITE + self._init_write(filename) + self.compress = zlib.compressobj(compresslevel, + zlib.DEFLATED, + -zlib.MAX_WBITS, + zlib.DEF_MEM_LEVEL, + 0) + self._write_mtime = mtime + self._buffer_size = _WRITE_BUFFER_SIZE + self._buffer = io.BufferedWriter(_WriteBufferStream(self), + buffer_size=self._buffer_size) + else: + raise ValueError("Invalid mode: {!r}".format(mode)) + + self.fileobj = fileobj + if self.mode == WRITE: self._write_gzip_header(compresslevel) except: From 8351549448e24e6d823ad8ee91a54f681c45d44a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 08:32:56 +0000 Subject: [PATCH 16/18] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst b/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst new file mode 100644 index 00000000000000..7586a35fd87894 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst @@ -0,0 +1 @@ +Fix a resource leak when constructing a :class:`gzip.GzipFile` with a filename fails, for example when passing an invalid ``compresslevel`` From 052937fc7b39bff4316aa2fef40217dc0b008acf Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 20 Mar 2025 16:40:12 +0000 Subject: [PATCH 17/18] Update Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst Co-authored-by: Victor Stinner --- .../next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst b/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst index 7586a35fd87894..0f52dec7ce8a83 100644 --- a/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst +++ b/Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst @@ -1 +1 @@ -Fix a resource leak when constructing a :class:`gzip.GzipFile` with a filename fails, for example when passing an invalid ``compresslevel`` +Fix a resource leak when constructing a :class:`gzip.GzipFile` with a filename fails, for example when passing an invalid ``compresslevel``. From 643a00d06c0f716a5ce205ca66d4004fb98dcc8b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 20 Mar 2025 16:40:55 +0000 Subject: [PATCH 18/18] Update Misc/NEWS.d/next/Library/2025-03-20-08-32-49.gh-issue-131492.saC2cA.rst Co-authored-by: Victor Stinner