CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pexpect

Pure Python module for spawning child applications and controlling them automatically with expect-like functionality.

Pending
Overview
Eval results
Files

repl-automation.mddocs/

REPL and Shell Automation

Tools for automating read-eval-print loops and interactive shells with customizable prompts and command execution. The REPLWrapper class provides high-level automation for interactive interpreters and shells.

Capabilities

REPL Wrapper Class

High-level wrapper for automating interactive shells and interpreters with consistent prompt handling.

class REPLWrapper:
    """
    Wrapper for read-eval-print loops (REPLs) and interactive shells.
    
    Provides a high-level interface for automating interactive interpreters
    by managing prompts and executing commands reliably.
    """
    
    def __init__(self, cmd_or_spawn, orig_prompt, prompt_change=None,
                 new_prompt=None, continuation_prompt=None, extra_init_cmd=""):
        """
        Initialize REPL wrapper.
        
        Parameters:
        - cmd_or_spawn (str or spawn): Command to start REPL or existing spawn instance
        - orig_prompt (str): Regular expression matching the original prompt
        - prompt_change (str): Command to change the prompt (optional)
        - new_prompt (str): New prompt pattern after change (optional)
        - continuation_prompt (str): Pattern for continuation prompts (optional)
        - extra_init_cmd (str): Additional initialization commands
        """
    
    def run_command(self, command, timeout=-1, async_=False):
        """
        Execute a command in the REPL and return output.
        
        Parameters:
        - command (str): Command to execute
        - timeout (int): Timeout in seconds (-1 for default)
        - async_ (bool): Execute asynchronously (experimental, Python 3.4+ only)
        
        Returns:
        str: Command output (text between command and next prompt)
        
        Raises:
        - TIMEOUT: If command execution times out
        - EOF: If REPL session ends unexpectedly
        """
    
    def set_prompt(self, orig_prompt, prompt_change, new_prompt):
        """
        Change the REPL prompt to a new pattern.
        
        Parameters:
        - orig_prompt (str): Current prompt pattern
        - prompt_change (str): Command to change prompt
        - new_prompt (str): New prompt pattern
        """

REPL Constants

PEXPECT_PROMPT: str = '[PEXPECT_PROMPT>'
    """Default unique prompt used by REPLWrapper for reliable operation."""

PEXPECT_CONTINUATION_PROMPT: str = '[PEXPECT_PROMPT+'
    """Default continuation prompt for multi-line commands."""

Convenience Functions

Pre-configured REPL wrappers for common interpreters and shells.

def python(command=sys.executable):
    """
    Start a Python shell and return a REPLWrapper object.
    
    Parameters:
    - command (str): Python executable path (default: sys.executable)
    
    Returns:
    REPLWrapper: Configured Python REPL wrapper
    """

def bash(command="bash"):
    """
    Start a bash shell and return a REPLWrapper object.
    
    Parameters:
    - command (str): Bash executable path (default: "bash")
    
    Returns:
    REPLWrapper: Configured bash shell wrapper
    """

def zsh(command="zsh", args=("--no-rcs", "-V", "+Z")):
    """
    Start a zsh shell and return a REPLWrapper object.
    
    Parameters:
    - command (str): Zsh executable path (default: "zsh")
    - args (tuple): Command line arguments for zsh
    
    Returns:
    REPLWrapper: Configured zsh shell wrapper
    """

Common REPL Patterns

Python REPL Automation

import pexpect
from pexpect.replwrap import REPLWrapper

# Start Python REPL
python_repl = REPLWrapper('python', '>>> ', None, None)

# Execute Python commands
result = python_repl.run_command('2 + 2')
print(f"2 + 2 = {result.strip()}")

result = python_repl.run_command('import sys; sys.version')
print(f"Python version: {result.strip()}")

# Multi-line command
code = """
def greet(name):
    return f"Hello, {name}!"

greet("World")
"""
result = python_repl.run_command(code)
print(f"Function result: {result.strip()}")

Bash Shell Automation

from pexpect.replwrap import REPLWrapper

# Start bash shell with custom prompt
bash = REPLWrapper('bash', r'\$ ', 'PS1="{}" PS2="{}" '.format(
    pexpect.replwrap.PEXPECT_PROMPT,
    pexpect.replwrap.PEXPECT_CONTINUATION_PROMPT),
    pexpect.replwrap.PEXPECT_PROMPT.strip())

# Execute shell commands
result = bash.run_command('echo "Hello, World!"')
print(f"Echo result: {result.strip()}")

result = bash.run_command('ls -la | head -5')
print(f"Directory listing:\n{result}")

result = bash.run_command('date')
print(f"Current date: {result.strip()}")

Node.js REPL Automation

from pexpect.replwrap import REPLWrapper

# Start Node.js REPL
node_repl = REPLWrapper('node', '> ', None, None)

# Execute JavaScript commands
result = node_repl.run_command('Math.PI')
print(f"Pi value: {result.strip()}")

result = node_repl.run_command('console.log("Hello from Node.js")')
print(f"Console output: {result.strip()}")

# Define and use a function
js_code = """
function factorial(n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}
factorial(5)
"""
result = node_repl.run_command(js_code)
print(f"Factorial result: {result.strip()}")

R Statistical Computing

from pexpect.replwrap import REPLWrapper

# Start R REPL
r_repl = REPLWrapper('R --vanilla', '> ', None, None)

# Execute R commands
result = r_repl.run_command('x <- c(1, 2, 3, 4, 5)')
print(f"Vector assignment: {result.strip()}")

