Skip to content

_pyio.BytesIO.readinto() advances the stream when passed a read-only buffer #158952

Description

@emerardd

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-IOtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions