diff --git a/Lib/gzip.py b/Lib/gzip.py index 247efc01527bb14..ea184ab88f1eb08 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -11,6 +11,7 @@ import struct import sys import time +import warnings # must stay eager to support finalization import weakref import zlib from compression._common import _streams @@ -229,7 +230,6 @@ def __init__(self, filename=None, mode=None, 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. " @@ -466,13 +466,15 @@ def readline(self, size=-1): self._check_not_closed() return self._buffer.readline(size) - def __del__(self): - if self.mode == WRITE and not self.closed: - import warnings - warnings.warn("unclosed GzipFile", - ResourceWarning, source=self, stacklevel=2) - - super().__del__() + def __del__(self, _warn=warnings.warn): + try: + # Wrap within a try to ensure the file is closed even if the + # warning raises, e.g. when ResourceWarning is made an error. + if self.mode == WRITE and not self.closed: + _warn("unclosed GzipFile", + ResourceWarning, source=self, stacklevel=2) + finally: + super().__del__() def _read_exact(fp, n): '''Read exactly *n* bytes from `fp` diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 8bc8e507683cb50..8115519cf6555a7 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -9,9 +9,10 @@ import struct import sys import unittest +import warnings from subprocess import PIPE, Popen from unittest import mock -from test.support import catch_unraisable_exception +from test.support import catch_unraisable_exception, gc_collect from test.support import force_not_colorized_test_class, import_helper from test.support import os_helper from test.support import _4G, bigmemtest, requires_subprocess @@ -965,6 +966,19 @@ def test_refloop_unraisable(self): gc.collect() self.assertIsNone(cm.unraisable) + def test_del_closes_if_warning_fails(self): + # ResourceWarning escalated to an error is reported as unraisable, + # and the file is still closed. + b = io.BytesIO() + f = gzip.GzipFile(fileobj=b, mode="wb") + f.write(data1) + with (warnings.catch_warnings(action="error", category=ResourceWarning), + catch_unraisable_exception() as cm): + del f + gc_collect() + self.assertIs(cm.unraisable.exc_type, ResourceWarning) + self.assertEqual(gzip.decompress(b.getvalue()), data1) + class TestOpen(BaseTest): def test_binary_modes(self): diff --git a/Misc/NEWS.d/next/Library/2026-10-14-15-50-59.gh-issue-159422.16mSqQ.rst b/Misc/NEWS.d/next/Library/2026-10-14-15-50-59.gh-issue-159422.16mSqQ.rst new file mode 100644 index 000000000000000..41e09f11942de9a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-14-15-50-59.gh-issue-159422.16mSqQ.rst @@ -0,0 +1,2 @@ +Fix :class:`gzip.GzipFile` not being closed at interpreter shutdown when +emitting its :exc:`ResourceWarning` failed with an :exc:`ImportError`.