bpo-37523: Raise ValueError for I/O operations on a closed zipfile.ZipExtFile. (GH-14658)

Raises ValueError when calling the following on a closed zipfile.ZipExtFile: read, readable, seek, seekable, tell.
This commit is contained in:
Daniel Hillier 2019-11-30 19:30:47 +11:00 committed by Serhiy Storchaka
parent 1df65f7c6c
commit 8d62df60d8
3 changed files with 25 additions and 0 deletions

View File

@ -571,6 +571,20 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
with open(TESTFN, "rb") as f: with open(TESTFN, "rb") as f:
self.assertEqual(zipfp.read(TESTFN), f.read()) self.assertEqual(zipfp.read(TESTFN), f.read())
def test_io_on_closed_zipextfile(self):
fname = "somefile.txt"
with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
zipfp.writestr(fname, "bogus")
with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
with zipfp.open(fname) as fid:
fid.close()
self.assertRaises(ValueError, fid.read)
self.assertRaises(ValueError, fid.seek, 0)
self.assertRaises(ValueError, fid.tell)
self.assertRaises(ValueError, fid.readable)
self.assertRaises(ValueError, fid.seekable)
def test_write_to_readonly(self): def test_write_to_readonly(self):
"""Check that trying to call write() on a readonly ZipFile object """Check that trying to call write() on a readonly ZipFile object
raises a ValueError.""" raises a ValueError."""

View File

@ -889,12 +889,16 @@ class ZipExtFile(io.BufferedIOBase):
return self._readbuffer[self._offset: self._offset + 512] return self._readbuffer[self._offset: self._offset + 512]
def readable(self): def readable(self):
if self.closed:
raise ValueError("I/O operation on closed file.")
return True return True
def read(self, n=-1): def read(self, n=-1):
"""Read and return up to n bytes. """Read and return up to n bytes.
If the argument is omitted, None, or negative, data is read and returned until EOF is reached. If the argument is omitted, None, or negative, data is read and returned until EOF is reached.
""" """
if self.closed:
raise ValueError("read from closed file.")
if n is None or n < 0: if n is None or n < 0:
buf = self._readbuffer[self._offset:] buf = self._readbuffer[self._offset:]
self._readbuffer = b'' self._readbuffer = b''
@ -1031,9 +1035,13 @@ class ZipExtFile(io.BufferedIOBase):
super().close() super().close()
def seekable(self): def seekable(self):
if self.closed:
raise ValueError("I/O operation on closed file.")
return self._seekable return self._seekable
def seek(self, offset, whence=0): def seek(self, offset, whence=0):
if self.closed:
raise ValueError("seek on closed file.")
if not self._seekable: if not self._seekable:
raise io.UnsupportedOperation("underlying stream is not seekable") raise io.UnsupportedOperation("underlying stream is not seekable")
curr_pos = self.tell() curr_pos = self.tell()
@ -1082,6 +1090,8 @@ class ZipExtFile(io.BufferedIOBase):
return self.tell() return self.tell()
def tell(self): def tell(self):
if self.closed:
raise ValueError("tell on closed file.")
if not self._seekable: if not self._seekable:
raise io.UnsupportedOperation("underlying stream is not seekable") raise io.UnsupportedOperation("underlying stream is not seekable")
filepos = self._orig_file_size - self._left - len(self._readbuffer) + self._offset filepos = self._orig_file_size - self._left - len(self._readbuffer) + self._offset

View File

@ -0,0 +1 @@
Change :class:`zipfile.ZipExtFile` to raise ``ValueError`` when trying to access the underlying file object after it has been closed. This new behavior is consistent with how accessing closed files is handled in other parts of Python.