gh-159739: Respect readline limits after a short read (#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.
This commit is contained in:
Jelle Zijlstra authored and GitHub committed 2026-10-22 07:38:52 -07:00
1 parent d36a3f6970
commit 87eaef1171
4 files changed
+21 -2

No files matched your search

+1 -1
View File
@@ -528,7 +528,7 @@ class IOBase(metaclass=abc.ABCMeta):
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():
+16
View File
@@ -319,6 +319,22 @@ class IOTest:
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__
@@ -0,0 +1,2 @@
Fix :meth:`io.IOBase.readline` returning more bytes than requested when a
stream supports :meth:`!peek` and returns short reads.
+2 -1
View File
@@ -653,8 +653,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 - PyBytesWriter_GetSize(writer);
do {
if (n >= PyBytes_GET_SIZE(readahead) || n >= limit)
if (n >= PyBytes_GET_SIZE(readahead) || n >= remaining)
break;
if (buf[n++] == '\n')
break;