Skip to content

Commit efaba28

Browse files
committed
gh-161271: Drop the read buffer in _pyio.BufferedRandom.truncate()
1 parent 9c37326 commit efaba28

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

‎Lib/_pyio.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1470,7 +1470,11 @@ def tell(self):
14701470
def truncate(self, pos=None):
14711471
if pos is None:
14721472
pos = self.tell()
1473-
# Use seek to flush the read buffer.
1473+
# Use seek to flush the read buffer and to rewind the raw stream, as
1474+
# _io's buffered_flush_and_rewind_unlocked() does. Without this a
1475+
# read after truncate() can still return data that was truncated
1476+
# away.
1477+
self.seek(self.tell())
14741478
return BufferedWriter.truncate(self, pos)
14751479

14761480
def read(self, size=None):

‎Lib/test/test_io/test_bufferedio.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,17 @@ def test_truncate_after_read_or_write(self):
14101410
self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases
14111411
self.assertEqual(bufio.truncate(), 4)
14121412

1413+
def test_read_after_truncate(self):
1414+
# gh-161271: truncate() must drop the read-ahead buffer, so that a
1415+
# read after it cannot return data that is no longer in the stream.
1416+
raw = self.BytesIO(b"A" * 10)
1417+
bufio = self.tp(raw, 100)
1418+
self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled
1419+
self.assertEqual(bufio.truncate(0), 0)
1420+
self.assertEqual(bufio.tell(), 2)
1421+
self.assertEqual(bufio.read(), b"")
1422+
self.assertEqual(raw.getvalue(), b"")
1423+
14131424
def test_misbehaved_io(self):
14141425
BufferedReaderTest.test_misbehaved_io(self)
14151426
BufferedWriterTest.test_misbehaved_io(self)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix the pure Python implementation of :class:`io.BufferedRandom` (``_pyio``):
2+
:meth:`!truncate` now drops the read-ahead buffer and rewinds the raw stream,
3+
as the C implementation does, so that a read after :meth:`!truncate` no longer
4+
returns data that was truncated away.

0 commit comments

Comments
 (0)