8000 [3.12] gh-131492, gh-131461: handle exceptions in GzipFile constructo… · python/cpython@2d6a778 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2d6a778

Browse files
miss-islingtongraingertvstinner
authored
[3.12] gh-131492, gh-131461: handle exceptions in GzipFile constructor while owning resources (GH-131462) (#131519)
(cherry picked from commit ce79274) Co-authored-by: Thomas Grainger <tagrain@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 3d1f8ca commit 2d6a778

File tree

3 files changed

+66
-52
lines changed

3 files changed

+66
-52
lines changed

Lib/gzip.py

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

239-
self.fileobj = fileobj
199+
try:
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))
240+
241+
self.fileobj = fileobj
240242

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

244251
@property
245252
def mtime(self):
@@ -368,11 +375,14 @@ def close(self):
368375
elif self.mode == READ:
369376
self._buffer.close()
370377
finally:
371-
self.fileobj = None
372-
myfileobj = self.myfileobj
373-
if myfileobj:
374-
self.myfileobj = None
375-
myfileobj.close()
378+
self._close()
379+
380+
def _close(self):
381+
self.fileobj = None
382+
myfileobj = self.myfileobj
383+
if myfileobj is not None:
384+
self.myfileobj = None
385+
myfileobj.close()
376386

377387
def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
378388
self._check_not_closed()

Lib/test/test_tarfile.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,10 +1615,13 @@ def write(self, data):
16151615
raise exctype
16161616

16171617
f = BadFile()
1618-
with self.assertRaises(exctype):
1619-
tar = tarfile.open(tmpname, self.mode, fileobj=f,
1620-
format=tarfile.PAX_FORMAT,
1621-
pax_headers={'non': 'empty'})
1618+
with (
1619+
warnings_helper.check_no_resource_warning(self),
1620+
self.assertRaises(exctype),
1621+
):
1622+
tarfile.open(tmpname, self.mode, fileobj=f,
1623+
format=tarfile.PAX_FORMAT,
1624+
pax_headers={'non': 'empty'})
16221625
self.assertFalse(f.closed)
16231626

16241627

Lines changed: 1 addition & 0 deletions
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)
0