GeezerVison is showing github.com — gh-160427: Return None from TextIOWrapper.buffer once detached by scrieve · Pull Request #160467 · python/cpython
Conversation
Fixes gh-160427.
TextIOWrapper.buffer raises ValueError: underlying buffer has been detached
once the wrapper is detached, so hasattr(stream, "buffer") raises instead of
returning a boolean. This is a regression in 3.14.9 and 3.15.1, introduced by
gh-157364.
That change replaced the buffer member descriptor with a getter that calls
buffer_access_safe(), which raises whenever self->buffer is NULL. Before
gh-157364 the attribute was exposed as
{"buffer", _Py_T_OBJECT, offsetof(textio, buffer), Py_READONLY}, and a NULL
object member reads as None. _pyio.TextIOWrapper.buffer still returns
self._buffer, which is None after detach, so the two implementations now
disagree.
The fix returns None from the getter when the wrapper is detached but
initialised. buffer_access_safe() is untouched, so every other caller keeps
the existing protection, and the uninitialised case still raises.
One existing test needed updating: test_reentrant_detach_during_read asserted
the ValueError from wrapper.buffer. Its subject is that the buffer survives
the read and the wrapper does not crash, which still holds, so it now asserts
wrapper.buffer is None.
Tests: ./python -m test test_io passes, 1,066 tests. test_detach now also
asserts the attribute is None and that hasattr works after detach.
I used an AI assistant to help trace the regression and prepare the patch, and
I have reviewed every line of it.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
All commit authors signed the Contributor License Agreement.

|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
pythongh-157364 replaced the buffer member descriptor with a getter that goes
through buffer_access_safe(), which raises ValueError when the buffer is NULL.
The attribute therefore raised instead of returning None after detach(),
breaking hasattr() in 3.14.9 and 3.15.1 for code that probes a stream for a
buffer.
Return None from the getter when the wrapper is detached but initialised, which
is what the member descriptor did before pythongh-157364 and what _pyio returns. The
safety helper keeps raising for every other caller.
|
@ashm-dev, one thing worth a look from you: I changed an assertion in test_reentrant_detach_during_read, which came from #157370. It asserted the ValueError from wrapper.buffer; the test's subject is that the buffer survives the read and the wrapper does not crash, which still passes, so it now asserts wrapper.buffer is None.
Before gh-157364 the attribute was a member descriptor, so a detached wrapper read as None, and _pyio still returns None. If you would rather keep that test untouched, I can restore the old assertion and move the None expectation somewhere else, but the two behaviours cannot both hold.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Need to work out what the right behavior here is before getting to a PR. Ideally get an agreement in the issue first, then once have that can work on a PR. In this case that means propose the change you'd like to make there and wait for a python triager or developer to figure out if that is the right direction.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Fair. I will take the behaviour question to gh-160427 and hold this as a draft until a triager or developer settles it.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Noted on gh-160472 being closed as a duplicate, and the parity point: when the behaviour question on gh-160427 is settled, the _pyio half will go into this same PR so _io and _pyio land together rather than as two changes.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Closing this draft: gh-160427 is still undecided, and cmaloney has raised reverting gh-157364, which would remove the case entirely. The branch stays on my fork and goes into a fresh PR if a fix is still wanted once a triager or developer settles it.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Reviewers
cmaloney
Awaiting requested review from cmaloney
cmaloney will be requested when the pull request is marked ready for review
cmaloney is a code owner
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|