bittty

bittty: A fast, pure Python terminal emulator library.

bittty (bitplane-tty) is a high-performance terminal emulator engine that provides comprehensive ANSI sequence parsing and terminal state management.

bittty.tcaps

This module provides access to terminal capabilities from the terminfo database.

Terminal Objects

class Terminal()

Stores and provides access to a terminal’s capabilities from terminfo.

__init__

def __init__(term_name: str, overrides: str)

Initializes the terminal definition.

This loads the capabilities for the given terminal name from the system’s terminfo database and then applies any user-provided overrides.

Arguments:

has

def has(cap: str) -> bool

Checks if the terminal has a given capability.

get_string

def get_string(cap: str) -> str

Gets a string capability.

This is the primary method for retrieving key codes (e.g., “kcuu1” for up arrow) to send to the child application.

get_number

def get_number(cap: str) -> int

Gets a numeric capability.

get_flag

def get_flag(cap: str) -> bool

Gets a boolean flag capability.

describe

def describe() -> str

Returns a descriptive string of all loaded capabilities for debugging.

bittty.buffer

A grid-based terminal buffer.

Buffer Objects

class Buffer()

A 2D grid that stores terminal content.

__init__

def __init__(width: int, height: int) -> None

Initialize buffer with given dimensions.

get_content

def get_content() -> List[List[Cell]]

Get buffer content as a 2D grid.

get_cell

def get_cell(x: int, y: int) -> Cell

Get cell at position.

set_cell

def set_cell(x: int, y: int, char: str, style_or_ansi=None) -> None

Set a single cell at position.

Arguments:

x, y: Position

set

def set(x: int, y: int, text: str, style_or_ansi=None) -> None

Set text at position, overwriting existing content.

insert

def insert(x: int, y: int, text: str, style_or_ansi=None) -> None

Insert text at position, shifting existing content right.

delete

def delete(x: int, y: int, count: int = 1) -> None

Delete characters at position.

clear_region

def clear_region(x1: int,
                 y1: int,
                 x2: int,
                 y2: int,
                 style_or_ansi=None) -> None

Clear a rectangular region.

clear_line

def clear_line(y: int,
               mode: int = constants.ERASE_FROM_CURSOR_TO_END,
               cursor_x: int = 0,
               style_or_ansi=None) -> None

Clear line content.

scroll_up

def scroll_up(count: int) -> None

Scroll content up, removing top lines and adding blank lines at bottom.

scroll_down

def scroll_down(count: int) -> None

Scroll content down, removing bottom lines and adding blank lines at top.

resize

def resize(width: int, height: int) -> None

Resize buffer to new dimensions.

get_line_text

def get_line_text(y: int) -> str

Get plain text content of a line (for debugging/testing).

get_line

def get_line(y: int,
             width: int = None,
             cursor_x: int = -1,
             cursor_y: int = -1,
             show_cursor: bool = False,
             mouse_x: int = -1,
             mouse_y: int = -1,
             show_mouse: bool = False) -> str

Get full ANSI sequence for a line.

get_line_tuple

def get_line_tuple(y: int,
                   width: int = None,
                   cursor_x: int = -1,
                   cursor_y: int = -1,
                   show_cursor: bool = False,
                   mouse_x: int = -1,
                   mouse_y: int = -1,
                   show_mouse: bool = False) -> tuple

Get line as a hashable tuple for caching.

bittty.parser

A state machine for processing terminal input streams.

Parser Objects

class Parser()

A state machine that parses a stream of terminal control codes.

The parser is always in one of several states (e.g. GROUND, ESCAPE, CSI_ENTRY). Each byte fed to the feed() method can cause a transition to a new state and/or execute a handler for a recognized escape sequence.

__init__

def __init__(terminal: Terminal) -> None

Initializes the parser state.

Arguments:

feed

def feed(data: str) -> None

Feeds a chunk of text into the parser.

This is the main entry point. It iterates over the data and passes each character to the state machine engine.

reset

def reset() -> None

Resets the parser to its initial ground state.

bittty.terminal

A terminal emulator.

UI frameworks can subclass this to create terminal widgets.

Terminal Objects

class Terminal()

A terminal emulator with process management and screen buffers.

