10000 Issue #5700: io.FileIO() called flush() after closing the file. · python/cpython@254dd59 · GitHub
[go: up one dir, main page]

Skip to content

Commit 254dd59

Browse files
Issue #5700: io.FileIO() called flush() after closing the file.
flush() was not called in close() if closefd=False.
2 parents 7065f37 + a3712a9 commit 254dd59

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

Lib/test/test_io.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,13 +589,43 @@ def test_unbounded_file(self):
589589
with self.open(zero, "r") as f:
590590
self.assertRaises(OverflowError, f.read)
591591

592-
def test_flush_error_on_close(self):
593-
f = self.open(support.TESTFN, "wb", buffering=0)
592+
def check_flush_error_on_close(self, *args, **kwargs):
593+
# Test that the file is closed despite failed flush
594+
# and that flush() is called before file closed.
595+
f = self.open(*args, **kwargs)
596+
closed = []
594597
def bad_flush():
598+
closed[:] = [f.closed]
595599
raise OSError()
596600
f.flush = bad_flush
597601
self.assertRaises(OSError, f.close) # exception not swallowed
598602
self.assertTrue(f.closed)
603+
self.assertTrue(closed) # flush() called
604+
self.assertFalse(closed[0]) # flush() called before file closed
605+
606+
def test_flush_error_on_close(self):
607+
# raw file
608+
# Issue #5700: io.FileIO calls flush() after file closed
609+
self.check_flush_error_on_close(support.TESTFN, 'wb', buffering=0)
610+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
611+
self.check_flush_error_on_close(fd, 'wb', buffering=0)
612+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
613+
self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False)
614+
os.close(fd)
615+
# buffered io
616+
self.check_flush_error_on_close(support.TESTFN, 'wb')
617+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
618+
self.check_flush_error_on_close(fd, 'wb')
619+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
620+
self.check_flush_error_on_close(fd, 'wb', closefd=False)
621+
os.close(fd)
622+
# text io
623+
self.check_flush_error_on_close(support.TESTFN, 'w')
624+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
625+
self.check_flush_error_on_close(fd, 'w')
626+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
627+
self.check_flush_error_on_close(fd, 'w', closefd=False)
628+
os.close(fd)
599629

600630
def test_multi_close(self):
601631
f = self.open(support.TESTFN, "wb", buffering=0)
@@ -784,13 +814,21 @@ def test_repr(self):
784814
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
785815

786816
def test_flush_error_on_close(self):
817+
# Test that buffered file is closed despite failed flush
818+
# and that flush() is called before file closed.
787819
raw = self.MockRawIO()
820+
closed = []
788821
def bad_flush():
822+
closed[:] = [b.closed, raw.closed]
789823
raise OSError()
790824
raw.flush = bad_flush
791825
b = self.tp(raw)
792826
self.assertRaises(OSError, b.close) # exception not swallowed
793827
self.assertTrue(b.closed)
828+
self.assertTrue(raw.closed)
829+
self.assertTrue(closed) # flush() called
830+
self.assertFalse(closed[0]) # flush() called before file closed
831+
self.assertFalse(closed[1])
794832

795833
def test_close_error_on_close(self):
796834
raw = self.MockRawIO()
@@ -2679,12 +2717,20 @@ def run(n):
26792717
self.assertEqual(content.count("Thread%03d\n" % n), 1)
26802718

26812719
def test_flush_error_on_close(self):
2720+
# Test that text file is closed despite failed flush
2721+
# and that flush() is called before file closed.
26822722
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
2723+
closed = []
26832724
def bad_flush():
2725+
closed[:] = [txt.closed, txt.buffer.closed]
26842726
raise OSError()
26852727
txt.flush = bad_flush
26862728
self.assertRaises(OSError, txt.close) # exception not swallowed
26872729
self.assertTrue(txt.closed)
2730+
self.assertTrue(txt.buffer.closed)
2731+
self.assertTrue(closed) # flush() called
2732+
self.assertFalse(closed[0]) # flush() called before file closed
2733+
self.assertFalse(closed[1])
26882734

26892735
def test_close_error_on_close(self):
26902736
buffer = self.BytesIO(self.testdata)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Core and Builtins
1313
Library
1414
-------
1515

16+
- Issue #5700: io.FileIO() called flush() after closing the file.
17+
flush() was not called in close() if closefd=False.
18+
1619
- Issue #23374: Fixed pydoc failure with non-ASCII files when stdout encoding
1720
differs from file system encoding (e.g. on Mac OS).
1821

Modules/_io/fileio.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,24 +127,31 @@ internal_close(fileio *self)
127127
static PyObject *
128128
fileio_close(fileio *self)
129129
{
130+
PyObject *res;
131+
PyObject *exc, *val, *tb;
132+
int rc;
130133
_Py_IDENTIFIER(close);
134+
res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
135+
&PyId_close, "O", self);
131136
if (!self->closefd) {
132137
self->fd = -1;
133-
Py_RETURN_NONE;
138+
return res;
134139
}
140+
if (res == NULL)
141+
PyErr_Fetch(&exc, &val, &tb);
135142
if (self->finalizing) {
136143
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
137144
if (r)
138145
Py_DECREF(r);
139146
else
140147
PyErr_Clear();
141148
}
142-
errno = internal_close(self);
143-
if (errno < 0)
144-
return NULL;
145-
146-
return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
147-
&PyId_close, "O", self);
149+
rc = internal_close(self);
150+
if (res == NULL)
151+
_PyErr_ChainExceptions(exc, val, tb);
152+
if (rc < 0)
153+
Py_CLEAR(res);
154+
return res;
148155
}
149156

150157
static PyObject *

0 commit comments

Comments
 (0)
0