Using Curses with Python Asyncio
Notes on Using Curses with Python Asyncio
Curses has been around for a long time. Asyncio is more than a decade newer. Curses coding practices were well established by that time.
It came time for me to write my first curses application (needed a GUI with minimal infrastructure requirements), which needed asyncio capabilities. I wouldn’t have minded finding more information on the webs on how the two standard libraries interact.
Here’s what I eventually discovered after coding the app (with a bonus template to start your own python curses app).
It’s about Polling vs. Blocking I/O
The curses getch() call can operate in both a blocking and a non-blocking mode. Either will work in asyncio, with at least one caveat - if the blocking getch() is called in a separate thread via an asyncio executor, it will not return some events, notably KEY_RESIZE. A polling mechanism is therefore recommended, e.g.:
self.stdscr.nodelay(True)
while not self.done:
char = self.stdscr.getch()
if char == ERR:
await asyncio.sleep(0.1)
elif char == KEY_RESIZE:
self.make_display()
else:
self.handle_char(char)
See a working demo template at curses_demo.py