This class handles all terminal logic but has no UI dependencies. Subclass this to create terminal widgets for specific UI frameworks.

get_pty_handler

@staticmethod
def get_pty_handler(rows: int = constants.DEFAULT_TERMINAL_HEIGHT,
                    cols: int = constants.DEFAULT_TERMINAL_WIDTH)

Create a platform-appropriate PTY handler.

__init__

def __init__(command: str = "/bin/bash",
             width: int = 80,
             height: int = 24,
             stdin=None,
             stdout=None) -> None

Initialize terminal.

set_pty_data_callback

def set_pty_data_callback(callback: Callable[[str], None]) -> None

Set callback for handling PTY data asynchronously.

resize

def resize(width: int, height: int) -> None

Resize terminal to new dimensions.

get_content

def get_content()

Get current screen content as raw buffer data.

capture_pane

def capture_pane() -> str

Capture terminal content.

write_text

def write_text(text: str, ansi_code: str = "") -> None

Write text at cursor position.

set_g0_charset

def set_g0_charset(charset: str) -> None

Set the G0 character set.

set_g1_charset

def set_g1_charset(charset: str) -> None

Set the G1 character set.

set_g2_charset

def set_g2_charset(charset: str) -> None

Set the G2 character set.

set_g3_charset

def set_g3_charset(charset: str) -> None

Set the G3 character set.

shift_in

def shift_in() -> None

Shift In (SI) - switch to G0.

shift_out

def shift_out() -> None

Shift Out (SO) - switch to G1.

single_shift_2

def single_shift_2() -> None

Single Shift 2 (SS2) - use G2 for next character only.

single_shift_3

def single_shift_3() -> None

Single Shift 3 (SS3) - use G3 for next character only.

move_cursor

def move_cursor(x: Optional[int], y: Optional[int]) -> None

Move cursor to position.

line_feed

def line_feed(is_wrapped: bool = False) -> None

Perform line feed.

carriage_return

def carriage_return() -> None

Move cursor to beginning of line.

backspace

def backspace() -> None

Move cursor back one position.

clear_screen

def clear_screen(mode: int = constants.ERASE_FROM_CURSOR_TO_END) -> None

Clear screen.

clear_line

def clear_line(mode: int = constants.ERASE_FROM_CURSOR_TO_END) -> None

Clear line.

clear_rect

def clear_rect(x1: int,
               y1: int,
               x2: int,
               y2: int,
               ansi_code: str = "") -> None

Clear a rectangular region.

alternate_screen_on

def alternate_screen_on() -> None

Switch to alternate screen.

alternate_screen_off

def alternate_screen_off() -> None

Switch to primary screen.

set_mode

def set_mode(mode: int, value: bool = True, private: bool = False) -> None

Set terminal mode.

clear_mode

def clear_mode(mode, private: bool = False) -> None

Clear terminal mode.

switch_screen

def switch_screen(alt: bool) -> None

Switch between primary and alternate screen.

set_title

def set_title(title: str) -> None

Set terminal title.

set_icon_title

def set_icon_title(icon_title: str) -> None

Set terminal icon title.

bell

def bell() -> None

Terminal bell.

alignment_test

def alignment_test() -> None

Fill the screen with ‘E’ characters for alignment testing.

save_cursor

def save_cursor() -> None

Save cursor position and attributes.

restore_cursor

def restore_cursor() -> None

Restore cursor position and attributes.

set_scroll_region

def set_scroll_region(top: int, bottom: int) -> None

Set scroll region.

insert_lines

def insert_lines(count: int) -> None

Insert blank lines at cursor position.

delete_lines

def delete_lines(count: int) -> None

Delete lines at cursor position.

insert_characters

def insert_characters(count: int, ansi_code: str = "") -> None

Insert blank characters at cursor position.

delete_characters

def delete_characters(count: int) -> None

Delete characters at cursor position.

scroll_up

def scroll_up(count: int) -> None

Scroll content up within scroll region.

scroll_down

def scroll_down(count: int) -> None

Scroll content down within scroll region.

set_cursor

def set_cursor(x: Optional[int], y: Optional[int]) -> None

Set cursor position (alias for move_cursor).

