@@ -656,7 +656,8 @@ async def run_async(
656656 handle_sigint = False
657657
658658 async def _run_async (f : "asyncio.Future[_AppResult]" ) -> _AppResult :
659- self .context = contextvars .copy_context ()
659+ context = contextvars .copy_context ()
660+ self .context = context
660661
661662 # Counter for cancelling 'flush' timeouts. Every time when a key is
662663 # pressed, we start a 'flush' timer for flushing our escape key. But
@@ -700,6 +701,16 @@ def read_from_input() -> None:
700701 flush_task .cancel ()
701702 flush_task = self .create_background_task (auto_flush_input ())
702703
704+ def read_from_input_in_context () -> None :
705+ # Ensure that key bindings callbacks are always executed in the
706+ # current context. This is important when key bindings are
707+ # accessing contextvars. (These callbacks are currently being
708+ # called from a different context. Underneath,
709+ # `loop.add_reader` is used to register the stdin FD.)
710+ # (We copy the context to avoid a `RuntimeError` in case the
711+ # context is already active.)
712+ context .copy ().run (read_from_input )
713+
703714 async def auto_flush_input () -> None :
704715 # Flush input after timeout.
705716 # (Used for flushing the enter key.)
@@ -719,7 +730,7 @@ def flush_input() -> None:
719730
720731 # Enter raw mode, attach input and attach WINCH event handler.
721732 with self .input .raw_mode (), self .input .attach (
722- read_from_input
733+ read_from_input_in_context
723734 ), attach_winch_signal_handler (self ._on_resize ):
724735 # Draw UI.
725736 self ._request_absolute_cursor_position ()
0 commit comments