gh-138720: Make Buffered closed check match flush (GH-138724)

In `_io__Buffered_flush_impl` the macro `CHECK_CLOSED` is used to check
the `buffered*` is in a good state to be flushed. That differs slightly
from `buffered_closed`.

In some cases, that difference would result in `close()` thinking the
file needed to be flushed and closed while `flush()` thought the file
was already closed.

This could happen during GC and would result in an unraisable exception.
This commit is contained in:
Cody Maloney 2025-09-18 03:02:29 -07:00 committed by GitHub
parent 9f7bbafffe
commit db68bfc771
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 10 additions and 2 deletions

View file

@ -1980,6 +1980,10 @@ class BufferedRWPairTest:
self.assertEqual(getattr(pair, method)(data), 5)
self.assertEqual(bytes(data), b"abcde")
# gh-138720: C BufferedRWPair would destruct in a bad order resulting in
# an unraisable exception.
support.gc_collect()
def test_write(self):
w = self.MockRawIO()
pair = self.tp(self.MockRawIO(), w)

View file

@ -0,0 +1,4 @@
Fix an issue where :class:`io.BufferedWriter` and :class:`io.BufferedRandom`
had different definitions of "closed" for :meth:`~io.IOBase.close` and
:meth:`~io.IOBase.flush` which resulted in an exception when close called
flush but flush thought the file was already closed.

View file

@ -553,8 +553,8 @@ _io__Buffered_close_impl(buffered *self)
if (!ENTER_BUFFERED(self)) {
return NULL;
}
r = buffered_closed(self);
/* gh-138720: Use IS_CLOSED to match flush CHECK_CLOSED. */
r = IS_CLOSED(self);
if (r < 0)
goto end;
if (r > 0) {