diff --git a/Lib/test/test_io/test_memoryio.py b/Lib/test/test_io/test_memoryio.py index 482b183da23ffa..3669ac0b038b71 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 00000000000000..b05c4222e30fcd --- /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 5debae5b42480b..9088a649878a48 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -225,7 +225,7 @@ write_str(stringio *self, PyObject *obj) if (self->state == STATE_ACCUMULATING) { if (self->string_size == self->pos) { - if (PyUnicodeWriter_WriteStr(self->writer, decoded)) + if (_PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)self->writer, decoded)) goto fail; goto success; }