Skip to content

Commit 8c28262

Browse files
[3.15] gh-159739: Respect readline limits after a short read (GH-159974) (#160246)
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 87eaef1) Co-authored-by: Jelle Zijlstra <jelle@openai.com>
1 parent dd66fb0 commit 8c28262

4 files changed

Lines changed: 21 additions & 2 deletions

File tree

‎Lib/_pyio.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ def nreadahead():
528528
return 1
529529
n = (readahead.find(b"\n") + 1) or len(readahead)
530530
if size >= 0:
531-
n = min(n, size)
531+
n = min(n, size - len(res))
532532
return n
533533
else:
534534
def nreadahead():

‎Lib/test/test_io/test_general.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,22 @@ def test_readline(self):
319319
with self.open(os_helper.TESTFN, "r", encoding="utf-8") as f:
320320
self.assertRaises(TypeError, f.readline, 5.3)
321321

322+
def test_readline_limit_with_short_read(self):
323+
class Reader(self.IOBase):
324+
data = b"abcdef"
325+
326+
def peek(self, size):
327+
return self.data
328+
329+
def read(self, size):
330+
data = self.data[:min(size, 2)]
331+
self.data = self.data[len(data):]
332+
return data
333+
334+
reader = Reader()
335+
self.assertEqual(reader.readline(3), b"abc")
336+
self.assertEqual(reader.data, b"def")
337+
322338
def test_readline_nonsizeable(self):
323339
# Issue #30061
324340
# Crash when readline() returns an object without __len__
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`io.IOBase.readline` returning more bytes than requested when a
2+
stream supports :meth:`!peek` and returns short reads.

‎Modules/_io/iobase.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,9 @@ _io__IOBase_readline_impl(PyObject *self, Py_ssize_t limit)
609609
Py_ssize_t n = 0;
610610
const char *buf = PyBytes_AS_STRING(readahead);
611611
if (limit >= 0) {
612+
Py_ssize_t remaining = limit - PyByteArray_GET_SIZE(buffer);
612613
do {
613-
if (n >= PyBytes_GET_SIZE(readahead) || n >= limit)
614+
if (n >= PyBytes_GET_SIZE(readahead) || n >= remaining)
614615
break;
615616
if (buf[n++] == '\n')
616617
break;

0 commit comments

Comments
 (0)