result = r_repl.run_command('mean(x)')
print(f"Mean: {result.strip()}")

result = r_repl.run_command('summary(x)')
print(f"Summary:\n{result}")

Advanced REPL Usage

Custom Prompt Management

from pexpect.replwrap import REPLWrapper
import pexpect

# Start shell with complex prompt setup
shell = REPLWrapper(
    'bash',
    orig_prompt=r'\$ ',
    prompt_change='PS1="{}" PS2="{}" '.format(
        '[MYPROMPT>', '[MYCONT>'
    ),
    new_prompt=r'\[MYPROMPT>\s*'
)

# Execute commands with custom prompt
result = shell.run_command('whoami')
print(f"Current user: {result.strip()}")

Multi-line Command Handling

from pexpect.replwrap import REPLWrapper

# Python REPL with multi-line support
python = REPLWrapper('python', '>>> ', None, None)

# Multi-line function definition
multiline_code = '''
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

# Calculate fibonacci numbers
for i in range(6):
    print(f"fib({i}) = {fibonacci(i)}")
'''

result = python.run_command(multiline_code)
print("Fibonacci sequence:")
print(result)

Error Handling in REPL

from pexpect.replwrap import REPLWrapper
import pexpect

python = REPLWrapper('python', '>>> ', None, None)

try:
    # This will cause a syntax error
    result = python.run_command('print("Hello World"')
    print("Result:", result)
except pexpect.TIMEOUT:
    print("Command timed out - possibly waiting for input")
    # Try to recover by sending closing parenthesis
    python.child.sendline(')')
    python.child.expect('>>> ')
    
try:
    # This will cause a runtime error
    result = python.run_command('1 / 0')
    print("Division result:", result)
except Exception as e:
    print(f"Error occurred: {e}")

Using Existing Spawn Instance

import pexpect
from pexpect.replwrap import REPLWrapper

# Create spawn instance first
child = pexpect.spawn('python')

# Create REPLWrapper from existing spawn
python = REPLWrapper(child, '>>> ', None, None)

# Use as normal
result = python.run_command('print("Using existing spawn")')
print(result)

# Access underlying spawn if needed
python.child.sendline('import os')
python.child.expect('>>> ')

Interactive Session with Timeout

from pexpect.replwrap import REPLWrapper
import pexpect

# Start REPL with custom timeout
repl = REPLWrapper('python', '>>> ', None, None)

try:
    # Long-running command with timeout
    result = repl.run_command('import time; time.sleep(2); print("Done")', 
                             timeout=5)
    print(f"Result: {result.strip()}")
    
except pexpect.TIMEOUT:
    print("Command timed out")
    # Could interrupt or terminate here

Database CLI Automation

from pexpect.replwrap import REPLWrapper

# Example with MySQL client (if available)
try:
    mysql = REPLWrapper('mysql -u root -p', 'mysql> ', None, None)
    
    # Execute SQL commands
    result = mysql.run_command('SHOW DATABASES;')
    print("Databases:")
    print(result)
    
    result = mysql.run_command('SELECT NOW();')
    print(f"Current time: {result}")
    
except Exception as e:
    print(f"Database connection failed: {e}")

REPL with Custom Initialization

from pexpect.replwrap import REPLWrapper

# Python REPL with custom initialization
init_commands = """
import math
import sys
import os
print("Custom Python REPL initialized")
"""

python = REPLWrapper('python', '>>> ', None, None, extra_init_cmd=init_commands)

# Modules are already imported
result = python.run_command('math.sqrt(16)')
print(f"Square root: {result.strip()}")

result = python.run_command('sys.version[:10]')
print(f"Python version: {result.strip()}")

Jupyter Console Automation

from pexpect.replwrap import REPLWrapper

# Start Jupyter console (if available)
try:
    jupyter = REPLWrapper('jupyter console', 'In \\[\\d+\\]: ', None, None)
    
    # Execute Jupyter commands
    result = jupyter.run_command('import numpy as np')
    print("NumPy imported")
    
    result = jupyter.run_command('np.array([1, 2, 3, 4, 5]).mean()')
    print(f"Array mean: {result.strip()}")
    
except Exception as e:
    print(f"Jupyter console not available: {e}")

Best Practices

Reliable Prompt Detection

# Use specific, unique prompts when possible
unique_prompt = '[MYAPP_PROMPT_{}]'.format(os.getpid())

repl = REPLWrapper(
    'myapp',
    orig_prompt='> ',
    prompt_change=f'set_prompt "{unique_prompt}"',
    new_prompt=re.escape(unique_prompt)
)

Resource Management

from pexpect.replwrap import REPLWrapper

# Use try/finally for cleanup
repl = None
try:
    repl = REPLWrapper('python', '>>> ', None, None)
    
    # Your REPL operations here
    result = repl.run_command('print("Hello")')
    
finally:
    if repl and hasattr(repl, 'child'):
        repl.child.close()

Context Manager Pattern

import contextlib
from pexpect.replwrap import REPLWrapper

@contextlib.contextmanager
def repl_session(command, prompt):
    """Context manager for REPL sessions."""
    repl = REPLWrapper(command, prompt, None, None)
    try:
        yield repl
    finally:
        if hasattr(repl, 'child'):
            repl.child.close()

# Usage
with repl_session('python', '>>> ') as python:
    result = python.run_command('2 + 2')
    print(f"Result: {result.strip()}")

Install with Tessl CLI

npx tessl i tessl/pypi-pexpect

docs

alternative-spawning.md

index.md

pattern-matching.md

process-control.md

repl-automation.md

ssh-operations.md

utilities.md

tile.json