diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 1118b54633b7cc1..ac301180d284fa9 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -921,7 +921,7 @@ def getbuffer(self): def close(self): if self._buffer is not None: - self._buffer.clear() + self._buffer = bytearray() super().close() def read(self, size=-1): diff --git a/Lib/test/test_io/test_memoryio.py b/Lib/test/test_io/test_memoryio.py index 5a93d2634580461..e44e9a6633e05dc 100644 --- a/Lib/test/test_io/test_memoryio.py +++ b/Lib/test/test_io/test_memoryio.py @@ -457,7 +457,9 @@ def test_getbuffer(self): # raises a BufferError. self.assertRaises(BufferError, memio.write, b'x' * 100) self.assertRaises(BufferError, memio.truncate) - self.assertRaises(BufferError, memio.close) + # gh-111049: _io.BytesIO detach on close would lead to corruption. + if self.ioclass is io.BytesIO: + self.assertRaises(BufferError, memio.close) self.assertFalse(memio.closed) # Mutating the buffer updates the BytesIO buf[3:6] = b"abc" @@ -471,6 +473,23 @@ def test_getbuffer(self): memio.close() self.assertRaises(ValueError, memio.getbuffer) + def test_getbuffer_delete(self): + # gh-111330: _pyio .close() works and the buffer stays working + if self.ioclass is io.BytesIO: + # gh-111049: _io.BytesIO detach on close would lead to corruption. + # gh-111331: It would be nice to support this. + self.skipTest("io.BytesIO does not support, gh-111049") + + memio = self.ioclass(b"1234567890") + buf = memio.getbuffer() + self.assertEqual(bytes(buf), b"1234567890") + memio.close() + self.assertTrue(memio.closed) + self.assertEqual(bytes(buf), b"1234567890") + buf[3:6] = b"abc" + self.assertEqual(bytes(buf), b"123abc7890") + self.assertRaises(ValueError, memio.getbuffer) + def test_getbuffer_empty(self): memio = self.ioclass() buf = memio.getbuffer() @@ -493,9 +512,11 @@ def test_getbuffer_gc_collect(self): # Create a reference loop. a = [buf] a.append(a) - # The Python implementation emits an unraisable exception. - with support.catch_unraisable_exception(): + + # gh-111330: _pyio GC with exports should pass. + with support.catch_unraisable_exception() as cm: del memio + self.assertIsNone(cm.unraisable) del buf del a # The C implementation emits an unraisable exception. diff --git a/Misc/NEWS.d/next/Library/2026-06-26-13-00-46.gh-issue-111330.DBcCXA.rst b/Misc/NEWS.d/next/Library/2026-06-26-13-00-46.gh-issue-111330.DBcCXA.rst new file mode 100644 index 000000000000000..597cd8d526da588 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-26-13-00-46.gh-issue-111330.DBcCXA.rst @@ -0,0 +1,2 @@ +Update pure-Python :class:`io.BytesIO` to close cleanly when the data has an +export such as a :class:`memoryview`.