Question

I'm making an algorithm that reads input from keyboard, stores it on a variable called message and then write this variable into a file. However, whenever the user is typing, if they hit Esc, I'd like the execution to stop without any errors.

Let's say the code is:

message = raw_input()

What do I have to add? So if I'm in the middle of a sentence like:

My name is th

And I hit Esc, it stops?

Was it helpful?

Solution

There is no way to do this with normal input. raw_input reads a whole line at a time.

In some (very few) cases, you can do this:

message = ''
while True:
    ch = sys.stdin.read(1)
    if ch == '\x1b':
        exit(0)
    elif ch == '\n':
        break
    message += ch

But in general, this won't work. For example, on a typical Unix system, sys.stdin will be line-buffered, and possibly even fed through a library like readline to allow the user to edit as he goes along. Or, if you're running the program inside IDLE, there is no way to read stdin at all; raw_input works around that by popping up a dialog box asking for input, but your code can't do that.

You can work around that in different ways on different platforms in different situations.


On Windows, if you know your input is going to be a "DOS prompt" window (which you can check with sys.stdin.isatty()), you can use the msvcrt functions. For example:

import sys, msvcrt
assert sys.stdin.isatty(), "Can't run without a console to run on"
message = u''
while True:
    ch = msvcrt.getwche()
    if ch == u'\x1b':
        exit(0)
    elif ch == u'\n':
        break
    message += ch

This should work for both 2.6+ and 3.3+, but in 2.x, unlike raw_input, it returns a unicode instead of a str. If you want str here, drop all the u prefixes and use getche instead of getwche.


On most POSIX-like platforms with reasonably-standard termios (including Mac OS X and Linux), if you know your input is going to be a "TTY" (which you can check with sys.stdin.isatty()—or, if you prefer, you can just look for a TTY to use instead of stdin, although that doesn't work on quite as many platforms/in quite as many situations), you can use the [termios](http://docs.python.org/3/library/termios.html) or tty module to put the input into "raw" mode. In Python 3.x, you will probably have to read directly from sys.stdin.buffer instead of sys.stdin, and decode to Unicode manually. So:

import sys, termios, tty
assert sys.stdin.isatty(), "Can't run without a console to run on"
fd = sys.stdin.fileno()
stash = termios.tcgetattr(fd)
try:
    tty.setraw(fd)
    newterm = termios.tcgetattr(fd)
    newterm[tty.LFLAG] |= termios.ECHO
    termios.tcsetattr(fd)
    message = b''
    while True:
        ch = sys.stdin.buffer.read(1)
        if ch == b'\x1b':
            exit(0)
        elif ch == b'\n':
            break
        else:
            message += ch
    message = message.decode(sys.stdin.encoding)
finally:
    termios.tcsetattr(fd, termios.TCSANOW, stash)

As with the Windows version, this should be 2.6+/3.3+ multi-version-compatible, except for the fact that it always returns unicode while 2.x raw_input would return str (which in this case is all in that one decode line, which you can drop if you don't want it).

Notice that I used both tty and termios here. The tty module is a higher-level wrapper, but it doesn't do everything you want. So, you can use it as far as it goes (to flip whatever switches are needed to get raw mode on your platform, and to let you use cross-platform/readable names for the flagsets and values instead of indices), but you usually still need termios anyway.


On any other platform, you're on your own.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top