From 8d62df60d8733d0fa9aee14ef746d0009a7a9726 Mon Sep 17 00:00:00 2001 From: Daniel Hillier Date: Sat, 30 Nov 2019 19:30:47 +1100 Subject: [PATCH] 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. --- Lib/test/test_zipfile.py | 14 ++++++++++++++ Lib/zipfile.py | 10 ++++++++++ .../2019-10-02-02-55-37.bpo-37523.GguwJ6.rst | 1 + 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2019-10-02-02-55-37.bpo-37523.GguwJ6.rst diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 1e1854be710..66f05ac1f3a 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -571,6 +571,20 @@ def test_write_default_name(self): with open(TESTFN, "rb") as f: 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): """Check that trying to call write() on a readonly ZipFile object raises a ValueError.""" diff --git a/Lib/zipfile.py b/Lib/zipfile.py index b0afb9da942..e1d07f2a523 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -889,12 +889,16 @@ def peek(self, n=1): return self._readbuffer[self._offset: self._offset + 512] def readable(self): + if self.closed: + raise ValueError("I/O operation on closed file.") return True def read(self, n=-1): """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 self.closed: + raise ValueError("read from closed file.") if n is None or n < 0: buf = self._readbuffer[self._offset:] self._readbuffer = b'' @@ -1031,9 +1035,13 @@ def close(self): super().close() def seekable(self): + if self.closed: + raise ValueError("I/O operation on closed file.") return self._seekable def seek(self, offset, whence=0): + if self.closed: + raise ValueError("seek on closed file.") if not self._seekable: raise io.UnsupportedOperation("underlying stream is not seekable") curr_pos = self.tell() @@ -1082,6 +1090,8 @@ def seek(self, offset, whence=0): return self.tell() def tell(self): + if self.closed: + raise ValueError("tell on closed file.") if not self._seekable: raise io.UnsupportedOperation("underlying stream is not seekable") filepos = self._orig_file_size - self._left - len(self._readbuffer) + self._offset diff --git a/Misc/NEWS.d/next/Library/2019-10-02-02-55-37.bpo-37523.GguwJ6.rst b/Misc/NEWS.d/next/Library/2019-10-02-02-55-37.bpo-37523.GguwJ6.rst new file mode 100644 index 00000000000..5711969ff38 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-10-02-02-55-37.bpo-37523.GguwJ6.rst @@ -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. \ No newline at end of file