Evan Fosmark
Evan Fosmark

Reputation: 101691

Reading a single character (getch style) in Python is not working in Unix

Any time I use the recipe at http://code.activestate.com/recipes/134892/ I can't seem to get it working. It always throws the following error:

Traceback (most recent call last):
    ...
    old_settings = termios.tcgetattr(fd)
termios.error: (22, 'Invalid argument)

My best thought is that it is because I'm running it in Eclipse so termios is throwing a fit about the file descriptor.

Upvotes: 4

Views: 9593

Answers (2)

Vitja Makarov
Vitja Makarov

Reputation: 76

Putting terminal into raw mode isn't always a good idea. Actually it's enough to clear ICANON bit. Here is another version of getch() with timeout support:

import tty, sys, termios
import select

def setup_term(fd, when=termios.TCSAFLUSH):
    mode = termios.tcgetattr(fd)
    mode[tty.LFLAG] = mode[tty.LFLAG] & ~(termios.ECHO | termios.ICANON)
    termios.tcsetattr(fd, when, mode)

def getch(timeout=None):
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        setup_term(fd)
        try:
            rw, wl, xl = select.select([fd], [], [], timeout)
        except select.error:
            return
        if rw:
            return sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

if __name__ == "__main__":
    print getch()

Upvotes: 5

Anurag Uniyal
Anurag Uniyal

Reputation: 88777

This is working on Ubuntu 8.04.1 , Python 2.5.2, i get no such error. May be you should try it from command line, eclipse may be using its own stdin, i get exact same error if I run it from Wing IDE, but from command line it works great. Reason is that IDE e.g Wing is using there own class netserver.CDbgInputStream as sys.stdin so sys.stdin.fileno is zero, thats why the error. Basically IDE stdin is not a tty (print sys.stdin.isatty() is False)

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch


getch = _GetchUnix()

print getch()

Upvotes: 9

Related Questions