set_column_mode

def set_column_mode(columns: int) -> None

Set terminal width for DECCOLM (column mode).

Arguments:

repeat_last_character

def repeat_last_character(count: int) -> None

Repeat the last printed character count times (REP command).

input_key

def input_key(char: str, modifier: int = constants.KEY_MOD_NONE) -> None

Convert key + modifier to standard control codes, then send to input().

input_fkey

def input_fkey(num: int, modifier: int = constants.KEY_MOD_NONE) -> None

Convert function key + modifier to standard control codes, then send to input().

input_numpad_key

def input_numpad_key(key: str) -> None

Convert numpad key to appropriate sequence based on DECNKM mode.

input

def input(data: str) -> None

Translate control codes based on terminal modes and send to PTY.

input_mouse

def input_mouse(x: int, y: int, button: int, event_type: str,
                modifiers: set[str]) -> None

Handle mouse input, cache position, and send appropriate sequence to PTY.

Arguments:

start_process

async def start_process() -> None

Start the child process with PTY or set up stream mode.

stop_process

def stop_process() -> None

Stop the child process and clean up.

bittty.constants

Constants for the terminal parser and emulator.

This module contains constants used in the terminal parsing and emulation logic, following standards like VT100, VT220, and xterm.

BEL

Bell

BS

Backspace

HT

Horizontal Tab

LF

Line Feed

CR

Carriage Return

SO

Shift Out (activate G1)

SI

Shift In (activate G0)

ESC

Escape

DEL

Delete

EBADF

Bad file descriptor

EINVAL

Invalid argument

bittty.pty_windows

Windows PTY implementation using pywinpty.

WinptyProcessWrapper Objects

class WinptyProcessWrapper()

Wrapper to provide subprocess.Popen-like interface for winpty PTY.

poll

def poll()

Check if process is still running.

wait

def wait()

Wait for process to complete.

returncode

@property
def returncode()

Get the return code.

pid

@property
def pid()

Get the process ID.

WindowsPTY Objects

class WindowsPTY(PTYBase)

Windows PTY implementation using pywinpty.

read

