The problem you're experiencing is the difference between 'raw', 'cooked', and 'cbreak' modes. And these modes are modes of the kernel level terminal driver, not modes of your application code or the standard library or anything else in userspace. This is the old-school Unix way of referring to these. Posix has replaced them with a much more fine-grained set of attributes, though the Posix attributes are typically flipped in concert with helper functions in a way that mimics the old 'raw', 'cooked' and 'cbreak' modes.
In cooked mode, the terminal driver itself has primitive line editing functionality builtin. It handles backspace, word erase (basically backspace a whole word at once) and similar things. Nothing sophisticated like handling arrow keys or history or anything like that. Very primitive. In this mode, your program never sees anything from the terminal until the end-of-line (eol) character is sent, and then your program gets a whole line, and the line ending is translated to Unix standard
regardless of what the terminal actually does. Also, as part of this, the terminal driver echoes typed characters back to the terminal so the user can see what they're typing.
In 'cooked' mode, the kernel level terminal driver also does some output translation. And part of that is turning
into
if needed.
Also, in 'cooked' mode the terminal driver handles special characters like Control-C (sends a SIGINT to the controlling process group (translated by CPython into a KeyboardInterrupt exception)) and Control-Z (sends a SIGTSTP (like a SIGSTOP, but can be caught) to the controlling process group).
In 'cbreak' mode, line editing is no longer done. The terminal driver gives each character (or short character sequence, like the escape sequence for an arrow key) to the program immediately. These characters are not echoed to the screen, and so unless your program then prints them the user won't see them. The terminal driver though still handles special characters like Control-C and Control-Z, though it ceases to handle line editing characters like backspace or the word-erase character (typically Control-W). Also, some output processing is still done, so the driver turns a
into a
.
In 'raw' mode, no processing is done on either input or output. No special character handling, no echoing, no transforming
into
, no handling for Control-Z, nothing. It's up to the program that puts the terminal in raw mode to do it all.
Now, you are setting the attributes for sys.stdin
so you may think this shouldn't affect sys.stdout
. But, in fact, both of your file descriptors lead to the exact same 'instance' of a terminal driver. And it's the settings for the terminal driver that determine what happens. So it doesn't matter if you change those settings through sys.stdin
, sys.stdout
, or even sys.stderr
, all change the same underlying terminal driver instance and they affect all the others.
This, of course, is not true of file descriptors that have been redirected by the shell before your program is launched.
As a side note, you can use the stty -a
on the command line to see a full read out of all of these flags (including which control characters result in which signals in cooked and cbreak modes).