diff --git a/cli.py b/cli.py index 728309733b6..af179c86c13 100644 --- a/cli.py +++ b/cli.py @@ -13401,6 +13401,30 @@ class HermesCLI: self._print_exit_summary() return + # On macOS with uv-managed Python, kqueue's selector cannot register + # fd 0, raising OSError(EINVAL) from kqueue.control() when prompt_toolkit + # calls loop.add_reader (#6393). Probe kqueue and, if it can't watch + # stdin, switch to a SelectSelector-backed event loop policy. + if sys.platform == "darwin": + try: + import selectors as _selectors + if hasattr(_selectors, "KqueueSelector"): + _kq = _selectors.KqueueSelector() + try: + _kq.register(0, _selectors.EVENT_READ) + _kq.unregister(0) + finally: + _kq.close() + except (OSError, ValueError, KeyError): + import asyncio as _aio_probe + import selectors as _selectors + + class _SelectEventLoopPolicy(_aio_probe.DefaultEventLoopPolicy): + def new_event_loop(self): + return _aio_probe.SelectorEventLoop(_selectors.SelectSelector()) + + _aio_probe.set_event_loop_policy(_SelectEventLoopPolicy()) + # Run the application with patch_stdout for proper output handling try: with patch_stdout(): @@ -13421,12 +13445,20 @@ class HermesCLI: except (KeyError, OSError) as _stdin_err: # Catch selector registration failures from broken stdin (#6393) # and I/O errors from broken stdout during interrupt (#13710). - if isinstance(_stdin_err, OSError) and getattr(_stdin_err, "errno", None) == errno.EIO: + _errno = getattr(_stdin_err, "errno", None) if isinstance(_stdin_err, OSError) else None + _msg = str(_stdin_err) + if _errno == errno.EIO: pass # suppress broken-stdout I/O errors on interrupt (#13710) - elif "is not registered" in str(_stdin_err) or "Bad file descriptor" in str(_stdin_err): + elif ( + _errno in (errno.EINVAL, errno.EBADF) + or "is not registered" in _msg + or "Bad file descriptor" in _msg + or "Invalid argument" in _msg + ): print( f"\nError: stdin is not usable ({_stdin_err}).\n" - "This can happen with certain Python installations (e.g. uv-managed cPython on macOS).\n" + "This can happen with certain Python installations (e.g. uv-managed cPython on macOS)\n" + "where kqueue cannot register fd 0.\n" "Try reinstalling Python via pyenv or Homebrew, then re-run: hermes setup" ) else: