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

Skip to content

Commit a3712a9

Browse files
Issue #5700: io.FileIO() called flush() after closing the file.
flush() was not called in close() if closefd=False.
1 parent 5e3d7a4 commit a3712a9

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
@@ -593,13 +593,43 @@ def test_unbounded_file(self):
593593
with self.open(zero, "r") as f:
594594
self.assertRaises(OverflowError, f.read)
595595

596-
def test_flush_error_on_close(self):
597-
f = self.open(support.TESTFN, "wb", buffering=0)
596+
def check_flush_error_on_close(self, *args, **kwargs):
597+
# Test that the file is closed despite failed flush
598+
# and that flush() is called before file closed.
599+
f = self.open(*args, **kwargs)
600+
closed = []
598601
def bad_flush():
602+
closed[:] = [f.closed]
599603
raise OSError()
600604
f.flush = bad_flush
601605
self.assertRaises(OSError, f.close) # exception not swallowed
602606
self.assertTrue(f.closed)
607+
self.assertTrue(closed) # flush() called
608+
self.assertFalse(closed[0]) # flush() called before file closed
609+
610+
def test_flush_error_on_close(self):
611+
# raw file
612+
# Issue #5700: io.FileIO calls flush() after file closed
613+
self.check_flush_error_on_close(support.TESTFN, 'wb', buffering=0)
614+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
615+
self.check_flush_error_on_close(fd, 'wb', buffering=0)
616+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
617+
self.check_flush_error_on_close(fd, 'wb', buffering=0, closefd=False)
618+
os.close(fd)
619+
# buffered io
620+
self.check_flush_error_on_close(support.TESTFN, 'wb')
621+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
622+
self.check_flush_error_on_close(fd, 'wb')
623+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
624+
self.check_flush_error_on_close(fd, 'wb', closefd=False)
625+
os.close(fd)
626+
# text io
627+
self.check_flush_error_on_close(support.TESTFN, 'w')
628+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
629+
self.check_flush_error_on_close(fd, 'w')
630+
fd = os.open(support.TESTFN, os.O_WRONLY|os.O_CREAT)
631+
self.check_flush_error_on_close(fd, 'w', closefd=False)
632+
os.close(fd)
603633

604634
def test_multi_close(self):
605635
f = self.open(support.TESTFN, "wb", buffering=0)
@@ -788,13 +818,21 @@ def test_repr(self):
788818
self.assertEqual(repr(b), "<%s name=b'dummy'>" % clsname)
789819

790820
def test_flush_error_on_close(self):
821+
# Test that buffered file is closed despite failed flush
822+
# and that flush() is called before file closed.
791823
raw = self.MockRawIO()
824+
closed = []
792825
def bad_flush():
826+
closed[:] = [b.closed, raw.closed]
793827
raise OSError()
794828
raw.flush = bad_flush
795829
b = self.tp(raw)
796830
self.assertRaises(OSError, b.close) # exception not swallowed
797831
self.assertTrue(b.closed)
832+
self.assertTrue(raw.closed)
833+
self.assertTrue(closed) # flush() called
834+
self.assertFalse(closed[0]) # flush() called before file closed
835+
self.assertFalse(closed[1])
798836

799837
def test_close_error_on_close(self):
800838
raw = self.MockRawIO()
@@ -2618,12 +2656,20 @@ def run(n):
26182656
self.assertEqual(content.count("Thread%03d\n" % n), 1)
26192657

26202658
def test_flush_error_on_close(self):
2659+
# Test that text file is closed despite failed flush
2660+
# and that flush() is called before file closed.
26212661
txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
2662+
closed = []
26222663
def bad_flush():
2664+
closed[:] = [txt.closed, txt.buffer.closed]
26232665
raise OSError()
26242666
txt.flush = bad_flush
26252667
self.assertRaises(OSError, txt.close) # exception not swallowed
26262668
self.assertTrue(txt.closed)
2669+
self.assertTrue(txt.buffer.closed)
2670+
self.assertTrue(closed) # flush() called
2671+
self.assertFalse(closed[0]) # flush() called before file closed
2672+
self.assertFalse(closed[1])
26272673

26282674
def test_close_error_on_close(self):
26292675
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
@@ -126,24 +126,31 @@ internal_close(fileio *self)
126126
static PyObject *
127127
fileio_close(fileio *self)
128128
{
129+
PyObject *res;
130+
PyObject *exc, *val, *tb;
131+
int rc;
129132
_Py_IDENTIFIER(close);
133+
res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
134+
&PyId_close, "O", self);
130135
if (!self->closefd) {
131136
self->fd = -1;
132-
Py_RETURN_NONE;
137+
return res;
133138
}
139+
if (res == NULL)
140+
PyErr_Fetch(&exc, &val, &tb);
134141
if (self->finalizing) {
135142
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
136143
if (r)
137144
Py_DECREF(r);
138145
else
139146
PyErr_Clear();
140147
}
141-
errno = internal_close(self);
142-
if (errno < 0)
143-
return NULL;
144-
145-
return _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
146-
&PyId_close, "O", self);
148+
rc = internal_close(self);
149+
if (res == NULL)
150+
_PyErr_ChainExceptions(exc, val, tb);
151+
if (rc < 0)
152+
Py_CLEAR(res);
153+
return res;
147154
}
148155

149156
static PyObject *

0 commit comments

Comments
 (0)
0