From 22276e2a7f936124bf88da48dd8c647ce0705d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hilmi=20Tuna=20Say=C4=B1n?= Date: Tue, 3 Nov 2026 22:28:18 +0100 Subject: [PATCH] gh-161272: Fix off-by-one in _pyio.IOBase.readlines(hint) --- Lib/_pyio.py | 2 +- Lib/test/test_io/test_general.py | 18 ++++++++++++++++++ ...6-11-03-20-12-00.gh-issue-161272.Zr9Tb4.rst | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-11-03-20-12-00.gh-issue-161272.Zr9Tb4.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 370b518f1e5e5a..855610689bd552 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -576,7 +576,7 @@ def readlines(self, hint=None): for line in self: lines.append(line) n += len(line) - if n >= hint: + if n > hint: break return lines diff --git a/Lib/test/test_io/test_general.py b/Lib/test/test_io/test_general.py index c4d5a80086718b..11a9e6f91de52e 100644 --- a/Lib/test/test_io/test_general.py +++ b/Lib/test/test_io/test_general.py @@ -319,6 +319,24 @@ 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_readlines_hint(self): + # gh-161272: reading stops only once the total size of the lines + # read *exceeds* hint, not when it is equal to it. + all_lines = [b"a\n", b"bb\n", b"ccc\n", b"dddd\n"] + with self.open(os_helper.TESTFN, "wb") as f: + f.writelines(all_lines) + for hint, expected in ((1, all_lines[:1]), (2, all_lines[:2]), + (3, all_lines[:2]), (5, all_lines[:3]), + (9, all_lines), (100, all_lines), + (0, all_lines), (-1, all_lines)): + with self.subTest(hint=hint): + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.readlines(hint), expected) + with self.open(os_helper.TESTFN, "rb") as f: + self.assertEqual(f.readlines(), all_lines) + with self.open(os_helper.TESTFN, "r", encoding="ascii") as f: + self.assertEqual(f.readlines(2), ["a\n", "bb\n"]) + def test_readline_limit_with_short_read(self): class Reader(self.IOBase): data = b"abcdef" diff --git a/Misc/NEWS.d/next/Library/2026-11-03-20-12-00.gh-issue-161272.Zr9Tb4.rst b/Misc/NEWS.d/next/Library/2026-11-03-20-12-00.gh-issue-161272.Zr9Tb4.rst new file mode 100644 index 00000000000000..2a7417ad6c1a00 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-03-20-12-00.gh-issue-161272.Zr9Tb4.rst @@ -0,0 +1,5 @@ +Fix an off-by-one in the pure Python implementation of +:meth:`io.IOBase.readlines` (``_pyio``): it now stops reading only once the +total size of the lines read *exceeds* *hint*, as documented and as the C +implementation does. Previously one line fewer was returned when that total +was exactly *hint*.