https://github.com/python/cpython/commit/c98773633c97463e35eb51fac81d1095e8061fe0
commit: c98773633c97463e35eb51fac81d1095e8061fe0
branch: main
author: Thomas Kowalski <[email protected]>
committer: vstinner <[email protected]>
date: 2026-06-01T13:01:57Z
summary:

gh-149046: fix: correctly handle `str` subclasses in `io.StringIO` (#149047)

files:
A Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst
M Lib/test/test_io/test_memoryio.py
M Modules/_io/stringio.c

diff --git a/Lib/test/test_io/test_memoryio.py 
b/Lib/test/test_io/test_memoryio.py
index 482b183da23ffa2..3669ac0b038b71b 100644
--- a/Lib/test/test_io/test_memoryio.py
+++ b/Lib/test/test_io/test_memoryio.py
@@ -967,6 +967,25 @@ def test_setstate(self):
         memio.close()
         self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, 
None))
 
+    def test_write_str_subclass(self):
+        # Writing a str subclass should use the subclass's unicode data
+        # directly, not call __str__ on it (which may return a different
+        # value).  gh-149047
+        class MyStr(str):
+            def __str__(self):
+                return "WRONG"
+
+        s = MyStr("correct")
+        memio = self.ioclass()
+        memio.write(s)
+        self.assertEqual(memio.getvalue(), "correct")
+
+        # Also test the fast path where pos == string_size (STATE_ACCUMULATING)
+        memio2 = self.ioclass()
+        memio2.write(MyStr("hello "))
+        memio2.write(MyStr("world"))
+        self.assertEqual(memio2.getvalue(), "hello world")
+
 
 class CStringIOPickleTest(PyStringIOPickleTest):
     UnsupportedOperation = io.UnsupportedOperation
diff --git 
a/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst 
b/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst
new file mode 100644
index 000000000000000..b05c4222e30fcd2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-04-27-11-12-00.gh-issue-149046.74shDd.rst
@@ -0,0 +1,2 @@
+:mod:`io`: Fix :class:`io.StringIO` serialization: no longer call ``str(obj)`` 
on :class:`str`
+subclasses. Patch by Thomas Kowalski.
diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c
index 0d9196f3647dde8..b8601383ad0a26f 100644
--- a/Modules/_io/stringio.c
+++ b/Modules/_io/stringio.c
@@ -225,7 +225,9 @@ write_str(stringio *self, PyObject *obj)
 
     if (self->state == STATE_ACCUMULATING) {
         if (self->string_size == self->pos) {
-            if (PyUnicodeWriter_WriteStr(self->writer, decoded))
+            // gh-149046: Avoid PyUnicodeWriter_WriteStr() which calls str(obj)
+            // on str subclasses
+            if (_PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)self->writer, 
decoded))
                 goto fail;
             goto success;
         }

_______________________________________________
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