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.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.

bittty.parser

Parser module for terminal escape sequence processing.

This module provides a modular parser system that processes terminal escape sequences through specialized handlers:

The main Parser class coordinates all these handlers and maintains the state machine.

bittty.parser.escape

Simple escape sequence handlers.

Handles simple escape sequences that start with ESC followed by a single character (not CSI, OSC, or other multi-character sequences). These include cursor operations, character set designations, and terminal mode changes.

handle_ris

def handle_ris(terminal: Terminal, data: str) -> None

RIS - Reset to Initial State (ESC c).

handle_ind

def handle_ind(terminal: Terminal, data: str) -> None

IND - Index (ESC D) - Line feed.

handle_ri

def handle_ri(terminal: Terminal, data: str) -> None

RI - Reverse Index (ESC M) - Reverse line feed.

handle_decsc

def handle_decsc(terminal: Terminal, data: str) -> None

DECSC - Save Cursor (ESC 7).

handle_decrc

def handle_decrc(terminal: Terminal, data: str) -> None

DECRC - Restore Cursor (ESC 8).

handle_deckpam

def handle_deckpam(terminal: Terminal, data: str) -> None

DECKPAM - Application Keypad Mode (ESC =).

handle_deckpnm

def handle_deckpnm(terminal: Terminal, data: str) -> None

DECKPNM - Numeric Keypad Mode (ESC >).

handle_st

def handle_st(terminal: Terminal, data: str) -> None

ST - String Terminator (ESC ) - Already handled by sequence patterns.

handle_ss2

def handle_ss2(terminal: Terminal, data: str) -> None

SS2 - Single Shift 2 (ESC N).

handle_ss3

def handle_ss3(terminal: Terminal, data: str) -> None

SS3 - Single Shift 3 (ESC O).

handle_nel

def handle_nel(terminal: Terminal, data: str) -> None

NEL - Next Line (ESC E).

handle_hts

def handle_hts(terminal: Terminal, data: str) -> None

HTS - Horizontal Tab Set (ESC H).

handle_ri_alt

def handle_ri_alt(terminal: Terminal, data: str) -> None

Alternative RI implementation if needed.

dispatch_escape

def dispatch_escape(terminal: Terminal, data: str) -> bool

Main escape sequence dispatcher using O(1) lookup table.

Returns True if sequence was handled, False if it should be handled elsewhere (like charset designation sequences).

handle_charset_escape

def handle_charset_escape(terminal: Terminal, data: str) -> bool

