From 32cc3eced1ec00e463e1621b1ae85c1b8d4c78af Mon Sep 17 00:00:00 2001 From: mads Date: Sun, 12 Apr 2026 01:15:18 +0800 Subject: [PATCH] fix(vt100): treat OSError on add_reader as EOFError (macOS kqueue) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Vt100Input._attached_input` already raises `EOFError` when `loop.add_reader(fd, ...)` fails with `PermissionError`, which is what Linux's `EPollSelector` raises when stdin points at /dev/null. macOS's `KqueueSelector` does not raise `PermissionError` for the same class of "unpollable stdin" cases — it raises `OSError [Errno 22] Invalid argument` (from `kqueue.control()`). This currently propagates all the way up out of `Application.run()` and crashes any consumer that expected the existing `EOFError` contract. Reproducer (macOS): python -c "from prompt_toolkit import prompt; prompt('> ')" \ 0< /dev/null Or any wrapper that launches a prompt_toolkit app with a non-TTY stdin on macOS — e.g. spawned from another agent process that has reassigned fd 0. Treat both `PermissionError` and `OSError` from `add_reader` as end-of-input and raise `EOFError`, so callers see the same exception regardless of which selector backend asyncio picked. --- src/prompt_toolkit/input/vt100.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/prompt_toolkit/input/vt100.py b/src/prompt_toolkit/input/vt100.py index 60a049b35..88e07184d 100644 --- a/src/prompt_toolkit/input/vt100.py +++ b/src/prompt_toolkit/input/vt100.py @@ -167,12 +167,14 @@ def callback_wrapper() -> None: try: loop.add_reader(fd, callback_wrapper) - except PermissionError: + except (PermissionError, OSError): # For `EPollSelector`, adding /dev/null to the event loop will raise # `PermissionError` (that doesn't happen for `SelectSelector` - # apparently). Whenever we get a `PermissionError`, we can raise - # `EOFError`, because there's not more to be read anyway. `EOFError` is - # an exception that people expect in + # apparently). On macOS `KqueueSelector`, an unpollable stdin fd + # (e.g. detached parent, /dev/null, or otherwise non-TTY) raises + # `OSError [Errno 22] Invalid argument` instead. Both mean "nothing + # more to read here", so we surface them as `EOFError`, which is an + # exception people expect in # `prompt_toolkit.application.Application.run()`. # To reproduce, do: `ptpython 0< /dev/null 1< /dev/null` raise EOFError