Pure Python module for spawning child applications and controlling them automatically with expect-like functionality.
—
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.
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
"""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."""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
"""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()}")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()}")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()}")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}")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()}")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)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}")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('>>> ')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 herefrom 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}")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()}")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}")# 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)
)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()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