From: Thomas Kowalski Date: Mon, 1 Jun 2026 13:01:57 +0000 (+0200) Subject: gh-149046: fix: correctly handle `str` subclasses in `io.StringIO` (#149047) X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=c98773633c97463e35eb51fac81d1095e8061fe0;p=thirdparty%2FPython%2Fcpython.git gh-149046: fix: correctly handle `str` subclasses in `io.StringIO` (#149047) --- diff --git a/Lib/test/test_io/test_memoryio.py b/Lib/test/test_io/test_memoryio.py index 482b183da23f..3669ac0b038b 100644 --- a/Lib/test/test_io/test_memoryio.py +++ b/Lib/test/test_io/test_memoryio.py @@ -967,6 +967,25 @@ class CStringIOTest(PyStringIOTest): 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 000000000000..b05c4222e30f --- /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 0d9196f3647d..b8601383ad0a 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; }