import fcntl import os import select import sys import termios from contextlib import contextmanager @contextmanager def setup_term(*args, **kwargs): fd = sys.stdin.fileno() oldterm = termios.tcgetattr(fd) newattr = termios.tcgetattr(fd) newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO termios.tcsetattr(fd, termios.TCSANOW, newattr) oldflags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK) try: yield finally: # restore terminal settings termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm) fcntl.fcntl(fd, fcntl.F_SETFL, oldflags) def getch(timeout): # sys.stdin.read is blocking, use select instead # NOTE: this will not work on windows as select only accepts sockets, not all file descriptors # TODO: use python selectors module? o, _, _ = select.select([sys.stdin], [], [], timeout) if o != []: assert len(o) == 1 return o[0].read(1) return None