Python termios: Safe Terminal Control

Published on: August 25, 2026
Reading time: 5 minutes
Detailed view of programming code in a dark theme on a computer screen.

The termios module exposes POSIX terminal control on Unix. It can enable or disable echo, switch between canonical line input and character-at-a-time reads, configure serial speeds, manage input and output queues, and query the terminal window size.

These operations change shared state on the terminal associated with a file descriptor. If a program exits without restoring that state, the user’s shell may remain without echo, with disabled control keys, or in an apparently broken mode. Always save the original attributes and restore them in finally.

Availability

termios is available only on Unix systems with POSIX TTY support. Windows uses different APIs, and the descriptor must refer to a real terminal.

import os
import sys

if not os.isatty(sys.stdin.fileno()):
    raise RuntimeError("stdin is not connected to a TTY")

Piped or file-redirected input does not have ordinary terminal attributes. Detect the situation before applying changes.

The tcgetattr() structure

tcgetattr(fd) returns seven elements:

[iflag, oflag, cflag, lflag, ispeed, ospeed, cc]

The values represent input flags, output flags, control flags, local flags, input speed, output speed, and control characters. Interpret them only through symbolic constants from termios.

Save and restore

import sys
import termios

fd = sys.stdin.fileno()
original = termios.tcgetattr(fd)
updated = termios.tcgetattr(fd)

try:
    updated[3] &= ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSADRAIN, updated)
    secret = input("Password: ")
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, original)
    print()

Call tcgetattr() twice or deep-copy the result because the cc list is mutable. A shallow copy can share the nested list.

When changes take effect

TCSANOW applies immediately. TCSADRAIN waits until queued output is transmitted. TCSAFLUSH also discards unread input.

For password prompts, TCSADRAIN avoids cutting output already written. Use TCSAFLUSH only when dropping pending keystrokes is intentional.

Echo

The ECHO bit controls whether typed characters appear.

attributes[3] &= ~termios.ECHO

Disabling echo does not make password collection automatically secure. Privileged processes and the program itself can still access the value. Prefer getpass.getpass() for ordinary passwords.

Canonical mode

With ICANON enabled, input is delivered after Enter and the terminal driver performs basic line editing. Disabling it allows reads before a full line is complete.

attributes[3] &= ~termios.ICANON

Non-canonical mode is useful for games, hotkeys, and full-screen interfaces, but the application must process bytes and escape sequences itself.

VMIN and VTIME

In the cc array, VMIN and VTIME control non-canonical reads.

attributes[6][termios.VMIN] = 1
attributes[6][termios.VTIME] = 0

VMIN 1 and VTIME 0 waits for at least one byte. VMIN 0 with a positive VTIME creates a timeout measured in tenths of a second. POSIX defines several combinations; test the actual target system.

Read one key event

import os
import sys
import termios

fd = sys.stdin.fileno()
original = termios.tcgetattr(fd)
updated = termios.tcgetattr(fd)
updated[3] &= ~(termios.ICANON | termios.ECHO)
updated[6][termios.VMIN] = 1
updated[6][termios.VTIME] = 0

try:
    termios.tcsetattr(fd, termios.TCSADRAIN, updated)
    key = os.read(fd, 1)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, original)

print(key)

A visible key can generate several bytes. Arrow and function keys normally produce escape sequences.

Unicode input

os.read() returns bytes. A UTF-8 character may require multiple bytes, so one byte is not necessarily one character.

Use an incremental decoder from codecs or parse the terminal protocol. Do not decode every individual byte as if the input were ASCII.

Signals and control keys

The ISIG flag allows characters such as Ctrl+C and Ctrl+Z to generate signals. Removing it makes those bytes ordinary input.

Disabling ISIG can prevent users from interrupting the application. Provide a reliable exit key and restore state after exceptions. See Python signal.

Input mappings

Flags including ICRNL, INLCR, and IGNCR control carriage-return and newline transformations. Changing them affects Enter and serial protocols.

Modify only the required bits and preserve the remaining configuration.

Output processing

OPOST enables output processing. Raw configurations may disable it, which can also change newline behavior and cursor positioning.

Interactive applications should normally use the convenience functions in tty or a full terminal library instead of rebuilding every flag manually.

Hardware control flags

cflag contains character size, parity, stop bits, receiver, and hardware-flow-control settings. These are especially relevant for serial ports.

Driver support varies. Incorrect configuration can produce unreadable data, block communication, or disable flow control.

Baud rate