Handle charset designation escape sequences like ESC(B.

Returns True if sequence was handled, False otherwise.

reset_terminal

def reset_terminal(terminal: Terminal) -> None

Reset terminal to initial state.

bittty.parser.dcs

DCS (Device Control String) sequence handlers.

Handles DCS sequences that start with ESC P. These are used for device-specific commands like sixel graphics, but current implementation is minimal.

dispatch_dcs

def dispatch_dcs(terminal: Terminal, string_buffer: str) -> None

Main DCS dispatcher.

Currently minimal implementation - primarily used for passthrough sequences like tmux or for potential future sixel graphics support.

bittty.parser.core

Core Parser class with state machine and sequence dispatching (fast state-specific scanners).

parse_string_sequence

@lru_cache(maxsize=300)
def parse_string_sequence(data: str, sequence_type: str) -> str

Strip prefix and terminator for OSC/DCS/APC/PM/SOS.

Parser Objects

class Parser()

State machine: GROUND → (CSI | STRING[osc|dcs|apc|pm|sos]) → GROUND Uses small, state-specific scanners for speed.

bittty.parser.csi

CSI (Control Sequence Introducer) sequence handlers.

Handles all CSI sequences that start with ESC[. These include cursor movement, screen clearing, styling, and terminal mode operations.

parse_csi_params

@lru_cache(maxsize=1000)
def parse_csi_params(data)

Parse CSI parameters when actually needed.

Arguments:

Returns:

dispatch_csi

def dispatch_csi(terminal, raw_csi_data)

BLAZING FAST CSI dispatcher with selective parsing and inlined handlers! 🚀

Revolutionary approach:

  1. No redundant parsing: SGR sequences pass raw to style system
  2. Selective parsing: Only parse parameters when actually needed
  3. Inlined handlers: Zero function call overhead
  4. Fast path detection: Check final char before any work

Arguments:

dispatch_sm_rm

def dispatch_sm_rm(terminal: Terminal, params: List[Optional[int]],
                   set_mode: bool) -> None

Handle SM/RM (Set/Reset Mode) for standard modes.

dispatch_sm_rm_private

def dispatch_sm_rm_private(terminal: Terminal, params: List[Optional[int]],
                           set_mode: bool) -> None

Handle SM/RM (Set/Reset Mode) for private modes (prefixed with ?).

get_private_mode_status

def get_private_mode_status(terminal: Terminal, mode: int) -> int

Get the status of a private mode for DECRQM response.

get_ansi_mode_status

def get_ansi_mode_status(terminal: Terminal, mode: int) -> int

Get the status of an ANSI mode for DECRQM response.

bittty.parser.osc

OSC (Operating System Command) sequence handlers.

Handles OSC sequences that start with ESC]. These include window title operations, color palette changes, and other system-level commands.

dispatch_osc

@lru_cache(maxsize=500)
def dispatch_osc(terminal: Terminal, string_buffer: str) -> None

BLAZING FAST OSC dispatcher with LRU caching and inlined handlers! 🚀

3.77x faster than the original through:

  1. LRU caching: Smart eviction beats custom cache clearing
  2. Inlined handlers: No function call overhead
  3. Fast paths: Handle 95% of OSC commands with minimal parsing

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.tcaps

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

TermInfo Objects

class TermInfo()

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.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,
                    stdin=None,
                    stdout=None)

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, with optional carriage return if DECNLM is enabled.

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

def scroll(lines: int) -> None

BLAZING FAST centralized scrolling with bulk operations! 🚀

Arguments:

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:

send

def send(data: str) -> None

Send data to PTY without flushing (for regular input/unsolicited messages).

respond

def respond(data: str) -> None

Send response to PTY with immediate flush (for query responses).

start_process

async def start_process() -> None

Start the child process with PTY.

stop_process

def stop_process() -> None

Stop the child process and clean up.

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.

scroll_region_up

def scroll_region_up(top: int, bottom: int, count: int) -> None

Scroll a specific region up by count lines. BLAZING FAST bulk operation!

scroll_region_down

def scroll_region_down(top: int, bottom: int, count: int) -> None

Scroll a specific region down by count lines. BLAZING FAST bulk operation!

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.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

VT

Vertical Tab

FF

Form Feed

CR

Carriage Return

SO

Shift Out (activate G1)

SI

Shift In (activate G0)

ESC

Escape

DEL

Delete

DA1_132_COLUMNS

132 column mode

DA1_PRINTER_PORT

Printer port

DA1_REGIS_GRAPHICS

ReGIS graphics

DA1_SIXEL_GRAPHICS

Sixel graphics

DA1_SELECTIVE_ERASE

Selective erase

DA1_USER_DEFINED_KEYS

User-defined keys (UDKs)

DA1_NATIONAL_REPLACEMENT_CHARSETS

National replacement character sets

DA1_TECH_CHARACTERS

Technical characters

DA1_LOCATOR_PORT

Locator port

DA1_TERMINAL_STATE_INTERROGATION

Terminal state interrogation

DA1_USER_WINDOWS

User windows

DA1_DUAL_SESSIONS

Dual sessions

DA1_HORIZONTAL_SCROLLING

Horizontal scrolling

DA1_ANSI_COLOR

ANSI color

DA1_GREEK_CHARSET

