summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/io.py21
-rw-r--r--Lib/test/test_io.py44
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_bytesio.c1
4 files changed, 58 insertions, 12 deletions
diff --git a/Lib/io.py b/Lib/io.py
index d090eec..54de504 100644
--- a/Lib/io.py
+++ b/Lib/io.py
@@ -368,6 +368,9 @@ class IOBase(object):
This is not implemented for read-only and non-blocking streams.
"""
+ if self.__closed:
+ raise ValueError("flush of closed file")
+ #self._checkClosed()
# XXX Should this return the number of bytes written???
__closed = False
@@ -378,10 +381,7 @@ class IOBase(object):
This method has no effect if the file is already closed.
"""
if not self.__closed:
- try:
- self.flush()
- except IOError:
- pass # If flush() fails, just give up
+ self.flush()
self.__closed = True
def __del__(self):
@@ -751,10 +751,7 @@ class _BufferedIOMixin(BufferedIOBase):
def close(self):
if not self.closed:
- try:
- self.flush()
- except IOError:
- pass # If flush() fails, just give up
+ self.flush()
self.raw.close()
### Inquiries ###
@@ -1087,6 +1084,8 @@ class BufferedWriter(_BufferedIOMixin):
return self.raw.truncate(pos)
def flush(self):
+ if self.closed:
+ raise ValueError("flush of closed file")
with self._write_lock:
self._flush_unlocked()
@@ -1472,11 +1471,9 @@ class TextIOWrapper(TextIOBase):
self._telling = self._seekable
def close(self):
- try:
+ if not self.closed:
self.flush()
- except:
- pass # If flush() fails, just give up
- self.buffer.close()
+ self.buffer.close()
@property
def closed(self):
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 38c58c3..c732b1f 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -313,6 +313,20 @@ class IOTest(unittest.TestCase):
file = io.open(f.fileno(), "r", closefd=False)
self.assertEqual(file.buffer.raw.closefd, False)
+ def test_flush_error_on_close(self):
+ f = io.open(test_support.TESTFN, "wb", buffering=0)
+ def bad_flush():
+ raise IOError()
+ f.flush = bad_flush
+ self.assertRaises(IOError, f.close) # exception not swallowed
+
+ def test_multi_close(self):
+ f = io.open(test_support.TESTFN, "wb", buffering=0)
+ f.close()
+ f.close()
+ f.close()
+ self.assertRaises(ValueError, f.flush)
+
class MemorySeekTestMixin:
@@ -565,6 +579,22 @@ class BufferedWriterTest(unittest.TestCase):
finally:
test_support.unlink(test_support.TESTFN)
+ def test_flush_error_on_close(self):
+ raw = MockRawIO()
+ def bad_flush():
+ raise IOError()
+ raw.flush = bad_flush
+ b = io.BufferedWriter(raw)
+ self.assertRaises(IOError, b.close) # exception not swallowed
+
+ def test_multi_close(self):
+ raw = MockRawIO()
+ b = io.BufferedWriter(raw)
+ b.close()
+ b.close()
+ b.close()
+ self.assertRaises(ValueError, b.flush)
+
class BufferedRWPairTest(unittest.TestCase):
@@ -1295,6 +1325,20 @@ class TextIOWrapperTest(unittest.TestCase):
decoder = io.IncrementalNewlineDecoder(decoder, translate=True)
self.check_newline_decoder_utf8(decoder)
+ def test_flush_error_on_close(self):
+ txt = io.TextIOWrapper(io.BytesIO(self.testdata), encoding="ascii")
+ def bad_flush():
+ raise IOError()
+ txt.flush = bad_flush
+ self.assertRaises(IOError, txt.close) # exception not swallowed
+
+ def test_multi_close(self):
+ txt = io.TextIOWrapper(io.BytesIO(self.testdata), encoding="ascii")
+ txt.close()
+ txt.close()
+ txt.close()
+ self.assertRaises(ValueError, txt.flush)
+
# XXX Tests for open()
diff --git a/Misc/NEWS b/Misc/NEWS
index 62d4e59..81dbf53 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -33,6 +33,10 @@ Core and Builtins
Library
-------
+- Issue #7865: The close() method of :mod:`io` objects should not swallow
+ exceptions raised by the implicit flush(). Also ensure that calling
+ close() several times is supported. Initial patch by Pascal Chambon.
+
- Issue #8581: logging: removed errors raised when closing handlers twice.
- Issue #4687: Fix accuracy of garbage collection runtimes displayed with
diff --git a/Modules/_bytesio.c b/Modules/_bytesio.c
index bbbb156..e369f81 100644
--- a/Modules/_bytesio.c
+++ b/Modules/_bytesio.c
@@ -163,6 +163,7 @@ PyDoc_STRVAR(flush_doc,
static PyObject *
bytesio_flush(BytesIOObject *self)
{
+ CHECK_CLOSED(self);
Py_RETURN_NONE;
}