Programmatically drive interactive terminal applications via PTY.
ptydriver provides a simple, reliable way to control interactive CLI applications (TUI apps, REPLs, shells, AI agents, etc.) by spawning them in a PTY and maintaining a virtual terminal screen.
Key Features:
- Drive any interactive CLI application programmatically
- Send keystrokes and control sequences
- Read terminal output with virtual screen tracking
- Manage multiple instances in parallel with ProcessPool
- No mocks - real process control via PTY
pip install ptydriverOr with uv:
uv pip install ptydriverfrom ptydriver import PtyProcess, Keys
# Drive any interactive CLI
with PtyProcess(["python3"]) as proc:
proc.send("print('hello')")
proc.wait_for("hello")
print("Success!")from ptydriver import PtyProcess, Keys
# Control fzf
with PtyProcess(["bash", "--norc"]) as proc:
proc.wait_for("$")
# Launch fzf with some input
proc.send("echo -e 'apple\\nbanana\\ncherry' | fzf")
# Type to filter
proc.send_raw("an") # Filters to "banana"
# Navigate and select
proc.send_raw(Keys.DOWN)
proc.send_raw(Keys.ENTER)
proc.wait_for("banana")from ptydriver import ProcessPool
# Manage multiple processes
with ProcessPool() as pool:
# Spawn 3 instances
for i in range(3):
pool.add(["python3"], name=f"python-{i}")
# Wait for all to be ready
for proc in pool:
proc.wait_for(">>>")
# Send command to all
pool.broadcast("print('Hello from all!')")
# Check all have output
for proc in pool:
proc.wait_for("Hello from all!")The main class for driving a single interactive process.
PtyProcess(
command: List[str], # Command and arguments
width: int = 120, # Terminal width
height: int = 40, # Terminal height
timeout: int = 5, # Default timeout for operations
env: Dict[str, str] = None, # Environment variables
cwd: str = None, # Working directory
)Methods:
send(text, delay=0.15, press_enter=True)- Send text to processsend_raw(sequence, delay=0.15)- Send raw escape sequencessend_bytes(data, delay=0.15)- Send raw bytes directlyget_content()- Get current screen content as stringget_screen()- Get screen as list of linesget_cursor_position()- Get (x, y) cursor positionset_size(width, height)- Resize the terminal windowwait_for(pattern, timeout=None)- Wait for text or regex pattern to appearexpect_any(patterns, timeout=None)- Wait for any of the given patterns (returns index, match)expect_sequence(patterns, timeout=None)- Wait for a sequence of patternscontains(pattern)- Check if text or regex is on screenis_alive()- Check if process is runningcleanup()- Clean up process (called automatically in context manager)
wait_for and contains support compiled regex patterns:
import re
from ptydriver import PtyProcess
with PtyProcess(["bash"]) as proc:
# Wait for a prompt matching regex
proc.wait_for(re.compile(r"[\$#]"))Wait for one of multiple possibilities or a specific sequence:
# Expect any of the options
index, match = proc.expect_any(["Success", "Error", re.compile(r"Code: \d+")])
if index == 0:
print("Succeeded!")
# Expect a sequence of events
proc.expect_sequence([
"Initializing...",
re.compile(r"Loading data: \d+%"),
"Done"
])Manage multiple PtyProcess instances for parallel execution.
pool = ProcessPool()
proc = pool.add(["bash"], name="my-bash")
pool.broadcast("echo hello") # Send to all
pool.cleanup()Common key sequences for terminal interaction.
from ptydriver import Keys
# Control characters
Keys.CTRL_C # Interrupt
Keys.CTRL_D # EOF
Keys.CTRL_L # Clear screen
# Arrow keys
Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT
# Special keys
Keys.ENTER, Keys.TAB, Keys.ESCAPE, Keys.BACKSPACE
# Function keys
Keys.F1 through Keys.F12
# Navigation
Keys.HOME, Keys.END, Keys.PAGE_UP, Keys.PAGE_DOWN
# Helper methods
Keys.ctrl('c') # Generate Ctrl+C
Keys.alt('f') # Generate Alt+F
Keys.meta('x') # Generate Meta+X (same as alt)
Keys.repeat(Keys.DOWN, 5) # Repeat key 5 times- Testing TUI applications: Automate testing of ncurses apps, vim, fzf, etc.
- Driving AI agents: Programmatically control CLI-based AI tools
- Shell automation: Script complex interactive shell sessions
- REPL interaction: Automate Python, Node.js, or other REPLs
- Parallel execution: Run multiple instances of interactive tools
- Python 3.8+
- macOS or Linux
- ptyprocess
- pyte
MIT