Skip to content

Commit 0a22407

Browse files
miss-islingtongraingertvstinner
authored
[3.13] gh-131492, gh-131461: handle exceptions in GzipFile constructor while owning resources (GH-131462) (#131518)
(cherry picked from commit ce79274) Co-authored-by: Thomas Grainger <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent d256cfe commit 0a22407

File tree

3 files changed

+66
-52
lines changed

3 files changed

+66
-52
lines changed

Lib/gzip.py

+58-48
Original file line numberDiff line numberDiff line change
@@ -197,51 +197,58 @@ def __init__(self, filename=None, mode=None,
197197
raise ValueError("Invalid mode: {!r}".format(mode))
198198
if mode and 'b' not in mode:
199199
mode += 'b'
200-
if fileobj is None:
201-
fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
202-
if filename is None:
203-
filename = getattr(fileobj, 'name', '')
204-
if not isinstance(filename, (str, bytes)):
205-
filename = ''
206-
else:
207-
filename = os.fspath(filename)
208-
origmode = mode
209-
if mode is None:
210-
mode = getattr(fileobj, 'mode', 'rb')
211-
212-
213-
if mode.startswith('r'):
214-
self.mode = READ
215-
raw = _GzipReader(fileobj)
216-
self._buffer = io.BufferedReader(raw)
217-
self.name = filename
218-
219-
elif mode.startswith(('w', 'a', 'x')):
220-
if origmode is None:
221-
import warnings
222-
warnings.warn(
223-
"GzipFile was opened for writing, but this will "
224-
"change in future Python releases. "
225-
"Specify the mode argument for opening it for writing.",
226-
FutureWarning, 2)
227-
self.mode = WRITE
228-
self._init_write(filename)
229-
self.compress = zlib.compressobj(compresslevel,
230-
zlib.DEFLATED,
231-
-zlib.MAX_WBITS,
232-
zlib.DEF_MEM_LEVEL,
233-
0)
234-
self._write_mtime = mtime
235-
self._buffer_size = _WRITE_BUFFER_SIZE
236-
self._buffer = io.BufferedWriter(_WriteBufferStream(self),
237-
buffer_size=self._buffer_size)
238-
else:
239-
raise ValueError("Invalid mode: {!r}".format(mode))
240200

241-
self.fileobj = fileobj
201+
try:
202+
if fileobj is None:
203+
fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
204+
if filename is None:
205+
filename = getattr(fileobj, 'name', '')
206+
if not isinstance(filename, (str, bytes)):
207+
filename = ''
208+
else:
209+
filename = os.fspath(filename)
210+
origmode = mode
211+
if mode is None:
212+
mode = getattr(fileobj, 'mode', 'rb')
213+
214+
215+
if mode.startswith('r'):
216+
self.mode = READ
217+
raw = _GzipReader(fileobj)
218+
self._buffer = io.BufferedReader(raw)
219+
self.name = filename
220+
221+
elif mode.startswith(('w', 'a', 'x')):
222+
if origmode is None:
223+
import warnings
224+
warnings.warn(
225+
"GzipFile was opened for writing, but this will "
226+
"change in future Python releases. "
227+
"Specify the mode argument for opening it for writing.",
228+
FutureWarning, 2)
229+
self.mode = WRITE
230+
self._init_write(filename)
231+
self.compress = zlib.compressobj(compresslevel,
232+
zlib.DEFLATED,
233+
-zlib.MAX_WBITS,
234+
zlib.DEF_MEM_LEVEL,
235+
0)
236+
self._write_mtime = mtime
237+
self._buffer_size = _WRITE_BUFFER_SIZE
238+
self._buffer = io.BufferedWriter(_WriteBufferStream(self),
239+
buffer_size=self._buffer_size)
240+
else:
241+
raise ValueError("Invalid mode: {!r}".format(mode))
242242

243-
if self.mode == WRITE:
244-
self._write_gzip_header(compresslevel)
243+
self.fileobj = fileobj
244+
245+
if self.mode == WRITE:
246+
self._write_gzip_header(compresslevel)
247+
except:
248+
# Avoid a ResourceWarning if the write fails,
249+
# eg read-only file or KeyboardInterrupt
250+
self._close()
251+
raise
245252

246253
@property
247254
def mtime(self):
@@ -370,11 +377,14 @@ def close(self):
370377
elif self.mode == READ:
371378
self._buffer.close()
372379
finally:
373-
self.fileobj = None
374-
myfileobj = self.myfileobj
375-
if myfileobj:
376-
self.myfileobj = None
377-
myfileobj.close()
380+
self._close()
381+
382+
def _close(self):
383+
self.fileobj = None
384+
myfileobj = self.myfileobj
385+
if myfileobj is not None:
386+
self.myfileobj = None
387+
myfileobj.close()
378388

379389
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
380390
self._check_not_closed()

Lib/test/test_tarfile.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1646,10 +1646,13 @@ def write(self, data):
16461646
raise exctype
16471647

16481648
f = BadFile()
1649-
with self.assertRaises(exctype):
1650-
tar = tarfile.open(tmpname, self.mode, fileobj=f,
1651-
format=tarfile.PAX_FORMAT,
1652-
pax_headers={'non': 'empty'})
1649+
with (
1650+
warnings_helper.check_no_resource_warning(self),
1651+
self.assertRaises(exctype),
1652+
):
1653+
tarfile.open(tmpname, self.mode, fileobj=f,
1654+
format=tarfile.PAX_FORMAT,
1655+
pax_headers={'non': 'empty'})
16531656
self.assertFalse(f.closed)
16541657

16551658
def test_missing_fileobj(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a resource leak when constructing a :class:`gzip.GzipFile` with a filename fails, for example when passing an invalid ``compresslevel``.

0 commit comments

Comments
 (0)