I am getting the little documented and less present in the web "BlockingIOError" when writing to the tty from a program where I need the terminal to be in "raw" mode.
Switching the terminal to raw in Unix (Linux, Mac) is the way to get lines to show
without printing a newline, and, more important, to read whatever is typed
without having to wait for the <enter>
key.
Check https://stackoverflow.com/a/6599441/108205 for a reliable way to do it in Python.
However, when printing data to the raw terminal, I would, at random times, have my program terminate with a BlockingIOError
exception.
(I had even built a retry mechanism to workaround it, but sometimes it was not work-aroundable at all).
The exception shows up in some issues on github, with no assertion on how to fix it. The way to trigger it with my project is:
import terminedia as TM
with TM.keyboard: # enables the use of TM.inkey() for realtime keyboard reading
print("*" * 100_000)
(you can pip install terminedia
to try)
This snippet, creating a context manager which momentarily switches the tty back to "normal" blocking mode wrapping the parts that have a lot of output fixed the issue for me:
import fcntl
import os
...
class UnblockTTY:
def __enter__(self):
self.fd = sys.stdin.fileno()
self.flags_save = fcntl.fcntl(self.fd, fcntl.F_GETFL)
flags = self.flags_save & ~os.O_NONBLOCK
fcntl.fcntl(self.fd, fcntl.F_SETFL, flags)
def __exit__(self, *args):
fcntl.fcntl(self.fd, fcntl.F_SETFL, self.flags_save)
For completeness, the snippet in the question would be fixed with:
import terminedia as TM
with TM.keyboard:
with TM.terminal.UnblockTTY():
print("*" * 100_000)
(although when using terminedia one should be using the "Screen" class rather or TM.print
which are already wrapped)