2

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)

jsbueno
  • 86,446
  • 9
  • 131
  • 182
  • the fix/workaround on the accepted answer is now on the project, and used in a seamless way for all output. Install the old version terminedia==0.2.1 to be able to trigger the issue as in the question text. – jsbueno Jan 13 '22 at 12:53

1 Answers1

1

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)

jsbueno
  • 86,446
  • 9
  • 131
  • 182