def read(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Read data from the PTY.

write

def write(data: str) -> int

Write data to the PTY.

resize

def resize(rows: int, cols: int) -> None

Resize the terminal.

close

def close() -> None

Close the PTY.

spawn_process

def spawn_process(command: str,
                  env: Optional[Dict[str, str]] = None) -> subprocess.Popen

Spawn a process attached to this PTY.

set_nonblocking

def set_nonblocking() -> None

Set the PTY to non-blocking mode for async operations.

read_async

async def read_async(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Async read from PTY. Returns empty string when no data available.

bittty.pty_base

Base PTY interface for terminal emulation.

This module defines the abstract interface that all platform-specific PTY implementations must follow.

PTYBase Objects

class PTYBase(ABC)

Abstract base class for PTY implementations.

read

@abstractmethod
def read(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Read data from the PTY.

write

@abstractmethod
def write(data: str) -> int

Write data to the PTY.

resize

@abstractmethod
def resize(rows: int, cols: int) -> None

Resize the terminal.

close

@abstractmethod
def close() -> None

Close the PTY.

closed

@property
def closed() -> bool

Check if PTY is closed.

spawn_process

@abstractmethod
def spawn_process(command: str,
                  env: Optional[Dict[str, str]] = None) -> subprocess.Popen

Spawn a process attached to this PTY.

set_nonblocking

@abstractmethod
def set_nonblocking() -> None

Set the PTY to non-blocking mode for async operations.

read_async

@abstractmethod
async def read_async(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Async read from PTY. Returns empty string when no data available.

bittty.style

Style Objects

@dataclass(frozen=True)
class Style()

merge

def merge(other: Style) -> Style

Merge another style into this one. The other style takes precedence.

diff

@lru_cache(maxsize=10000)
def diff(other: "Style") -> str

Generate minimal ANSI sequence to transition to another style.

get_background

@lru_cache(maxsize=10000)
def get_background(ansi: str) -> str

Extract just the background color as an ANSI sequence.

Arguments:

Returns:

ANSI sequence with just the background color, or empty string

merge_ansi_styles

@lru_cache(maxsize=10000)
def merge_ansi_styles(base: str, new: str) -> str

Merge two ANSI style sequences, returning a new ANSI sequence.

Arguments:

Returns:

Merged ANSI sequence

style_to_ansi

@lru_cache(maxsize=10000)
def style_to_ansi(style: Style) -> str

Convert a Style object back to an ANSI escape sequence.

Arguments:

Returns:

ANSI escape sequence string

bittty.pty_unix

Unix/Linux/macOS PTY implementation.

UnixPTY Objects

class UnixPTY(PTYBase)

Unix/Linux/macOS PTY implementation.

read

def read(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Read data from the PTY.

write

def write(data: str) -> int

Write data to the PTY.

resize

def resize(rows: int, cols: int) -> None

Resize the terminal using TIOCSWINSZ ioctl.

close

def close() -> None

Close the PTY file descriptors.

spawn_process

def spawn_process(command: str,
                  env: Optional[Dict[str, str]] = None) -> subprocess.Popen

Spawn a process attached to this PTY.

set_nonblocking

def set_nonblocking() -> None

Set the PTY to non-blocking mode for async operations.

read_async

async def read_async(size: int = constants.DEFAULT_PTY_BUFFER_SIZE) -> str

Async read from PTY. Returns empty string when no data available.

bittty.charsets

Character set mappings for terminal emulation.

get_charset

def get_charset(designator: str) -> dict

Get character set mapping for a designator.

bittty.color

Functions for generating ANSI escape sequences.

get_color_code

@lru_cache(maxsize=1024)
def get_color_code(fg: Optional[int] = None, bg: Optional[int] = None) -> str

Generate ANSI color code for 256-color palette.

Arguments:

Returns:

ANSI escape sequence for the colors

get_rgb_code

@lru_cache(maxsize=512)
def get_rgb_code(fg_rgb: Optional[Tuple[int, int, int]] = None,
                 bg_rgb: Optional[Tuple[int, int, int]] = None) -> str

Generate ANSI color code for RGB colors.

Arguments:

Returns:

ANSI escape sequence for the RGB colors

get_style_code

@lru_cache(maxsize=256)
def get_style_code(bold: bool = False,
                   dim: bool = False,
                   italic: bool = False,
                   underline: bool = False,
                   blink: bool = False,
                   reverse: bool = False,
                   strike: bool = False,
                   conceal: bool = False) -> str

Generate ANSI style code for text attributes.

Returns:

ANSI escape sequence for the styles

get_combined_code

@lru_cache(maxsize=2048)
def get_combined_code(fg: Optional[int] = None,
                      bg: Optional[int] = None,
                      fg_rgb: Optional[Tuple[int, int, int]] = None,
                      bg_rgb: Optional[Tuple[int, int, int]] = None,
                      bold: bool = False,
                      dim: bool = False,
                      italic: bool = False,
                      underline: bool = False,
                      blink: bool = False,
                      reverse: bool = False,
                      strike: bool = False,
                      conceal: bool = False) -> str

Generate a combined ANSI code for colors and styles.

RGB colors take precedence over palette colors.

Returns:

Complete ANSI escape sequence or empty string

reset_code

@lru_cache(maxsize=1)
def reset_code() -> str

Get the ANSI reset code.

get_basic_color_code

@lru_cache(maxsize=16)
def get_basic_color_code(color: int, is_bg: bool = False) -> str

Generate ANSI code for basic 16 colors (0-15).

Arguments:

Returns:

ANSI escape sequence

get_cursor_code

@lru_cache(maxsize=1)
def get_cursor_code() -> str

Get ANSI code for cursor display (reverse video).

get_clear_line_code

@lru_cache(maxsize=1)
def get_clear_line_code() -> str

Get ANSI code to clear to end of line.

reset_foreground_code

@lru_cache(maxsize=1)
def reset_foreground_code() -> str

Get ANSI code to reset foreground color only.

reset_background_code

@lru_cache(maxsize=1)
def reset_background_code() -> str

Get ANSI code to reset background color only.

reset_text_attributes

@lru_cache(maxsize=1)
def reset_text_attributes() -> str

Reset text attributes but preserve background color.