Python library to use the pseudo-tty of a docker container
—
Low-level terminal management for handling raw mode, TTY size detection, and terminal attribute manipulation. This module provides the foundation for dockerpty's terminal control capabilities.
Wrapper functionality to temporarily make the TTY raw. This is useful when streaming data from a pseudo-terminal into the TTY.
class Terminal:
def __init__(self, fd, raw=True):
"""
Initialize a terminal for the tty with stdin attached to fd.
Initializing the Terminal has no immediate side effects. The start()
method must be invoked, or 'with Terminal:' used before the terminal is affected.
Parameters:
- fd: file-like object, stdin file descriptor for the terminal
- raw: bool, whether to operate in raw mode (default: True)
"""
def __enter__(self):
"""
Context manager entry - invoked when entering a 'with' block.
Calls start() automatically.
Returns:
self
"""
def __exit__(self, *_):
"""
Context manager exit - invoked when exiting a 'with' block.
Calls stop() automatically.
Parameters:
- *_: Exception information (ignored)
Returns:
None
"""
def israw(self):
"""
Returns True if the TTY should operate in raw mode.
Returns:
bool - True if terminal should use raw mode
"""
def start(self):
"""
Saves the current terminal attributes and makes the tty raw.
This method returns immediately. If the fd is not a TTY or raw=False,
no changes are made.
Returns:
None
"""
def stop(self):
"""
Restores the terminal attributes back to before setting raw mode.
If the raw terminal was not started, does nothing.
Returns:
None
"""Usage example:
import sys
from dockerpty.tty import Terminal
# Use as context manager (recommended)
with Terminal(sys.stdin, raw=True):
# Terminal is now in raw mode
# Keys are passed through without interpretation
do_pty_operations()
# Terminal attributes automatically restored
# Or manual management
terminal = Terminal(sys.stdin, raw=True)
terminal.start()
try:
do_pty_operations()
finally:
terminal.stop() # Always restore terminalFunction to determine the size of a TTY in rows and columns.
def size(fd):
"""
Return a tuple (rows,cols) representing the size of the TTY fd.
The provided file descriptor should be the stdout stream of the TTY.
Uses TIOCGWINSZ ioctl to get terminal dimensions, with fallback to
environment variables LINES and COLUMNS.
Parameters:
- fd: file-like object, stdout stream of the TTY
Returns:
tuple - (rows, cols) as integers, or None if size cannot be determined
"""Usage example:
import sys
from dockerpty.tty import size
# Get current terminal size
terminal_size = size(sys.stdout)
if terminal_size:
rows, cols = terminal_size
print(f"Terminal is {cols}x{rows}")
else:
print("Not running in a terminal")Utility functions for managing file descriptor blocking modes and stream selection.
def set_blocking(fd, blocking=True):
"""
Set the given file-descriptor blocking or non-blocking.
Uses fcntl to modify the O_NONBLOCK flag on the file descriptor.
Parameters:
- fd: file descriptor or file-like object with fileno() method
- blocking: bool, True for blocking mode, False for non-blocking (default: True)
Returns:
bool - original blocking status (True if was blocking, False if was non-blocking)
"""
def select(read_streams, write_streams, timeout=0):
"""
Select the streams ready for reading, and streams ready for writing.
Uses select.select() internally but only returns two lists of ready streams.
Handles POSIX signal interrupts (EINTR) gracefully by returning empty lists.
Parameters:
- read_streams: list of file-like objects to check for read readiness
- write_streams: list of file-like objects to check for write readiness
- timeout: float, timeout in seconds (default: 0 for immediate return)
Returns:
tuple - (ready_read_streams, ready_write_streams)
Raises:
select.error - for non-EINTR select errors
"""Usage examples:
import sys
import socket
from dockerpty.io import set_blocking, select
# Make stdin non-blocking
original_blocking = set_blocking(sys.stdin, False)
# Use select to wait for input
ready_read, ready_write = select([sys.stdin], [], timeout=5.0)
if ready_read:
data = sys.stdin.read()
# Restore original blocking mode
set_blocking(sys.stdin, original_blocking)Raw mode disables terminal input processing, meaning:
This is essential for dockerpty because it needs to forward all user input directly to the container's PTY without the host terminal interpreting special keys.
The Terminal class manages terminal attributes using the termios module:
termios.tcgetattr() saves original terminal statetty.setraw() configures raw mode attributestermios.tcsetattr() restores original attributesThis ensures the user's terminal is always restored to its original state, even if dockerpty exits unexpectedly.
The size() function uses multiple methods to determine terminal size:
LINES and COLUMNS environment variablesdockerpty uses SIGWINCH signal handling to automatically resize the container's PTY when the terminal window changes size:
tty.size() to get new dimensionsFunctions safely handle non-terminal file descriptors:
os.isatty() checks if fd is a terminal before terminal operationsThe select() function handles POSIX signal interruption:
errno.EINTR from interrupted system callsTerminal attribute restoration is robust:
Install with Tessl CLI
npx tessl i tessl/pypi-dockerpty