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>
"""
+ # 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:
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:
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`
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
# 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):
--- /dev/null
+Deleting :class:`gzip.GzipFile` before it is closed now emits a
+:exc:`ResourceWarning`.