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:
term_name
- The terminal name (e.g., “xterm-256color”).overrides
- A string of user-defined overrides, like in tmux.conf.
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
char
- Character to storestyle_or_ansi
- Either a Style object or ANSI string (for backward compatibility)
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:
terminal
- A Terminal object that the parser will manipulate.
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:
columns
- 80 for normal mode, 132 for wide mode
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:
x
- 1-based mouse column.y
- 1-based mouse row.button
- The button that was pressed/released.event_type
- “press”, “release”, or “move”.modifiers
- A set of active modifiers (“shift”, “meta”, “ctrl”).
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:
ansi
- ANSI escape sequence
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:
base
- Base ANSI sequencenew
- New ANSI sequence to merge
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:
style
- Style object to convert
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:
fg
- Foreground color (0-255) or Nonebg
- Background color (0-255) or None
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:
fg_rgb
- Foreground RGB tuple (r, g, b) or Nonebg_rgb
- Background RGB tuple (r, g, b) or None
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:
color
- Color index (0-7 for normal, 8-15 for bright)is_bg
- True for background, False for foreground
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.