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

utilities.mddocs/

Utilities and Helpers

Utility functions for common tasks including executable finding, command line parsing, and file system operations that support pexpect's functionality.

Capabilities

Executable Location

Functions for finding executable files in the system PATH.

def which(filename, env=None):
    """
    Find executable file in PATH.
    
    Searches for the given filename in the directories listed in the PATH
    environment variable and returns the full path to the first executable
    file found.
    
    Parameters:
    - filename (str): Name of executable to find
    - env (dict): Environment variables (uses os.environ if None)
    
    Returns:
    str or None: Full path to executable, or None if not found
    """

def is_executable_file(path):
    """
    Check if a file is executable.
    
    Checks that the path points to an executable regular file or symlink
    to an executable file. Handles platform-specific executable detection.
    
    Parameters:
    - path (str): Path to check
    
    Returns:
    bool: True if file exists and is executable, False otherwise
    """

Command Line Parsing

Functions for parsing and processing command line strings.

def split_command_line(command_line):
    """
    Split command line into argument list.
    
    Parses a command line string into a list of arguments, handling
    quoted strings and escape sequences properly.
    
    Parameters:
    - command_line (str): Command line string to parse
    
    Returns:
    list: List of command arguments
    """

Low-Level I/O Utilities

Internal utility functions for handling I/O operations with proper interrupt handling.

def select_ignore_interrupts(iwtd, owtd, ewtd, timeout=None):
    """
    Wrapper around select.select() that ignores interrupts.
    
    Provides robust select() operation that retries on EINTR,
    making pexpect more reliable on systems with signal handling.
    
    Parameters:
    - iwtd (list): Input wait list (file descriptors to read)
    - owtd (list): Output wait list (file descriptors to write)  
    - ewtd (list): Error wait list (file descriptors for exceptions)
    - timeout (float): Timeout in seconds (None for blocking)
    
    Returns:
    tuple: (readable, writable, exceptional) file descriptor lists
    """

def poll_ignore_interrupts(fd_list, timeout=None):
    """
    Wrapper around select.poll() that ignores interrupts.
    
    Provides robust poll() operation that retries on EINTR.
    
    Parameters:
    - fd_list (list): List of (fd, event_mask) tuples
    - timeout (int): Timeout in milliseconds (None for blocking)
    
    Returns:
    list: List of (fd, event) tuples for ready file descriptors
    """

Type Compatibility

Constants and utilities for Python 2/3 compatibility.

string_types: tuple
    """
    Tuple of string types for isinstance() checks.
    (str,) on Python 3, (unicode, str) on Python 2.
    """

Usage Examples

Finding Executables

import pexpect

# Find common executables
ssh_path = pexpect.which('ssh')
if ssh_path:
    print(f"SSH found at: {ssh_path}")
    child = pexpect.spawn(ssh_path)
else:
    print("SSH not found in PATH")

# Check multiple possible names
for cmd in ['python3', 'python', 'python2']:
    python_path = pexpect.which(cmd)
    if python_path:
        print(f"Python found: {python_path}")
        break
else:
    print("No Python interpreter found")

# Use custom environment
custom_env = os.environ.copy()
custom_env['PATH'] = '/usr/local/bin:/usr/bin:/bin'
git_path = pexpect.which('git', env=custom_env)
print(f"Git in custom PATH: {git_path}")

Validating Executable Files

import pexpect
import os

# Check if file is executable before spawning
script_path = '/usr/local/bin/myscript.sh'

if os.path.exists(script_path):
    if pexpect.is_executable_file(script_path):
        print(f"{script_path} is executable")
        child = pexpect.spawn(script_path)
    else:
        print(f"{script_path} exists but is not executable")
else:
    print(f"{script_path} does not exist")

# Check multiple candidates
candidates = [
    '/usr/bin/python3',
    '/usr/local/bin/python3',
    '/opt/python/bin/python3'
]

for candidate in candidates:
    if pexpect.is_executable_file(candidate):
        print(f"Using Python: {candidate}")
        python = pexpect.spawn(candidate)
        break
else:
    print("No valid Python executable found")

Command Line Parsing

import pexpect

# Parse complex command lines
command_line = 'ssh -o "StrictHostKeyChecking no" user@host "ls -la /tmp"'
args = pexpect.split_command_line(command_line)
print("Parsed arguments:")
for i, arg in enumerate(args):
    print(f"  {i}: {arg}")

# Use with spawn
child = pexpect.spawn(args[0], args[1:])

# Handle commands with quotes and escapes
complex_cmd = r'echo "Hello \"World\"" && ls -la'
parsed = pexpect.split_command_line(complex_cmd)
print(f"Complex command parsed: {parsed}")

Building Dynamic Commands

import pexpect

def find_and_run(program, args=None, **kwargs):
    """
    Find program in PATH and run it with pexpect.
    """
    program_path = pexpect.which(program)
    if not program_path:
        raise FileNotFoundError(f"Program '{program}' not found in PATH")
    
    if not pexpect.is_executable_file(program_path):
        raise PermissionError(f"Program '{program_path}' is not executable")
    
    # Build command
    if args:
        return pexpect.spawn(program_path, args, **kwargs)
    else:
        return pexpect.spawn(program_path, **kwargs)

# Usage examples
try:
    # Run git with arguments
    git = find_and_run('git', ['status', '--porcelain'])
    git.expect(pexpect.EOF)
    print("Git status:", git.before.decode())
    git.close()
    
    # Run Python interpreter
    python = find_and_run('python3', ['-c', 'print("Hello from Python")'])
    python.expect(pexpect.EOF)
    print("Python output:", python.before.decode())
    python.close()
    
