Skip to content

Instantly share code, notes, and snippets.

@ali1234
Last active December 24, 2022 00:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ali1234/7a6899ecc77a67a643de65a919457020 to your computer and use it in GitHub Desktop.
Save ali1234/7a6899ecc77a67a643de65a919457020 to your computer and use it in GitHub Desktop.
Unbuffered, non-blocking stdin in Python
import fcntl
import termios
import sys
import os
import time
class NonBlockingInput(object):
def __enter__(self):
# canonical mode, no echo
self.old = termios.tcgetattr(sys.stdin)
new = termios.tcgetattr(sys.stdin)
new[3] = new[3] & ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new)
# set for non-blocking io
self.orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, self.orig_fl | os.O_NONBLOCK)
def __exit__(self, *args):
# restore terminal to previous state
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, self.orig_fl)
with NonBlockingInput():
while True:
c = sys.stdin.read(1)
print('tick', repr(c))
time.sleep(0.1)
@lomalkin
Copy link

After use this in case of print() large text I receive:

BlockingIOError: [Errno 35] write could not complete without blocking

It looks fixed if edit def __exit__ like this, to restore blocking output:

    def __exit__(self, *args):
        # restore terminal to previous state
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old)
        # set for blocking io
        orig_fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
        fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | ~ os.O_NONBLOCK)

@ali1234
Copy link
Author

ali1234 commented Dec 24, 2022

    fcntl.fcntl(sys.stdin, fcntl.F_SETFL, orig_fl | ~ os.O_NONBLOCK)

This should be & not | but yes, looks good.

@ali1234
Copy link
Author

ali1234 commented Dec 24, 2022

Another way is to just remember the orig_fl from __enter__ and restore it directly. Untested but shouldn't harm anything. :)

@lomalkin
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment