https://github.com/python/cpython/commit/93089c073661f5aa9f8cca574a3e223716728639 commit: 93089c073661f5aa9f8cca574a3e223716728639 branch: main author: Cody Maloney <cmalo...@users.noreply.github.com> committer: vstinner <vstin...@python.org> date: 2025-03-13T11:20:05+01:00 summary:
gh-130806: Emit ResourceWarning if GzipFile unclosed (#130905) This may indicate accidental data loss. Ways to make sure all data is written: 1. Use the [file-like object](https://docs.python.org/3/glossary.html#term-file-object) as a [“With Statement Context Manager”](https://docs.python.org/3/reference/datamodel.html#context-managers). - All objects which [inherit](https://docs.python.org/3/tutorial/classes.html#inheritance) from [IOBase](https://docs.python.org/3/library/io.html#io.IOBase) support this. - [`BufferedIOBase`](https://docs.python.org/3/library/io.html#io.BufferedIOBase), [`BufferedWriter`](https://docs.python.org/3/library/io.html#io.BufferedWriter), and [`GzipFile`](https://docs.python.org/3/library/gzip.html#gzip.GzipFile) all support this. - Ensures `.close()` is called in both exception and regular cases. 2. Ensure [`.close()`](https://docs.python.org/3/library/io.html#io.IOBase.close) is always called which flushes data before closing. 3. If the underlying stream need to be kept open, use [`.detach()`](https://docs.python.org/3/library/io.html#io.BufferedIOBase.detach) Since 3.12 flushing has been necessary in GzipFile (see gh-105808 which was a release blocker), this makes that more visible. Users have been encountering as they upgrade to 3.12 (ex. gh-129726). There are a number of cases of unclosed file-like objects being deleted in CPython libraries and the test suite. This issue includes resolving those cases where the new ResourceWarning is emitted. Co-authored-by: Victor Stinner <vstin...@python.org> files: A Misc/NEWS.d/next/Library/2025-03-05-20-02-21.gh-issue-130806.o0l2FJ.rst M Lib/gzip.py M Lib/test/test_gzip.py diff --git a/Lib/gzip.py b/Lib/gzip.py index d681ef6b488dad..c9c088783bea65 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -193,6 +193,11 @@ def __init__(self, filename=None, mode=None, """ + # Ensure attributes exist at __del__ + self.mode = None + self.fileobj = None + self._buffer = None + if mode and ('t' in mode or 'U' in mode): raise ValueError("Invalid mode: {!r}".format(mode)) if mode and 'b' not in mode: @@ -368,7 +373,9 @@ def closed(self): def close(self): fileobj = self.fileobj - if fileobj is None or self._buffer.closed: + if fileobj is None: + return + if self._buffer is None or self._buffer.closed: return try: if self.mode == WRITE: @@ -445,6 +452,13 @@ 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 _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 260fae5ae1b368..fa5de7c190e6a3 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -9,6 +9,7 @@ import struct import sys import unittest +import warnings from subprocess import PIPE, Popen from test.support import catch_unraisable_exception from test.support import import_helper @@ -899,9 +900,10 @@ def test_refloop_unraisable(self): # fileobj would be closed before the GzipFile as the result of a # reference loop. See issue gh-129726 with catch_unraisable_exception() as cm: - gzip.GzipFile(fileobj=io.BytesIO(), mode="w") - gc.collect() - self.assertIsNone(cm.unraisable) + with self.assertWarns(ResourceWarning): + gzip.GzipFile(fileobj=io.BytesIO(), mode="w") + gc.collect() + self.assertIsNone(cm.unraisable) class TestOpen(BaseTest): diff --git a/Misc/NEWS.d/next/Library/2025-03-05-20-02-21.gh-issue-130806.o0l2FJ.rst b/Misc/NEWS.d/next/Library/2025-03-05-20-02-21.gh-issue-130806.o0l2FJ.rst new file mode 100644 index 00000000000000..37c3d12549eb91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-05-20-02-21.gh-issue-130806.o0l2FJ.rst @@ -0,0 +1,2 @@ +Deleting :class:`gzip.GzipFile` before it is closed now emits a +:exc:`ResourceWarning`. _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com