Greek character set

DA1_TURKISH_CHARSET

Turkish character set

DA1_ISO_LATIN2_CHARSET

ISO Latin-2 character set

DA1_PC_TERM

PC Term

DA1_SOFT_KEY_MAP

Soft key map

DA1_ASCII_EMULATION

ASCII emulation

EBADF

Bad file descriptor

EINVAL

Invalid argument

bittty.charsets

Character set mappings for terminal emulation.

get_charset

def get_charset(designator: str) -> dict

Get character set mapping for a designator.

bittty.pty.base

Base PTY interface for terminal emulation.

This module provides a concrete base class that works with file-like objects, with platform-specific subclasses overriding only the byte-level I/O methods.

PTY Objects

class PTY()

A generic PTY that lacks OS integration.

Uses StringIO if no file handles are provided, and subprocess to handle its children.

If you use this then you’ll have to

__init__

def __init__(from_process: Optional[BinaryIO] = None,
             to_process: Optional[BinaryIO] = None,
             rows: int = constants.DEFAULT_TERMINAL_HEIGHT,
             cols: int = constants.DEFAULT_TERMINAL_WIDTH)

Initialize PTY with file-like input/output sources.

Arguments:

read_bytes

def read_bytes(size: int) -> bytes

Read raw bytes. Override in subclasses for platform-specific I/O.

write_bytes

def write_bytes(data: bytes) -> int

Write raw bytes. Override in subclasses for platform-specific I/O.

read

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

Read data using the C incremental UTF-8 decoder (buffers split code points).

write

def write(data: str) -> int

Write string as UTF-8 bytes.

resize

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

Resize the terminal (base implementation just updates dimensions).

close

def close() -> None

Close the PTY streams.

closed

@property
def closed() -> bool

Check if PTY is closed.

spawn_process

def spawn_process(command: str, env: dict[str, str] = ENV) -> subprocess.Popen

Spawn a process connected to PTY streams.

read_async

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

Async read using thread pool executor.

Uses loop.run_in_executor() as a generic cross-platform approach. Unix PTY overrides this with more efficient file descriptor monitoring. Windows and other platforms use this thread pool implementation.

flush

def flush() -> None

Flush output.

bittty.pty

PTY implementations for terminal emulation.

bittty.pty.windows

Windows PTY implementation using pywinpty.

WinptyFileWrapper Objects

class WinptyFileWrapper()

File-like wrapper for winpty.PTY to work with base PTY class.

read

def read(size: int = -1) -> str

Read data as strings.

write

def write(data: str) -> int

Write string data.

close

def close() -> None

Close the PTY.

closed

@property
def closed() -> bool

Check if closed.

flush

def flush() -> None

Flush - no-op for winpty.

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(PTY)

Windows PTY implementation using pywinpty.

Note: This PTY operates in text mode - winpty handles UTF-8 internally. The read/write methods work directly with strings for performance, with bytes conversion only when needed for compatibility.

read

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

Read data directly from winpty (text mode, no UTF-8 splitting needed).

write

def write(data: str) -> int

Write string data directly to winpty (text mode).

resize

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

Resize the terminal.

spawn_process

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

Spawn a process attached to this PTY.

bittty.pty.unix

Unix/Linux/macOS PTY implementation.

UnixPTY Objects

class UnixPTY(PTY)

Unix/Linux/macOS PTY implementation.

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: dict[str, str] = UNIX_ENV) -> subprocess.Popen

Spawn a process attached to this PTY.

flush

def flush() -> None

Flush output using os.fsync() for real PTY file descriptor.

More efficient than generic flush() - ensures data is written through to the terminal device, not just buffered. Important for interactive terminal responsiveness.

read_async

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

Async read from PTY using efficient file descriptor monitoring.

Uses loop.add_reader() with file descriptors for maximum efficiency on Unix. This is the most performant approach since Unix supports select/poll on PTY fds.