Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_io/test_bufferedio.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading