From 7a0602fdf778d9369ed38c43b51b6fe8195150a3 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 22 Oct 2026 07:38:52 -0700 Subject: [PATCH] [3.14] gh-159739: Respect readline limits after a short read (GH-159974) Backport to 3.15 and 3.14. Both branches use a bytearray instead of PyBytesWriter; the test lives in Lib/test/test_io.py on 3.14. (cherry picked from commit 87eaef117191037cebce2becea5350ff4f45dee3) Co-authored-by: Jelle Zijlstra --- Lib/_pyio.py | 2 +- Lib/test/test_io.py | 16 ++++++++++++++++ ...026-10-20-00-00-00.gh-issue-159739.4c9783.rst | 2 ++ Modules/_io/iobase.c | 3 ++- 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-20-00-00-00.gh-issue-159739.4c9783.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index cc336d2939443cd..01fd821fb0049c8 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -528,7 +528,7 @@ def nreadahead(): return 1 n = (readahead.find(b"\n") + 1) or len(readahead) if size >= 0: - n = min(n, size) + n = min(n, size - len(res)) return n else: def nreadahead(): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index d11e66e94b49708..cbec18fb4036327 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -624,6 +624,22 @@ def test_readline(self): with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f: self.assertRaises(TypeError, f.readline, 5.3) + def test_readline_limit_with_short_read(self): + class Reader(self.IOBase): + data = b"abcdef" + + def peek(self, size): + return self.data + + def read(self, size): + data = self.data[:min(size, 2)] + self.data = self.data[len(data):] + return data + + reader = Reader() + self.assertEqual(reader.readline(3), b"abc") + self.assertEqual(reader.data, b"def") + def test_readline_nonsizeable(self): # Issue #30061 # Crash when readline() returns an object without __len__ diff --git a/Misc/NEWS.d/next/Library/2026-10-20-00-00-00.gh-issue-159739.4c9783.rst b/Misc/NEWS.d/next/Library/2026-10-20-00-00-00.gh-issue-159739.4c9783.rst new file mode 100644 index 000000000000000..b65ac4cede106bf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-20-00-00-00.gh-issue-159739.4c9783.rst @@ -0,0 +1,2 @@ +Fix :meth:`io.IOBase.readline` returning more bytes than requested when a +stream supports :meth:`!peek` and returns short reads. diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index e291d7cfc4710f8..5cbd28ca136b4b9 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -609,8 +609,9 @@ _io__IOBase_readline_impl(PyObject *self, Py_ssize_t limit) Py_ssize_t n = 0; const char *buf = PyBytes_AS_STRING(readahead); if (limit >= 0) { + Py_ssize_t remaining = limit - PyByteArray_GET_SIZE(buffer); do { - if (n >= PyBytes_GET_SIZE(readahead) || n >= limit) + if (n >= PyBytes_GET_SIZE(readahead) || n >= remaining) break; if (buf[n++] == '\n') break;