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 <vstinner@python.org>
This commit is contained in:
16
Lib/gzip.py
16
Lib/gzip.py
@@ -193,6 +193,11 @@ class GzipFile(_compression.BaseStream):
|
||||
|
||||
"""
|
||||
|
||||
# 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 @@ class GzipFile(_compression.BaseStream):
|
||||
|
||||
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 @@ class GzipFile(_compression.BaseStream):
|
||||
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`
|
||||
|
||||
Reference in New Issue
Block a user