I have a prompt_toolkit application where I want a bunch of keys to be acted on immediately, without pressing Enter. It's not a fullscreen app, it's just using the bottom_toolbar
. So I have set up key bindings:
class Annotator:
def process(self):
kb = KeyBindings()
@kb.add("5")
def _(event):
self.buildJohnnyFive()
@kb.add("s")
def _(event):
self.needInput()
text = prompt("> ", bottom_toolbar=self.bottomToolbar, key_bindings=kb)
print(f"You said: {text}")
def buildJohnnyFive(self):
self.buildHim() # Do work
def needInput(self):
raw = prompt("input> ", bottom_toolbar=self.bottomToolbar)
self.useInput(raw)
def bottomToolbar(self):
return f"Current: {self.status1} - {self.status2}"
This works well, and the functions get called. However, in some of these functions I need to accept more specific input from the user. But when I call prompt()
from inside the function bound to a key, I get an exception:
File "/opt/homebrew/Cellar/[email protected]/3.10.13_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/events.py", line 80, in _run
self._context.run(self._callback, *self._args)
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/application/application.py", line 714, in read_from_input_in_context
context.copy().run(read_from_input)
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/application/application.py", line 694, in read_from_input
self.key_processor.process_keys()
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/key_binding/key_processor.py", line 273, in process_keys
self._process_coroutine.send(key_press)
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/key_binding/key_processor.py", line 188, in _process
self._call_handler(matches[-1], key_sequence=buffer[:])
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/key_binding/key_processor.py", line 323, in _call_handler
handler.call(event)
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/key_binding/key_bindings.py", line 127, in call
result = self.handler(event)
File "/Users/nick/my_app/src/annotator.py", line 110, in _
self.addCustomED()
File "/Users/nick/my_app/src/annotator.py", line 145, in needInput
raw = prompt("input> ", bottom_toolbar=self.bottom_toolbar)
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/shortcuts/prompt.py", line 1425, in prompt
return session.prompt(
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/shortcuts/prompt.py", line 1035, in prompt
return self.app.run(
File "/Users/nick/my_app/.venv/lib/python3.10/site-packages/prompt_toolkit/application/application.py", line 1002, in run
return asyncio.run(coro)
File "/opt/homebrew/Cellar/[email protected]/3.10.13_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", line 33, in run
raise RuntimeError(
Exception asyncio.run() cannot be called from a running event loop
I understand the problem: I'm already running a prompt()
, which hasn't ended yet, and now I'm asking for another, inner prompt which conflicts with the existing event loop.
What I don't understand is: how am I supposed to do this? I need to call the initial prompt so that the user gets the info in the bottom toolbar. There needs to be a run loop so that the app doesn't quit before the user has time to press keys. But then how am I supposed to get input at this inner function? Can I somehow end or suspend the outer prompt while the inner prompt is waiting for the user's input? I don't want to switch to doing everything on the one prompt, because then the user will have to press Return every time. And I actually don't WANT the terminal to fill up with lines of input commands.
Some guidance would be much appreciated.
I couldn't test it but in source code for prompt()
I found information about option in_thread
(you can find it also in documentation for prompt())
in_thread: Run the prompt in a background thread; block the current thread.
This avoids interference with an event loop in the current thread.
So maybe it will work
prompt(">", in_thread=True, ...)