diff --git a/Lib/mailbox.py b/Lib/mailbox.py --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -1874,17 +1874,26 @@ self._pos = f.tell() else: self._pos = pos + self._do_close = True + self._is_open = True + + def _set_noclose(self): + """Subclass hook - use to prevent close of file.""" + self._do_close = False def read(self, size=None): """Read bytes.""" + self._closed_check() return self._read(size, self._file.read) def read1(self, size=None): """Read bytes.""" + self._closed_check() return self._read(size, self._file.read1) def readline(self, size=None): """Read a line.""" + self._closed_check() return self._read(size, self._file.readline) def readlines(self, sizehint=None): @@ -1908,10 +1917,12 @@ def tell(self): """Return the position.""" + self._closed_check() return self._pos def seek(self, offset, whence=0): """Change position.""" + self._closed_check() if whence == 1: self._file.seek(self._pos) self._file.seek(offset, whence) @@ -1919,9 +1930,15 @@ def close(self): """Close the file.""" - if hasattr(self._file, 'close'): + if self._do_close: + self._do_close = False self._file.close() - del self._file + del self._file + self._is_open = False + + def _closed_check(self): + if not self._is_open: + raise ValueError('I/O operation on closed file') def _read(self, size, read_method): """Read size bytes using read_method.""" @@ -1940,20 +1957,24 @@ self.close() def readable(self): + self._closed_check() return self._file.readable() def writable(self): - return self._file.writable() + self._closed_check() + return False def seekable(self): + self._closed_check() return self._file.seekable() def flush(self): + self._closed_check() return self._file.flush() @property def closed(self): - return self._file.closed + return not self._is_open class _PartialFile(_ProxyFile): @@ -1962,6 +1983,7 @@ def __init__(self, f, start=None, stop=None): """Initialize a _PartialFile.""" _ProxyFile.__init__(self, f, start) + super()._set_noclose() self._start = start self._stop = stop @@ -1971,6 +1993,7 @@ def seek(self, offset, whence=0): """Change position, possibly with respect to start or stop.""" + self._closed_check() if whence == 0: self._pos = self._start whence = 1 @@ -1988,11 +2011,6 @@ size = remaining return _ProxyFile._read(self, size, read_method) - def close(self): - # do *not* close the underlying file object for partial files, - # since it's global to the mailbox object - del self._file - def _lock_file(f, dotlock=True): """Lock file f using lockf and dot locking.""" diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -290,12 +290,14 @@ key1 = self._box.add(_sample_message) with self._box.get_file(key0) as file: data0 = file.read() - with self._box.get_file(key1) as file: - data1 = file.read() + file1 = self._box.get_file(key1) + data1 = file1.read() self.assertEqual(data0.decode('ascii').replace(os.linesep, '\n'), self._template % 0) self.assertEqual(data1.decode('ascii').replace(os.linesep, '\n'), _sample_message) + file1.close() + file1.close() def test_iterkeys(self): # Get keys using iterkeys() @@ -1834,9 +1836,17 @@ def _test_close(self, proxy): # Close a file + self.assertFalse(proxy.closed) proxy.close() - self.assertRaises(AttributeError, lambda: proxy.close()) - + self.assertTrue(proxy.closed) + self.assertRaises(ValueError, proxy.read) + self.assertRaises(ValueError, proxy.read1) + self.assertRaises(ValueError, proxy.readlines) + self.assertRaises(ValueError, proxy.tell) + self.assertRaises(ValueError, proxy.readable) + self.assertRaises(ValueError, proxy.writable) + self.assertRaises(ValueError, proxy.seekable) + self.assertRaises(ValueError, proxy.flush) class TestProxyFile(TestProxyFileBase):