ZetCode

Python os.isatty Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.isatty function, which checks if a file descriptor is connected to a terminal device. We'll cover TTY detection, common use cases, and practical examples.

Basic Definitions

The os.isatty function determines if a file descriptor refers to a terminal device (TTY). It's useful for detecting interactive sessions.

Key parameter: fd (file descriptor to check). Returns True if connected to a terminal, False otherwise. Raises OSError for invalid file descriptors.

Checking Standard Output

This example checks if standard output is connected to a terminal. This is useful for deciding whether to use colors or progress indicators.

check_stdout.py
import os
import sys

# Check if stdout is a terminal
if os.isatty(sys.stdout.fileno()):
    print("Output is going to a terminal")
    print("\033[1;32mColored text\033[0m")  # ANSI color codes
else:
    print("Output is being redirected")
    print("Plain text")

The script uses ANSI color codes only when output is to a terminal. This prevents control characters from appearing in log files.

The fileno() method gets the underlying file descriptor number needed for os.isatty. Standard streams have predefined file descriptors (0,1,2).

Detecting Interactive Sessions

This example demonstrates how to detect if a script is running interactively by checking standard input. This affects prompt behavior.

interactive_check.py
import os
import sys

def get_input(prompt):
    if os.isatty(sys.stdin.fileno()):
        return input(prompt)
    else:
        return sys.stdin.readline().strip()

# Get user input with conditional prompt
user_input = get_input("Enter your name: ")
print(f"Hello, {user_input}")

The function shows a prompt only in interactive sessions. When input is redirected (from a file or pipe), it reads silently.

This pattern is common in command-line tools that need to handle both interactive and non-interactive usage.

Checking File Descriptors

This example shows how to check arbitrary file descriptors, including those from opened files. Not all file descriptors refer to terminals.

file_descriptor_check.py
import os

# Open a regular file
with open("data.txt", "w") as f:
    print(f"Is data.txt a terminal? {os.isatty(f.fileno())}")

# Check standard error
print(f"Is stderr a terminal? {os.isatty(2)}")

# Try with invalid descriptor
try:
    print(os.isatty(999))
except OSError as e:
    print(f"Error: {e}")

The script demonstrates that regular files are not terminals, while stderr might be. It also shows the error for invalid descriptors.

File descriptor numbers can be used directly (like stderr's 2) or obtained from file objects with fileno().

Conditional Terminal Formatting

This example uses os.isatty to enable rich terminal formatting only when appropriate, falling back to plain text otherwise.

terminal_formatting.py
import os
import sys

def print_status(message, success):
    if os.isatty(sys.stdout.fileno()):
        # Terminal formatting
        color = "\033[92m" if success else "\033[91m"
        reset = "\033[0m"
        symbol = "✓" if success else "✗"
        print(f"{color}{symbol} {message}{reset}")
    else:
        # Plain text
        status = "SUCCESS" if success else "FAILURE"
        print(f"{status}: {message}")

# Example usage
print_status("Operation completed", True)
print_status("Operation failed", False)

The function shows colored checkmarks/X marks in terminals, but plain text when output is redirected. This improves script usability in both cases.

This approach is commonly used by tools like git, npm, and others that need to adapt their output based on the environment.

Testing Pseudo-Terminals

This advanced example demonstrates os.isatty behavior with pseudo-terminals (PTYs), which are common in terminal emulators and SSH sessions.

pty_check.py
import os
import pty
import sys

def create_pty():
    master, slave = pty.openpty()
    print(f"Master FD: {master}, isatty: {os.isatty(master)}")
    print(f"Slave FD: {slave}, isatty: {os.isatty(slave)}")
    return master, slave

print("Original stdout isatty:", os.isatty(sys.stdout.fileno()))
master, slave = create_pty()
os.close(slave)
os.close(master)

The script creates a pseudo-terminal pair and checks isatty for both ends. The slave end typically reports as a terminal, while the master doesn't.

PTYs are how terminal emulators and remote sessions provide terminal functionality while not being physical terminals.

Cross-Platform Considerations

This example shows how os.isatty behaves differently on Windows versus Unix-like systems, particularly with console vs. non-console files.

cross_platform.py
import os
import sys

print(f"stdin isatty: {os.isatty(sys.stdin.fileno())}")
print(f"stdout isatty: {os.isatty(sys.stdout.fileno())}")
print(f"stderr isatty: {os.isatty(sys.stderr.fileno())}")

# Check NUL device (different on Windows vs Unix)
try:
    with open(os.devnull, "r") as null_file:
        print(f"Null device isatty: {os.isatty(null_file.fileno())}")
except Exception as e:
    print(f"Error checking null device: {e}")

The script checks standard streams and the null device. Windows consoles are terminals, while Unix null devices never are.

Behavior may vary between terminal emulators and different Windows versions. Always test on target platforms.

Security Considerations

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.