Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6be8876

Browse files
committed
Merged revisions 80720 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r80720 | antoine.pitrou | 2010-05-03 18:25:33 +0200 (lun., 03 mai 2010) | 5 lines 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. Patch by Pascal Chambon. ........
1 parent fb7b668 commit 6be8876

7 files changed

Lines changed: 80 additions & 31 deletions

File tree

Lib/_pyio.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ def flush(self) -> None:
322322
323323
This is not implemented for read-only and non-blocking streams.
324324
"""
325+
self._checkClosed()
325326
# XXX Should this return the number of bytes written???
326327

327328
__closed = False
@@ -332,10 +333,7 @@ def close(self) -> None:
332333
This method has no effect if the file is already closed.
333334
"""
334335
if not self.__closed:
335-
try:
336-
self.flush()
337-
except IOError:
338-
pass # If flush() fails, just give up
336+
self.flush()
339337
self.__closed = True
340338

341339
def __del__(self) -> None:
@@ -702,14 +700,13 @@ def truncate(self, pos=None):
702700
### Flush and close ###
703701

704702
def flush(self):
703+
if self.closed:
704+
raise ValueError("flush of closed file")
705705
self.raw.flush()
706706

707707
def close(self):
708-
if not self.closed and self.raw is not None:
709-
try:
710-
self.flush()
711-
except IOError:
712-
pass # If flush() fails, just give up
708+
if self.raw is not None and not self.closed:
709+
self.flush()
713710
self.raw.close()
714711

715712
def detach(self):
@@ -1521,11 +1518,8 @@ def flush(self):
15211518
self._telling = self._seekable
15221519

15231520
def close(self):
1524-
if self.buffer is not None:
1525-
try:
1526-
self.flush()
1527-
except IOError:
1528-
pass # If flush() fails, just give up
1521+
if self.buffer is not None and not self.closed:
1522+
self.flush()
15291523
self.buffer.close()
15301524

15311525
@property

Lib/test/test_io.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,20 @@ def test_unbounded_file(self):
536536
with self.open(zero, "r") as f:
537537
self.assertRaises(OverflowError, f.read)
538538

539+
def test_flush_error_on_close(self):
540+
f = self.open(support.TESTFN, "wb", buffering=0)
541+
def bad_flush():
542+
raise IOError()
543+
f.flush = bad_flush
544+
self.assertRaises(IOError, f.close) # exception not swallowed
545+
546+
def test_multi_close(self):
547+
f = self.open(support.TESTFN, "wb", buffering=0)
548+
f.close()
549+
f.close()
550+
f.close()
551+
self.assertRaises(ValueError, f.flush)
552+
539553
class CIOTest(IOTest):
540554
pass
541555

@@ -635,6 +649,22 @@ def test_repr(self):
635649
raw.name = b"dummy"
636650
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
637651

652+
def test_flush_error_on_close(self):
653+
raw = self.MockRawIO()
654+
def bad_flush():
655+
raise IOError()
656+
raw.flush = bad_flush
657+
b = self.tp(raw)
658+
self.assertRaises(IOError, b.close) # exception not swallowed
659+
660+
def test_multi_close(self):
661+
raw = self.MockRawIO()
662+
b = self.tp(raw)
663+
b.close()
664+
b.close()
665+
b.close()
666+
self.assertRaises(ValueError, b.flush)
667+
638668

639669
class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
640670
read_mode = "rb"
@@ -2107,6 +2137,20 @@ def run(n):
21072137
for n in range(20):
21082138
self.assertEquals(content.count("Thread%03d\n" % n), 1)
21092139

2140+
def test_flush_error_on_close(self):
2141+
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
2142+
def bad_flush():
2143+
raise IOError()
2144+
txt.flush = bad_flush
2145+
self.assertRaises(IOError, txt.close) # exception not swallowed
2146+
2147+
def test_multi_close(self):
2148+
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
2149+
txt.close()
2150+
txt.close()
2151+
txt.close()
2152+
self.assertRaises(ValueError, txt.flush)
2153+
21102154
class CTextIOWrapperTest(TextIOWrapperTest):
21112155

21122156
def test_initialization(self):

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ C-API
345345
Library
346346
-------
347347

348+
- Issue #7865: The close() method of :mod:`io` objects should not swallow
349+
exceptions raised by the implicit flush(). Also ensure that calling
350+
close() several times is supported. Patch by Pascal Chambon.
351+
348352
- Issue #4687: Fix accuracy of garbage collection runtimes displayed with
349353
gc.DEBUG_STATS.
350354

Modules/_io/bufferedio.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,11 +440,7 @@ buffered_close(buffered *self, PyObject *args)
440440
res = PyObject_CallMethodObjArgs((PyObject *)self, _PyIO_str_flush, NULL);
441441
ENTER_BUFFERED(self)
442442
if (res == NULL) {
443-
/* If flush() fails, just give up */
444-
if (PyErr_ExceptionMatches(PyExc_IOError))
445-
PyErr_Clear();
446-
else
447-
goto end;
443+
goto end;
448444
}
449445
Py_XDECREF(res);
450446

Modules/_io/bytesio.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ PyDoc_STRVAR(flush_doc,
169169
static PyObject *
170170
bytesio_flush(bytesio *self)
171171
{
172+
CHECK_CLOSED(self);
172173
Py_RETURN_NONE;
173174
}
174175

Modules/_io/iobase.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,7 @@ iobase_close(PyObject *self, PyObject *args)
183183
res = PyObject_CallMethodObjArgs(self, _PyIO_str_flush, NULL);
184184
PyObject_SetAttrString(self, "__IOBase_closed", Py_True);
185185
if (res == NULL) {
186-
/* If flush() fails, just give up */
187-
if (PyErr_ExceptionMatches(PyExc_IOError))
188-
PyErr_Clear();
189-
else
190-
return NULL;
186+
return NULL;
191187
}
192188
Py_XDECREF(res);
193189
Py_RETURN_NONE;

Modules/_io/textio.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,16 +2398,30 @@ static PyObject *
23982398
textiowrapper_close(textio *self, PyObject *args)
23992399
{
24002400
PyObject *res;
2401+
int r;
24012402
CHECK_INITIALIZED(self);
2402-
res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
2403-
if (res == NULL) {
2404-
/* If flush() fails, just give up */
2405-
PyErr_Clear();
2403+
2404+
res = textiowrapper_closed_get(self, NULL);
2405+
if (res == NULL)
2406+
return NULL;
2407+
r = PyObject_IsTrue(res);
2408+
Py_DECREF(res);
2409+
if (r < 0)
2410+
return NULL;
2411+
2412+
if (r > 0) {
2413+
Py_RETURN_NONE; /* stream already closed */
24062414
}
2407-
else
2408-
Py_DECREF(res);
2415+
else {
2416+
res = PyObject_CallMethod((PyObject *)self, "flush", NULL);
2417+
if (res == NULL) {
2418+
return NULL;
2419+
}
2420+
else
2421+
Py_DECREF(res);
24092422

2410-
return PyObject_CallMethod(self->buffer, "close", NULL);
2423+
return PyObject_CallMethod(self->buffer, "close", NULL);
2424+
}
24112425
}
24122426

24132427
static PyObject *

0 commit comments

Comments
 (0)