ispeed and ospeed use constants such as B9600 and B115200.

attributes[4] = termios.B115200
attributes[5] = termios.B115200

Not every speed is available. Production serial applications often benefit from a higher-level library such as pyserial.

Wait for output with tcdrain()

tcdrain(fd) waits until queued output has been transmitted. It is useful before changing line parameters or closing a serial device.

The call can block for a long time when hardware is slow or disconnected. Design cancellation and timeouts around the operation.

Discard queues with tcflush()

termios.tcflush(fd, termios.TCIFLUSH)

TCIFLUSH discards input, TCOFLUSH output, and TCIOFLUSH both. Dropping output can lose commands that the application already accepted.

Flow control

tcflow() suspends or resumes input or output with actions such as TCOOFF and TCOON. Do not confuse this software control with hardware flow control.

Send a break

tcsendbreak(fd, duration) sends a break condition on serial lines. A zero duration means roughly 0.25 to 0.5 seconds under POSIX; nonzero meanings are system-dependent.

Window size

Since Python 3.11, tcgetwinsize() returns rows and columns.

rows, columns = termios.tcgetwinsize(sys.stdout.fileno())
print(rows, columns)

tcsetwinsize() sets the associated size when the platform supports it. This is particularly useful for pseudo-terminal tests.

SIGWINCH

Unix processes normally receive SIGWINCH after terminal resize. Let the handler set a flag and recompute layout in the main loop.

The later guide to curses in this batch covers full-screen resize handling.

A reusable context manager

from contextlib import contextmanager
import termios

@contextmanager
def temporary_attributes(fd, modify):
    original = termios.tcgetattr(fd)
    updated = termios.tcgetattr(fd)
    modify(updated)
    try:
        termios.tcsetattr(fd, termios.TCSADRAIN, updated)
        yield
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, original)

Centralizing restoration reduces missed error paths.

Fork and subprocesses

Child processes can inherit the same terminal and see temporary attributes. Avoid starting unrelated subprocesses while the TTY is modified unless that behavior is part of the protocol.

A child can also change the shared TTY state. Use a pseudo-terminal when an interactive program needs isolation.

Threads

The state belongs to the terminal, not a thread. Two threads changing flags can restore values in the wrong order. Keep terminal control in one thread.

Manual recovery

If an experimental program leaves the terminal without echo, the Unix command stty sane usually restores a reasonable state. Document this recovery command for users.

Testing

Test a real terminal and redirected input, Ctrl+C, exceptions during reads, Unicode, arrows, resize, suspend/resume, subprocesses, serial hardware, VMIN/VTIME combinations, and restoration after failure.

Use pty for automated TTY tests, but still perform manual tests on every supported terminal.

Common mistakes

Common failures include omitting finally, modifying the original list, shallow-copying cc, treating one key as one byte, disabling ISIG without another exit path, operating on redirected stdin, changing too many flags, and failing to test exception restoration.

Conclusion

termios provides precise POSIX terminal and serial control. Use it only when low-level flags are necessary; prefer tty, getpass, curses, or specialized serial libraries for common tasks.

Save and restore state, change only required bits, and test on the target Unix system. Consult the official termios documentation and termios(3).

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Side view of contemplating female assistant in casual style standing near shelves and choosing file with documents
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python fcntl: File Locks and Control

    Learn Python fcntl for file locks, descriptor flags, ioctl, pipes, and Unix control while avoiding invalid buffers and memory corruption.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python readline: History and Autocomplete

    Learn Python readline for command history, tab completion, line editing, GNU Readline, libedit, and safer interactive terminal prompts.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Peaceful river scene with mossy boulders and flowing water captured in long exposure.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python io: Master Streams and Buffers

    Learn Python io for text and binary streams, buffering, encodings, StringIO, BytesIO, raw I/O, and file-like interfaces.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Business professional analyzing financial data on multiple computer monitors at his workspace.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python select: Monitor Multiple I/O

    Learn Python select to monitor sockets and pipes, handle partial I/O, backpressure, poll, epoll, signals, and platform differences.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python signal: Graceful Process Shutdown

    Learn Python signal to handle SIGTERM and SIGINT, stop services, use timers and wakeup file descriptors, and avoid handler deadlocks.

    Ler mais

    Tempo de leitura: 6 minutos
    24/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python errno: Understand System Errors

    Learn Python errno to interpret system codes, handle OSError, files, networks, retries, and native calls portably.

    Ler mais

    Tempo de leitura: 4 minutos
    24/08/2026