Skip to content

_pyio.BufferedRandom.truncate() keeps the read buffer, so later reads return data that is no longer in the file #161271

Open

Description

@tsayin4

Bug report

Bug description

_io.BufferedRandom.truncate() flushes and rewinds the raw stream before truncating
(buffered_flush_and_rewind_unlocked() in Modules/_io/bufferedio.c), which drops the
read-ahead buffer. _pyio.BufferedRandom.truncate() does not: the comment there says
"Use seek to flush the read buffer", but nothing seeks, so reads after truncate()
still hand out bytes that are no longer in the file.

import io, _pyio, os

for mod in (io, _pyio):
    with open("x", "wb") as f:
        f.write(b"abcdef")
    with mod.open("x", "r+b") as f:
        f.read(1)
        f.truncate(0)
        print(mod.__name__, f.read(), os.path.getsize("x"))
io    b''       0
_pyio b'bcdef'  0     # the file is empty, yet read() returns 5 bytes

The same divergence appears with truncate() (no argument), with any truncation point
below the read-ahead position, in r+b / w+b / a+b modes, and for a
BufferedRandom built directly on a BytesIO.

CPython versions tested on

3.11, 3.13 — the affected code in Lib/_pyio.py is unchanged on main.

Operating systems tested on

macOS, but the code is platform independent.

Linked PRs

Activity

  1. cmaloney commented on Nov 3, 2026

    @cmaloney
    Contributor

    holding off on _pyio changes until python/steering-council#372 is decided.

  2. added
    AI-needs-explanationAI-assisted work that needs further explanation and follow up from the contributor
    on Nov 3, 2026
  3. bedevere-app commented on Nov 3, 2026

    @bedevere-app

    Thank you for taking the time to open this issue. Python is a community-driven project, and we want it to be a welcoming place to learn and collaborate.

    This issue appears to have been written with AI assistance. Please explain your issue in your own words and describe how you checked the information you provided. This helps us understand your issue and leaves more time to address other issues.

    Please read our guidelines for using AI tools.

  4. tsayin4 commented on Nov 3, 2026

    @tsayin4
    Author

    Fair enough, I hadn't seen python/steering-council#372 when I filed this. Happy for it to be closed if _pyio is on the way out.

    In my words: _io's truncate rewinds the raw stream and drops the read buffer first (buffered_flush_and_rewind_unlocked), _pyio doesn't, even though the comment right there says "Use seek to flush the read buffer". So the next read still hands back buffered bytes:

    open("x","wb").write(b"abcdef")
    f = _pyio.open("x","r+b"); f.read(1); f.truncate(0)
    f.read()      # b'bcdef', and the file is 0 bytes

    I checked it on r+b, ab+ and w+b with truncation points 0, 1, 3 and None, comparing the return value, tell(), the next read and the file size against io. 12 of 25 cases differed, 0 after adding self.seek(self.tell()). I used an LLM for the English.

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

    AI-needs-explanationAI-assisted work that needs further explanation and follow up from the contributor

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions