Headless Terminal Implementation
Overview
This skill guides the implementation of headless terminal interfaces—programmatic wrappers that control shell sessions without a visible terminal UI. These implementations typically involve spawning shell processes, sending input (keystrokes, commands), and capturing output.
Approach
Step 1: Understand the Interface Contract
Before implementing, thoroughly read and understand the interface to be implemented:
-
Identify all required methods and their signatures
-
Note return types and expected behaviors
-
Check for optional methods or features
-
Look for existing implementations or tests that clarify expected behavior
Step 2: Select the Underlying Library
Common libraries for terminal emulation in Python:
Library Best For Trade-offs
pexpect
Interactive terminal emulation, pattern matching Higher-level API, good for expect/send patterns
pty
Low-level pseudo-terminal control More control, but more boilerplate
subprocess
Simple non-interactive commands Limited for true terminal emulation
Decision criteria to document:
-
Does the task require interactive input (passwords, prompts)? → pexpect
-
Does the task need low-level terminal control? → pty
-
Is it purely non-interactive command execution? → subprocess may suffice
Document the choice explicitly with reasoning for future maintainability.
Step 3: Handle Shell Configuration
When spawning a shell process, consider these configuration options:
Interactive mode (-i flag):
-
Ensures startup files (.bashrc , .bash_profile ) are sourced
-
Affects signal handling behavior
-
Required if tests verify startup file sourcing
Echo behavior:
-
echo=False in pexpect prevents input from appearing in output
-
Consider whether the use case needs to see echoed input
Terminal dimensions:
-
Default (24, 80) is standard terminal size
-
May affect output formatting for commands that detect terminal width
-
Document why specific dimensions are chosen
Step 4: Implement Core Functionality
Focus on implementing exactly what the interface requires:
-
Process lifecycle management - Spawn, check liveness, terminate
-
Input handling - Send keystrokes, handle special keys (Ctrl+C, etc.)
-
Output capture - Read available output, handle buffering
-
Resource cleanup - Proper termination, context manager support
Step 5: Address Edge Cases
Explicitly handle these scenarios:
Edge Case Handling Strategy
Shell exits unexpectedly Check process liveness before operations
Long-running commands Configurable timeouts, background execution support
Non-ASCII characters Explicit encoding handling (UTF-8 typically)
Race conditions Appropriate delays between send and read operations
Command cancellation Proper signal handling (SIGINT for Ctrl+C)
Verification Strategies
Testing Approach
Create focused tests for each capability:
-
Basic functionality - Import, instantiation
-
Non-interactive commands - Simple command execution and output
-
Interactive commands - Commands requiring input
-
Signal handling - Ctrl+C cancellation
-
Shell state - Variable persistence, environment
-
Startup configuration - Verify startup files are sourced
-
Background execution - Long-running command handling
Verification Guidelines
-
Run the test suite to verify all functionality
-
Avoid redundant manual verification after tests pass
-
If a single comprehensive test file exists, use it rather than creating separate verification scripts
Common Pitfalls
Scope Management
-
Implement only what is required - Avoid adding unrequested features like extra utility methods
-
If additional methods seem useful, note them but don't implement unless asked
-
Context manager support is often expected but verify it's in the interface
Efficiency
-
Avoid redundant searches - If searching for a class, one targeted search is sufficient
-
Don't duplicate verification - If tests pass, additional manual checks are unnecessary
-
Don't create example files unless explicitly requested
Documentation Gaps
Common decisions that should be documented but often aren't:
-
Why the chosen library over alternatives
-
Why specific shell flags are used
-
Why specific default values were selected
-
Thread safety considerations (or explicit statement that it's not thread-safe)
Error Handling
Consider and document handling for:
-
Invalid keystroke sequences
-
Terminal process termination
-
Timeout scenarios
-
Permission issues
Implementation Checklist
Before considering the implementation complete:
-
All interface methods are implemented
-
Library choice is documented with reasoning
-
Configuration decisions are justified
-
Edge cases are explicitly handled or documented as out of scope
-
Tests cover all required functionality
-
No unrequested features were added
-
Resource cleanup is properly implemented