Skip to content

Commit 22276e2

Browse files
committed
gh-161272: Fix off-by-one in _pyio.IOBase.readlines(hint)
1 parent 9c37326 commit 22276e2

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎Lib/_pyio.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def readlines(self, hint=None):
576576
for line in self:
577577
lines.append(line)
578578
n += len(line)
579-
if n >= hint:
579+
if n > hint:
580580
break
581581
return lines
582582

‎Lib/test/test_io/test_general.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,24 @@ 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_readlines_hint(self):
323+
# gh-161272: reading stops only once the total size of the lines
324+
# read *exceeds* hint, not when it is equal to it.
325+
all_lines = [b"a\n", b"bb\n", b"ccc\n", b"dddd\n"]
326+
with self.open(os_helper.TESTFN, "wb") as f:
327+
f.writelines(all_lines)
328+
for hint, expected in ((1, all_lines[:1]), (2, all_lines[:2]),
329+
(3, all_lines[:2]), (5, all_lines[:3]),
330+
(9, all_lines), (100, all_lines),
331+
(0, all_lines), (-1, all_lines)):
332+
with self.subTest(hint=hint):
333+
with self.open(os_helper.TESTFN, "rb") as f:
334+
self.assertEqual(f.readlines(hint), expected)
335+
with self.open(os_helper.TESTFN, "rb") as f:
336+
self.assertEqual(f.readlines(), all_lines)
337+
with self.open(os_helper.TESTFN, "r", encoding="ascii") as f:
338+
self.assertEqual(f.readlines(2), ["a\n", "bb\n"])
339+
322340
def test_readline_limit_with_short_read(self):
323341
class Reader(self.IOBase):
324342
data = b"abcdef"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix an off-by-one in the pure Python implementation of
2+
:meth:`io.IOBase.readlines` (``_pyio``): it now stops reading only once the
3+
total size of the lines read *exceeds* *hint*, as documented and as the C
4+
implementation does. Previously one line fewer was returned when that total
5+
was exactly *hint*.

0 commit comments

Comments
 (0)