From efaba28dfcbc26fc84e7747c0aa5109e66b9e11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hilmi=20Tuna=20Say=C4=B1n?= Date: Tue, 3 Nov 2026 22:28:10 +0100 Subject: [PATCH] gh-161271: Drop the read buffer in _pyio.BufferedRandom.truncate() --- Lib/_pyio.py | 6 +++++- Lib/test/test_io/test_bufferedio.py | 11 +++++++++++ .../2026-11-03-20-10-00.gh-issue-161271.Qk2Lm7.rst | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-11-03-20-10-00.gh-issue-161271.Qk2Lm7.rst diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 370b518f1e5e5a6..2be127985066b66 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1470,7 +1470,11 @@ def tell(self): def truncate(self, pos=None): if pos is None: pos = self.tell() - # Use seek to flush the read buffer. + # Use seek to flush the read buffer and to rewind the raw stream, as + # _io's buffered_flush_and_rewind_unlocked() does. Without this a + # read after truncate() can still return data that was truncated + # away. + self.seek(self.tell()) return BufferedWriter.truncate(self, pos) def read(self, size=None): diff --git a/Lib/test/test_io/test_bufferedio.py b/Lib/test/test_io/test_bufferedio.py index e83dd0d4e28d006..64ae4718be4f42a 100644 --- a/Lib/test/test_io/test_bufferedio.py +++ b/Lib/test/test_io/test_bufferedio.py @@ -1410,6 +1410,17 @@ def test_truncate_after_read_or_write(self): self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases self.assertEqual(bufio.truncate(), 4) + def test_read_after_truncate(self): + # gh-161271: truncate() must drop the read-ahead buffer, so that a + # read after it cannot return data that is no longer in the stream. + raw = self.BytesIO(b"A" * 10) + bufio = self.tp(raw, 100) + self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled + self.assertEqual(bufio.truncate(0), 0) + self.assertEqual(bufio.tell(), 2) + self.assertEqual(bufio.read(), b"") + self.assertEqual(raw.getvalue(), b"") + def test_misbehaved_io(self): BufferedReaderTest.test_misbehaved_io(self) BufferedWriterTest.test_misbehaved_io(self) diff --git a/Misc/NEWS.d/next/Library/2026-11-03-20-10-00.gh-issue-161271.Qk2Lm7.rst b/Misc/NEWS.d/next/Library/2026-11-03-20-10-00.gh-issue-161271.Qk2Lm7.rst new file mode 100644 index 000000000000000..619c88af7c043a3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-03-20-10-00.gh-issue-161271.Qk2Lm7.rst @@ -0,0 +1,4 @@ +Fix the pure Python implementation of :class:`io.BufferedRandom` (``_pyio``): +:meth:`!truncate` now drops the read-ahead buffer and rewinds the raw stream, +as the C implementation does, so that a read after :meth:`!truncate` no longer +returns data that was truncated away.