This is my code.
def getch():
fd = sys.stdin.fileno()
old_Settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
sys.stdin.flush()
except KeyboardInterrupt:
print ("[Error] Abnormal program termination")
finally:
termios.tcsetattr(fd,termios.TCSADRAIN,old_settings)
return ch
In this code, KeyboardInterrupt exception doesn't work.
Thank you
Yes, when you set your TTY to raw mode—or, in particular, when you disable the termios
setting ISIG
—you no longer get the corresponding behavior:
ISIG When any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate the corresponding signal.
In other words, the character INTR
, aka ^C
in most terminals, no longer generates a SIGINT
signal, which is what Python uses to raise a KeyboardInterrupt
.
This is how full-screen programs like emacs
can use ^C
as a control character instead of just quitting.
So, there are two options:
Instead of calling setraw
, set the exact set of termios
flags you want, making sure not to disable ISIG
.
It's worth looking at the source to Python's tty.setraw
to see exactly what it does, to use as a baseline.
You could just do exactly the same thing without the ISIG
. But really, you should read the docs to learn what each of these things means.
And make sure to man termios
on your own system, don't search online; things are a bit different on Linux vs. macOS/*BSD, and there are minor differences even between versions of the same OS.
Even after reading the docs, and experimenting on your own, it may not be entirely clear what effect every setting has. This article seems to do a decent job explaining things what goes into raw mode, and what each of the relevant flags actually means (although I only quickly skimmed it—and. again, you still need to read man termios
for your platform).
Alternatively, you can just handle ^C
manually.
You're already reading a character at a time with sys.stdin.read(1)
, so when someone hits ^C
, you're going to read a '\x03'
character.
You can do whatever you want when that happens, including raise KeyboardInterrupt
.
As a side note, if this is Python 3, you may want to read in binary mode:
ch = sys.stdin.buffer.raw.read(1)
sys.stdin.flush()
(Then you'll check against b'\x03'
instead of '\x03'
. of course.)
In text mode, you should get a whole character at a time, even if it's a multibyte character in UTF-8 (or whatever your locale is). Which seems simpler at first—but that can actually be misleading, because a single character can still be only part of a keystroke. For example, with most terminals, an up arrow sends 3 characters ('\x1B[A'
), but
read(1)` will just give you the first one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With