CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dockerpty

Python library to use the pseudo-tty of a docker container

Pending
Overview
Eval results
Files

terminal-control.mddocs/

Terminal Control

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.

Capabilities

Terminal Class

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 terminal

TTY Size Detection

Function 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")

File Descriptor Control

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 Terminal Operation

What Raw Mode Does

Raw mode disables terminal input processing, meaning:

  • No line buffering: Characters are available immediately, not after Enter
  • No signal generation: Ctrl+C, Ctrl+Z don't generate signals
  • No character interpretation: No backspace processing, no echo
  • Direct key forwarding: All keystrokes passed through to the application

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.

Terminal Attribute Management

The Terminal class manages terminal attributes using the termios module:

  1. Save current attributes: termios.tcgetattr() saves original terminal state
  2. Set raw mode: tty.setraw() configures raw mode attributes
  3. Restore on exit: termios.tcsetattr() restores original attributes

This ensures the user's terminal is always restored to its original state, even if dockerpty exits unexpectedly.

Window Size Handling

Size Detection Methods

The size() function uses multiple methods to determine terminal size:

  1. TIOCGWINSZ ioctl: Primary method using system call to get window size
  2. Environment variables: Fallback to LINES and COLUMNS environment variables
  3. Return None: If neither method works (not a terminal)

Automatic Resize Handling

dockerpty uses SIGWINCH signal handling to automatically resize the container's PTY when the terminal window changes size:

  1. Signal installation: WINCHHandler installs SIGWINCH handler
  2. Size detection: Handler calls tty.size() to get new dimensions
  3. PTY resize: Calls Docker API to resize container's PTY to match
  4. Signal restoration: Original SIGWINCH handler restored on exit

Error Handling

Terminal Detection

Functions safely handle non-terminal file descriptors:

  • os.isatty() checks if fd is a terminal before terminal operations
  • Operations are skipped gracefully if not running in a terminal
  • No errors are raised for non-terminal usage

Signal Interruption

The select() function handles POSIX signal interruption:

  • Catches errno.EINTR from interrupted system calls
  • Returns empty lists to allow graceful continuation
  • Re-raises other select errors that indicate real problems

Attribute Restoration

Terminal attribute restoration is robust:

  • Attributes are saved before any modifications
  • Restoration happens in exception handlers and context manager exits
  • Multiple restoration calls are safe (idempotent)
  • Original handler restoration prevents signal handler leaks

Install with Tessl CLI

npx tessl i tessl/pypi-dockerpty

docs

container-operations.md

core-pty-management.md

index.md

main-entry-points.md

stream-management.md

terminal-control.md

tile.json