Bug description
The pure Python implementation of BytesIO.readinto() and readinto1() consumes input before rejecting a read-only destination buffer. The C implementation rejects the same argument without advancing the stream.
import io
import _pyio
for module in (io, _pyio):
stream = module.BytesIO(b"abcdef")
try:
stream.readinto(b"xxx")
except TypeError:
print(module.__name__, stream.tell(), stream.read())
Output on main:
io 0 b'abcdef'
_pyio 3 b'def'
The same happens with readinto1() and with a read-only memoryview. The destination is invalid because readinto() requires a writable bytes-like object, but rejecting it should not consume the input first.
The cause is in _pyio.BufferedIOBase._readinto(): it calls self.read() or self.read1() before the assignment to the destination memoryview raises TypeError. BytesIO inherits this implementation. Checking the memoryview's readonly flag before reading avoids changing the stream state and matches the C implementation.
I have prepared a focused fix and a regression test shared by the C and pure Python BytesIO implementations. The test covers both methods, bytes and memoryview destinations, empty destinations, and positions at the beginning, near EOF, and beyond EOF. On the original implementation the C test passes and the Python test fails eight subcases because the stream position changes. Both pass with the fix, and the full test_io package passes locally.
CPython versions tested on
Main at 2639fd65ff8e0c1949c480a8e670fe9c2467a1f8, locally built CPython 3.16.0a0 debug.
Operating systems tested on
Windows x64.
Linked PRs
Bug description
The pure Python implementation of
BytesIO.readinto()andreadinto1()consumes input before rejecting a read-only destination buffer. The C implementation rejects the same argument without advancing the stream.Output on main:
The same happens with
readinto1()and with a read-onlymemoryview. The destination is invalid becausereadinto()requires a writable bytes-like object, but rejecting it should not consume the input first.The cause is in
_pyio.BufferedIOBase._readinto(): it callsself.read()orself.read1()before the assignment to the destination memoryview raisesTypeError.BytesIOinherits this implementation. Checking the memoryview'sreadonlyflag before reading avoids changing the stream state and matches the C implementation.I have prepared a focused fix and a regression test shared by the C and pure Python BytesIO implementations. The test covers both methods, bytes and memoryview destinations, empty destinations, and positions at the beginning, near EOF, and beyond EOF. On the original implementation the C test passes and the Python test fails eight subcases because the stream position changes. Both pass with the fix, and the full
test_iopackage passes locally.CPython versions tested on
Main at
2639fd65ff8e0c1949c480a8e670fe9c2467a1f8, locally built CPython 3.16.0a0 debug.Operating systems tested on
Windows x64.
Linked PRs