diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 6514af8b1253c3..57b42fbb65fe10 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4194,6 +4194,22 @@ def write(self, data): self.assertEqual([b"abcdef", b"middle", b"g"*chunk_size], buf._write_stack) + def test_issue142594(self): + wrapper = None + detached = False + class ReentrantRawIO(self.RawIOBase): + @property + def closed(self): + nonlocal detached + if wrapper is not None and not detached: + detached = True + wrapper.detach() + return False + + raw = ReentrantRawIO() + wrapper = self.TextIOWrapper(raw) + wrapper.close() # should not crash + class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio diff --git a/Misc/NEWS.d/next/Library/2025-12-14-18-30-48.gh-issue-142594.belDmD.rst b/Misc/NEWS.d/next/Library/2025-12-14-18-30-48.gh-issue-142594.belDmD.rst new file mode 100644 index 00000000000000..ee6a958933f7c8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-14-18-30-48.gh-issue-142594.belDmD.rst @@ -0,0 +1,2 @@ +Fix crash in ``TextIOWrapper.close()`` when the underlying buffer's +``closed`` property calls :meth:`~io.TextIOBase.detach`. diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index a136987fb52a4a..9945febea80828 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -3149,6 +3149,9 @@ _io_TextIOWrapper_close_impl(textio *self) if (r > 0) { Py_RETURN_NONE; /* stream already closed */ } + if (self->detached) { + Py_RETURN_NONE; /* gh-142594 null pointer issue */ + } else { PyObject *exc = NULL; if (self->finalizing) {