mirror of
https://github.com/python/cpython.git
synced 2025-11-02 11:08:57 +00:00
gh-131492, gh-131461: handle exceptions in GzipFile constructor while owning resources (#131462)
Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
f53e7de6a8
commit
ce79274e9f
4 changed files with 65 additions and 49 deletions
100
Lib/gzip.py
100
Lib/gzip.py
|
|
@ -202,51 +202,58 @@ class GzipFile(_compression.BaseStream):
|
||||||
raise ValueError("Invalid mode: {!r}".format(mode))
|
raise ValueError("Invalid mode: {!r}".format(mode))
|
||||||
if mode and 'b' not in mode:
|
if mode and 'b' not in mode:
|
||||||
mode += 'b'
|
mode += 'b'
|
||||||
if fileobj is None:
|
|
||||||
fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
|
try:
|
||||||
if filename is None:
|
if fileobj is None:
|
||||||
filename = getattr(fileobj, 'name', '')
|
fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
|
||||||
if not isinstance(filename, (str, bytes)):
|
if filename is None:
|
||||||
filename = ''
|
filename = getattr(fileobj, 'name', '')
|
||||||
else:
|
if not isinstance(filename, (str, bytes)):
|
||||||
filename = os.fspath(filename)
|
filename = ''
|
||||||
origmode = mode
|
else:
|
||||||
if mode is None:
|
filename = os.fspath(filename)
|
||||||
mode = getattr(fileobj, 'mode', 'rb')
|
origmode = mode
|
||||||
|
if mode is None:
|
||||||
|
mode = getattr(fileobj, 'mode', 'rb')
|
||||||
|
|
||||||
|
|
||||||
if mode.startswith('r'):
|
if mode.startswith('r'):
|
||||||
self.mode = READ
|
self.mode = READ
|
||||||
raw = _GzipReader(fileobj)
|
raw = _GzipReader(fileobj)
|
||||||
self._buffer = io.BufferedReader(raw)
|
self._buffer = io.BufferedReader(raw)
|
||||||
self.name = filename
|
self.name = filename
|
||||||
|
|
||||||
elif mode.startswith(('w', 'a', 'x')):
|
elif mode.startswith(('w', 'a', 'x')):
|
||||||
if origmode is None:
|
if origmode is None:
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"GzipFile was opened for writing, but this will "
|
"GzipFile was opened for writing, but this will "
|
||||||
"change in future Python releases. "
|
"change in future Python releases. "
|
||||||
"Specify the mode argument for opening it for writing.",
|
"Specify the mode argument for opening it for writing.",
|
||||||
FutureWarning, 2)
|
FutureWarning, 2)
|
||||||
self.mode = WRITE
|
self.mode = WRITE
|
||||||
self._init_write(filename)
|
self._init_write(filename)
|
||||||
self.compress = zlib.compressobj(compresslevel,
|
self.compress = zlib.compressobj(compresslevel,
|
||||||
zlib.DEFLATED,
|
zlib.DEFLATED,
|
||||||
-zlib.MAX_WBITS,
|
-zlib.MAX_WBITS,
|
||||||
zlib.DEF_MEM_LEVEL,
|
zlib.DEF_MEM_LEVEL,
|
||||||
0)
|
0)
|
||||||
self._write_mtime = mtime
|
self._write_mtime = mtime
|
||||||
self._buffer_size = _WRITE_BUFFER_SIZE
|
self._buffer_size = _WRITE_BUFFER_SIZE
|
||||||
self._buffer = io.BufferedWriter(_WriteBufferStream(self),
|
self._buffer = io.BufferedWriter(_WriteBufferStream(self),
|
||||||
buffer_size=self._buffer_size)
|
buffer_size=self._buffer_size)
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid mode: {!r}".format(mode))
|
raise ValueError("Invalid mode: {!r}".format(mode))
|
||||||
|
|
||||||
self.fileobj = fileobj
|
self.fileobj = fileobj
|
||||||
|
|
||||||
if self.mode == WRITE:
|
if self.mode == WRITE:
|
||||||
self._write_gzip_header(compresslevel)
|
self._write_gzip_header(compresslevel)
|
||||||
|
except:
|
||||||
|
# Avoid a ResourceWarning if the write fails,
|
||||||
|
# eg read-only file or KeyboardInterrupt
|
||||||
|
self._close()
|
||||||
|
raise
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mtime(self):
|
def mtime(self):
|
||||||
|
|
@ -387,11 +394,14 @@ class GzipFile(_compression.BaseStream):
|
||||||
elif self.mode == READ:
|
elif self.mode == READ:
|
||||||
self._buffer.close()
|
self._buffer.close()
|
||||||
finally:
|
finally:
|
||||||
self.fileobj = None
|
self._close()
|
||||||
myfileobj = self.myfileobj
|
|
||||||
if myfileobj:
|
def _close(self):
|
||||||
self.myfileobj = None
|
self.fileobj = None
|
||||||
myfileobj.close()
|
myfileobj = self.myfileobj
|
||||||
|
if myfileobj is not None:
|
||||||
|
self.myfileobj = None
|
||||||
|
myfileobj.close()
|
||||||
|
|
||||||
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
|
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
|
||||||
self._check_not_closed()
|
self._check_not_closed()
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ from test import archiver_tests
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
from test.support import script_helper
|
from test.support import script_helper
|
||||||
|
from test.support import warnings_helper
|
||||||
|
|
||||||
# Check for our compression modules.
|
# Check for our compression modules.
|
||||||
try:
|
try:
|
||||||
|
|
@ -1638,10 +1639,13 @@ class WriteTest(WriteTestBase, unittest.TestCase):
|
||||||
raise exctype
|
raise exctype
|
||||||
|
|
||||||
f = BadFile()
|
f = BadFile()
|
||||||
with self.assertRaises(exctype):
|
with (
|
||||||
tar = tarfile.open(tmpname, self.mode, fileobj=f,
|
warnings_helper.check_no_resource_warning(self),
|
||||||
format=tarfile.PAX_FORMAT,
|
self.assertRaises(exctype),
|
||||||
pax_headers={'non': 'empty'})
|
):
|
||||||
|
tarfile.open(tmpname, self.mode, fileobj=f,
|
||||||
|
format=tarfile.PAX_FORMAT,
|
||||||
|
pax_headers={'non': 'empty'})
|
||||||
self.assertFalse(f.closed)
|
self.assertFalse(f.closed)
|
||||||
|
|
||||||
def test_missing_fileobj(self):
|
def test_missing_fileobj(self):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :exc:`ResourceWarning` when constructing a :class:`gzip.GzipFile` in write mode with a broken file object.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix a resource leak when constructing a :class:`gzip.GzipFile` with a filename fails, for example when passing an invalid ``compresslevel``.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue