mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
Issue #5700: io.FileIO() called flush() after closing the file.
flush() was not called in close() if closefd=False.
This commit is contained in:
parent
5e3d7a401d
commit
a3712a9a6c
3 changed files with 65 additions and 9 deletions
|
@ -593,13 +593,43 @@ class IOTest(unittest.TestCase):
|
||||||
with self.open(zero, "r") as f:
|
with self.open(zero, "r") as f:
|
||||||
self.assertRaises(OverflowError, f.read)
|
self.assertRaises(OverflowError, f.read)
|
||||||
|
|
||||||
def test_flush_error_on_close(self):
|
def check_flush_error_on_close(self, *args, **kwargs):
|
||||||
f = self.open(support.TESTFN, "wb", buffering=0)
|
# Test that the file is closed despite failed flush
|
||||||
|
# and that flush() is called before file closed.
|
||||||
|
f = self.open(*args, **kwargs)
|
||||||
|
closed = []
|
||||||
def bad_flush():
|
def bad_flush():
|
||||||
|
closed[:] = [f.closed]
|
||||||
raise OSError()
|
raise OSError()
|
||||||
f.flush = bad_flush
|
f.flush = bad_flush
|
||||||
self.assertRaises(OSError, f.close) # exception not swallowed
|
self.assertRaises(OSError, f.close) # exception not swallowed
|
||||||
self.assertTrue(f.closed)
|
self.assertTrue(f.closed)
|
||||||
|
self.assertTrue(closed) # flush() called
|
||||||
|
self.assertFalse(closed[0]) # flush() called before file closed
|
||||||
|
|
||||||
|
def test_flush_error_on_close(self):
|
||||||
|
# raw file
|
||||||
|
# Issue #5700: io.FileIO calls flush() after file closed
|
||||||
|
self.check_flush_error_on_close(support.TESTFN, 'wb', buffering=0)
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'wb', buffering=0)
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False)
|
||||||
|
os.close(fd)
|
||||||
|
# buffered io
|
||||||
|
self.check_flush_error_on_close(support.TESTFN, 'wb')
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'wb')
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'wb', closefd=False)
|
||||||
|
os.close(fd)
|
||||||
|
# text io
|
||||||
|
self.check_flush_error_on_close(support.TESTFN, 'w')
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'w')
|
||||||
|
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
|
||||||
|
self.check_flush_error_on_close(fd, 'w', closefd=False)
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
def test_multi_close(self):
|
def test_multi_close(self):
|
||||||
f = self.open(support.TESTFN, "wb", buffering=0)
|
f = self.open(support.TESTFN, "wb", buffering=0)
|
||||||
|
@ -788,13 +818,21 @@ class CommonBufferedTests:
|
||||||
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
|
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
|
||||||
|
|
||||||
def test_flush_error_on_close(self):
|
def test_flush_error_on_close(self):
|
||||||
|
# Test that buffered file is closed despite failed flush
|
||||||
|
# and that flush() is called before file closed.
|
||||||
raw = self.MockRawIO()
|
raw = self.MockRawIO()
|
||||||
|
closed = []
|
||||||
def bad_flush():
|
def bad_flush():
|
||||||
|
closed[:] = [b.closed, raw.closed]
|
||||||
raise OSError()
|
raise OSError()
|
||||||
raw.flush = bad_flush
|
raw.flush = bad_flush
|
||||||
b = self.tp(raw)
|
b = self.tp(raw)
|
||||||
self.assertRaises(OSError, b.close) # exception not swallowed
|
self.assertRaises(OSError, b.close) # exception not swallowed
|
||||||
self.assertTrue(b.closed)
|
self.assertTrue(b.closed)
|
||||||
|
self.assertTrue(raw.closed)
|
||||||
|
self.assertTrue(closed) # flush() called
|
||||||
|
self.assertFalse(closed[0]) # flush() called before file closed
|
||||||
|
self.assertFalse(closed[1])
|
||||||
|
|
||||||
def test_close_error_on_close(self):
|
def test_close_error_on_close(self):
|
||||||
raw = self.MockRawIO()
|
raw = self.MockRawIO()
|
||||||
|
@ -2618,12 +2656,20 @@ class TextIOWrapperTest(unittest.TestCase):
|
||||||
self.assertEqual(content.count("Thread%03d\n" % n), 1)
|
self.assertEqual(content.count("Thread%03d\n" % n), 1)
|
||||||
|
|
||||||
def test_flush_error_on_close(self):
|
def test_flush_error_on_close(self):
|
||||||
|
# Test that text file is closed despite failed flush
|
||||||
|
# and that flush() is called before file closed.
|
||||||
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
|
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
|
||||||
|
closed = []
|
||||||
def bad_flush():
|
def bad_flush():
|
||||||
|
closed[:] = [txt.closed, txt.buffer.closed]
|
||||||
raise OSError()
|
raise OSError()
|
||||||
txt.flush = bad_flush
|
txt.flush = bad_flush
|
||||||
self.assertRaises(OSError, txt.close) # exception not swallowed
|
self.assertRaises(OSError, txt.close) # exception not swallowed
|
||||||
self.assertTrue(txt.closed)
|
self.assertTrue(txt.closed)
|
||||||
|
self.assertTrue(txt.buffer.closed)
|
||||||
|
self.assertTrue(closed) # flush() called
|
||||||
|
self.assertFalse(closed[0]) # flush() called before file closed
|
||||||
|
self.assertFalse(closed[1])
|
||||||
|
|
||||||
def test_close_error_on_close(self):
|
def test_close_error_on_close(self):
|
||||||
buffer = self.BytesIO(self.testdata)
|
buffer = self.BytesIO(self.testdata)
|
||||||
|
|
|
@ -13,6 +13,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #5700: io.FileIO() called flush() after closing the file.
|
||||||
|
flush() was not called in close() if closefd=False.
|
||||||
|
|
||||||
- Issue #23374: Fixed pydoc failure with non-ASCII files when stdout encoding
|
- Issue #23374: Fixed pydoc failure with non-ASCII files when stdout encoding
|
||||||
differs from file system encoding (e.g. on Mac OS).
|
differs from file system encoding (e.g. on Mac OS).
|
||||||
|
|
||||||
|
|
|
@ -126,11 +126,18 @@ internal_close(fileio *self)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
fileio_close(fileio *self)
|
fileio_close(fileio *self)
|
||||||
{
|
{
|
||||||
|
PyObject *res;
|
||||||
|
PyObject *exc, *val, *tb;
|
||||||
|
int rc;
|
||||||
_Py_IDENTIFIER(close);
|
_Py_IDENTIFIER(close);
|
||||||
|
res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
|
||||||
|
&PyId_close, "O", self);
|
||||||
if (!self->closefd) {
|
if (!self->closefd) {
|
||||||
self->fd = -1;
|
self->fd = -1;
|
||||||
Py_RETURN_NONE;
|
return res;
|
||||||
}
|
}
|
||||||
|
if (res == NULL)
|
||||||
|
PyErr_Fetch(&exc, &val, &tb);
|
||||||
if (self->finalizing) {
|
if (self->finalizing) {
|
||||||
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
|
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
|
||||||
if (r)
|
if (r)
|
||||||
|
@ -138,12 +145,12 @@ fileio_close(fileio *self)
|
||||||
else
|
else
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
errno = internal_close(self);
|
rc = internal_close(self);
|
||||||
if (errno < 0)
|
if (res == NULL)
|
||||||
return NULL;
|
_PyErr_ChainExceptions(exc, val, tb);
|
||||||
|
if (rc < 0)
|
||||||
return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
|
Py_CLEAR(res);
|
||||||
&PyId_close, "O", self);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue