GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-160427: Return None from TextIOWrapper.buffer once detached by scrieve · Pull Request #160467 · python/cpython

Conversation

@scrieve

@scrieve scrieve commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

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 reactions
@bedevere-app bedevere-app Bot added the awaiting review label Oct 23, 2026
@python-cla-bot

python-cla-bot Bot commented Oct 23, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot mentioned this pull request Oct 23, 2026
hasattr(stream, "buffer") fails with ValueError if buffer is detached #160427
Closed
@scrieve
pythongh-160427: Return None from TextIOWrapper.buffer once detached
0071853
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.
@scrieve
scrieve force-pushed the gh-160427-buffer-none branch from 7551b73 to 0071853 Compare October 23, 2026 19:14
@scrieve

scrieve commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

@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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cmaloney

cmaloney commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@scrieve

scrieve commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Fair. I will take the behaviour question to gh-160427 and hold this as a draft until a triager or developer settles it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@scrieve
scrieve marked this pull request as draft October 23, 2026 19:37
@bedevere-app bedevere-app Bot removed the awaiting review label Oct 23, 2026
@scrieve scrieve mentioned this pull request Oct 23, 2026
_pyio buffered classes raise AttributeError after detach() instead of ValueError #160472
Closed
@scrieve

scrieve commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@scrieve

scrieve commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@scrieve scrieve closed this Oct 24, 2026
@scrieve
scrieve deleted the gh-160427-buffer-none branch October 27, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cmaloney 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

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

hasattr(stream, "buffer") fails with ValueError if buffer is detached

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@scrieve @cmaloney
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.