https://github.com/python/cpython/commit/c61307222e18bfa06691d53e3ad336c46f432de0
commit: c61307222e18bfa06691d53e3ad336c46f432de0
branch: main
author: Stan Ulbrych <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-06-24T12:47:52+01:00
summary:
gh-151814: Fix unbounded memory growth from repeated empty writes to
`io.TextIOWrapper` (#151817)
files:
A Misc/NEWS.d/next/Library/2026-06-20-21-15-13.gh-issue-151814.OIbgsO.rst
M Modules/_io/textio.c
diff --git
a/Misc/NEWS.d/next/Library/2026-06-20-21-15-13.gh-issue-151814.OIbgsO.rst
b/Misc/NEWS.d/next/Library/2026-06-20-21-15-13.gh-issue-151814.OIbgsO.rst
new file mode 100644
index 000000000000000..1365fb4d8edb1d2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-20-21-15-13.gh-issue-151814.OIbgsO.rst
@@ -0,0 +1,2 @@
+Fix unbounded memory growth in :class:`io.TextIOWrapper` when repeatedly
+writing an empty string.
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 24e08cec88f2a3a..5b2a20a30c28cb2 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -1820,32 +1820,38 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject
*text)
}
}
- if (self->pending_bytes == NULL) {
- assert(self->pending_bytes_count == 0);
- self->pending_bytes = b;
- }
- else if (!PyList_CheckExact(self->pending_bytes)) {
- PyObject *list = PyList_New(2);
- if (list == NULL) {
+ if (bytes_len > 0) {
+ if (self->pending_bytes == NULL) {
+ assert(self->pending_bytes_count == 0);
+ self->pending_bytes = b;
+ }
+ else if (!PyList_CheckExact(self->pending_bytes)) {
+ PyObject *list = PyList_New(2);
+ if (list == NULL) {
+ Py_DECREF(b);
+ return NULL;
+ }
+ // Since Python 3.12, allocating GC object won't trigger GC and
release
+ // GIL. See https://github.com/python/cpython/issues/97922
+ assert(!PyList_CheckExact(self->pending_bytes));
+ PyList_SET_ITEM(list, 0, self->pending_bytes);
+ PyList_SET_ITEM(list, 1, b);
+ self->pending_bytes = list;
+ }
+ else {
+ if (PyList_Append(self->pending_bytes, b) < 0) {
+ Py_DECREF(b);
+ return NULL;
+ }
Py_DECREF(b);
- return NULL;
}
- // Since Python 3.12, allocating GC object won't trigger GC and release
- // GIL. See https://github.com/python/cpython/issues/97922
- assert(!PyList_CheckExact(self->pending_bytes));
- PyList_SET_ITEM(list, 0, self->pending_bytes);
- PyList_SET_ITEM(list, 1, b);
- self->pending_bytes = list;
+
+ self->pending_bytes_count += bytes_len;
}
else {
- if (PyList_Append(self->pending_bytes, b) < 0) {
- Py_DECREF(b);
- return NULL;
- }
Py_DECREF(b);
}
- self->pending_bytes_count += bytes_len;
if (self->pending_bytes_count >= self->chunk_size || needflush ||
text_needflush) {
if (_textiowrapper_writeflush(self) < 0)
_______________________________________________
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]