You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
`hasattr(stream, "buffer") fails with ValueError if buffer is detached #160427
This occurs with Python 3.14.9 and 3.15.1, but not with earlier versions such as 3.14.8 and 3.15.0. Tested both on Linux and on Windows. I consider hasattr failing with an exception pretty bad, especially when using it with standard library objects, and the behavior changing in a bug fix release makes it even worse.
I noticed this when a unit test for our custom isatty utility started to fail (robotframework/robotframework#5825). We have that utility so that we can safely check is a stream connected to console or not. It used to look like this:
defisatty(stream):
# first check if buffer was detachedifhasattr(stream, "buffer") andstream.bufferisNone:
returnFalseifnothasattr(stream, "isatty"):
returnFalsetry:
returnstream.isatty()
exceptValueError: # Occurs if file is closed.returnFalse
I already fixed this in our end by simply removing the separate detached buffer check. stream.isatty() raised a ValueError if its buffer is detached, similarly when the stream is closed, and we already handle that. Apparently the separate check was added before handling closed streams and nobody noticed earlier that the check is redundant.
I would like to settle the intended behaviour here before asking anyone to review a patch.
The case for None:
In 3.14.8 buffer was a member descriptor (_Py_T_OBJECT), so a detached wrapper read as None and hasattr() did not raise.
_pyio.TextIOWrapper.buffer still returns None after detach, so the two implementations disagree today.
Nothing in the io docs says the attribute raises. The nearest statement is that the TextIOBase is unusable after detach, which reads as being about operations.
The case for the current ValueError is that gh-157364 deliberately routed the attribute through buffer_access_safe() while closing a use-after-free. That guard is still needed for reads and writes; the open question is only whether the attribute getter is the right place to raise it.
Proposal: the getter returns None when the wrapper is detached but initialised, and keeps raising ValueError for the uninitialised case. Every other caller keeps the existing guard, so nothing that gh-157364 protected changes.
If a triager or developer would rather keep the ValueError, that is a fine answer and I will drop the patch. A patch against this proposal is at #160467, held as a draft until there is agreement here.
FWIW, as a user I'd expect hasattr (and isinstance and other such functions) to return a Boolean and never raise. I know this depends on the underlying type, but I'd expect this to hold at least with the standard library types.
Activity
pythongh-160427: Return None from TextIOWrapper.buffer once detached
wlkr73 commentedon Oct 23, 2026
I would like to settle the intended behaviour here before asking anyone to review a patch.
The case for
None:bufferwas a member descriptor (_Py_T_OBJECT), so a detached wrapper read asNoneandhasattr()did not raise._pyio.TextIOWrapper.bufferstill returnsNoneafter detach, so the two implementations disagree today.iodocs says the attribute raises. The nearest statement is that theTextIOBaseis unusable after detach, which reads as being about operations.The case for the current
ValueErroris that gh-157364 deliberately routed the attribute throughbuffer_access_safe()while closing a use-after-free. That guard is still needed for reads and writes; the open question is only whether the attribute getter is the right place to raise it.Proposal: the getter returns
Nonewhen the wrapper is detached but initialised, and keeps raisingValueErrorfor the uninitialised case. Every other caller keeps the existing guard, so nothing that gh-157364 protected changes.If a triager or developer would rather keep the
ValueError, that is a fine answer and I will drop the patch. A patch against this proposal is at #160467, held as a draft until there is agreement here.cmaloney commentedon Oct 24, 2026
cc: @vstinner gh-157364 backports made an API change. Revert or I can work next week on a new PR to fix?
pekkaklarck commentedon Oct 24, 2026
FWIW, as a user I'd expect
hasattr(andisinstanceand other such functions) to return a Boolean and never raise. I know this depends on the underlying type, but I'd expect this to hold at least with the standard library types.