https://github.com/python/cpython/commit/998b89020456db591be41e6529b04f4bc8c8181f
commit: 998b89020456db591be41e6529b04f4bc8c8181f
branch: main
author: Cody Maloney <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-08T18:11:23+03:00
summary:

gh-111330: Fix GC of _pyio.BytesIO with exports (GH-152335)

Swap out the buffer rather than using `.clear()` which will raise an
exception if there are exports.

files:
A Misc/NEWS.d/next/Library/2026-06-26-13-00-46.gh-issue-111330.DBcCXA.rst
M Lib/_pyio.py
M Lib/test/test_io/test_memoryio.py

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 f53525d77b7dbaf..026dff23fe7ce24 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`.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to