except (FileNotFoundError, PermissionError) as e:
    print(f"Error: {e}")

Cross-Platform Executable Search

import pexpect
import sys
import os

def find_editor():
    """Find a suitable text editor across platforms."""
    
    if sys.platform == 'win32':
        # Windows editors
        editors = ['notepad.exe', 'wordpad.exe', 'write.exe']
    else:
        # Unix/Linux editors
        editors = ['nano', 'vim', 'vi', 'emacs', 'gedit']
    
    for editor in editors:
        editor_path = pexpect.which(editor)
        if editor_path and pexpect.is_executable_file(editor_path):
            return editor_path
    
    return None

# Find and use editor
editor = find_editor()
if editor:
    print(f"Found editor: {editor}")
    # Could spawn editor here
else:
    print("No suitable editor found")

Environment-Aware Executable Search

import pexpect
import os

def find_in_custom_paths(program, extra_paths=None):
    """
    Find executable in PATH plus additional custom paths.
    """
    # Try standard PATH first
    result = pexpect.which(program)
    if result:
        return result
    
    # Try additional paths
    if extra_paths:
        original_path = os.environ.get('PATH', '')
        try:
            # Temporarily extend PATH
            extended_path = os.pathsep.join(extra_paths + [original_path])
            custom_env = os.environ.copy()
            custom_env['PATH'] = extended_path
            
            result = pexpect.which(program, env=custom_env)
            return result
            
        finally:
            # PATH is restored automatically since we used a copy
            pass
    
    return None

# Usage
extra_search_paths = [
    '/opt/local/bin',
    '/usr/local/sbin',
    '~/bin'
]

program = find_in_custom_paths('special_tool', extra_search_paths)
if program:
    print(f"Found special_tool at: {program}")

Robust Command Execution

import pexpect
import shlex

def safe_spawn(command_line, **kwargs):
    """
    Safely spawn a command with proper argument parsing.
    """
    # Parse the command line
    try:
        if isinstance(command_line, str):
            args = pexpect.split_command_line(command_line)
        else:
            args = command_line
        
        if not args:
            raise ValueError("Empty command")
        
        program = args[0]
        program_args = args[1:] if len(args) > 1 else []
        
        # Find the executable
        program_path = pexpect.which(program)
        if not program_path:
            raise FileNotFoundError(f"Command '{program}' not found")
        
        # Verify it's executable
        if not pexpect.is_executable_file(program_path):
            raise PermissionError(f"Command '{program_path}' is not executable")
        
        # Spawn with full path
        return pexpect.spawn(program_path, program_args, **kwargs)
        
    except Exception as e:
        raise RuntimeError(f"Failed to spawn '{command_line}': {e}")

# Usage examples
try:
    # Safe command execution
    child = safe_spawn('ls -la /tmp')
    child.expect(pexpect.EOF)
    print("Directory listing:")
    print(child.before.decode())
    child.close()
    
    # With complex quoting
    child = safe_spawn('find /tmp -name "*.tmp" -type f')
    child.expect(pexpect.EOF)
    print("Temp files:")
    print(child.before.decode())
    child.close()
    
except RuntimeError as e:
    print(f"Command failed: {e}")

String Type Compatibility

import pexpect
from pexpect.utils import string_types

def handle_mixed_input(data):
    """
    Handle both string and bytes input across Python versions.
    """
    if isinstance(data, string_types):
        # It's a string type (str in Python 3, str/unicode in Python 2)
        print(f"Got string: {data}")
        return data.encode('utf-8')
    elif isinstance(data, bytes):
        print(f"Got bytes: {data}")
        return data
    else:
        raise TypeError(f"Expected string or bytes, got {type(data)}")

# Usage
text_data = "Hello, World!"
byte_data = b"Hello, World!"

processed_text = handle_mixed_input(text_data)
processed_bytes = handle_mixed_input(byte_data)

print(f"Processed text: {processed_text}")
print(f"Processed bytes: {processed_bytes}")

Integration Examples

Command Validation Pipeline

import pexpect
import os

class CommandValidator:
    """Validate and prepare commands for pexpect execution."""
    
    def __init__(self, extra_paths=None):
        self.extra_paths = extra_paths or []
    
    def validate_command(self, command_line):
        """
        Validate that a command can be executed.
        
        Returns:
        tuple: (program_path, args) if valid
        
        Raises:
        ValueError: If command is invalid or not found
        """
        # Parse command line
        args = pexpect.split_command_line(command_line)
        if not args:
            raise ValueError("Empty command line")
        
        program = args[0]
        program_args = args[1:]
        
        # Find executable
        program_path = pexpect.which(program)
        if not program_path and self.extra_paths:
            # Try extra paths
            old_path = os.environ.get('PATH', '')
            try:
                new_path = os.pathsep.join(self.extra_paths + [old_path])
                env = os.environ.copy()
                env['PATH'] = new_path
                program_path = pexpect.which(program, env=env)
            finally:
                pass
        
        if not program_path:
            raise ValueError(f"Command '{program}' not found in PATH")
        
        if not pexpect.is_executable_file(program_path):
            raise ValueError(f"File '{program_path}' is not executable")
        
        return program_path, program_args
    
    def safe_spawn(self, command_line, **kwargs):
        """Spawn command after validation."""
        program_path, args = self.validate_command(command_line)
        return pexpect.spawn(program_path, args, **kwargs)

# Usage
validator = CommandValidator(extra_paths=['/opt/local/bin', '/usr/local/sbin'])

try:
    child = validator.safe_spawn('git status --porcelain')
    child.expect(pexpect.EOF)
    print("Git status output:")
    print(child.before.decode())
    child.close()
    
except ValueError as e:
    print(f"Command validation failed: {e}")

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