CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-command-runner

Platform agnostic command and shell execution tool with timeout handling, live output capture, and UAC/sudo privilege elevation

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

core-execution.mddocs/

Core Command Execution

Primary command execution functionality providing a comprehensive interface for running shell commands with advanced timeout handling, encoding support, and flexible output redirection. This is the main entry point for command_runner functionality.

Capabilities

Main Command Execution

Executes shell commands with extensive configuration options for timeout handling, output capture, and cross-platform compatibility. Supports both synchronous and asynchronous execution patterns.

def command_runner(
    command,                    # Union[str, List[str]] - Command to execute
    valid_exit_codes=False,     # Union[List[int], bool] - Accepted exit codes
    timeout=3600,               # Optional[int] - Timeout in seconds (default: 3600)
    shell=False,                # bool - Use shell execution
    encoding=None,              # Optional[Union[str, bool]] - Output encoding
    stdin=None,                 # Optional[Union[int, str, Callable, queue.Queue]]
    stdout=None,                # Optional[Union[int, str, Callable, queue.Queue]]
    stderr=None,                # Optional[Union[int, str, Callable, queue.Queue]]
    no_close_queues=False,      # Optional[bool] - Keep queues open after execution
    windows_no_window=False,    # bool - Hide console window on Windows
    live_output=False,          # bool - Display output during execution
    method="monitor",           # str - Capture method ("monitor" or "poller")
    check_interval=0.05,        # float - Polling interval in seconds
    stop_on=None,               # Callable - Custom stop condition function
    on_exit=None,               # Callable - Callback executed after completion
    process_callback=None,      # Callable - Callback with process information
    split_streams=False,        # bool - Return separate stdout/stderr tuples
    silent=False,               # bool - Suppress error logging
    priority=None,              # Union[int, str] - Process priority level
    io_priority=None,           # str - IO priority level
    heartbeat=0,                # int - Heartbeat logging interval (seconds)
    **kwargs                    # Any - Additional subprocess.Popen arguments
):
    """
    Execute shell commands with comprehensive error handling and output capture.
    
    This function provides a robust interface for command execution that handles:
    - Cross-platform command formatting and execution
    - Reliable timeout enforcement with process tree termination
    - Flexible output redirection (files, callbacks, queues, live display)
    - Comprehensive encoding handling for different platforms
    - Process priority and IO priority management
    - Partial output capture on interruptions and timeouts
    
    Args:
        command: Command to execute. Can be string (shell=True) or list of strings.
                String commands are automatically split on Unix when shell=False.
        valid_exit_codes: Exit codes to treat as successful. False (default) means 
                         only 0 is valid. True means any exit code is valid.
                         List means specific codes are valid.
        timeout: Maximum execution time in seconds. None disables timeout.
        shell: Whether to execute via shell. Required for complex shell commands.
        encoding: Output encoding. None uses platform default (utf-8/cp437).
                 False returns raw bytes. String specifies custom encoding.
        stdin: Input redirection. Can be subprocess constant, string filename,
               callable function, or queue.Queue object.
        stdout: Output redirection. None captures to return value, False discards,
                string filename writes to file, callable sends to function,
                queue.Queue sends to queue.
        stderr: Error output redirection. Similar to stdout. None redirects to stdout.
        no_close_queues: Keep queues open after execution (don't send None sentinel).
        windows_no_window: Hide console window on Windows (Python 3.7+).
        live_output: Show command output on screen during execution.
        method: Capture method. "monitor" (default) uses lower CPU but limited features.
                "poller" enables queues/callbacks and partial output on interrupts.
        check_interval: Polling interval for timeout checks and output reading.
        stop_on: Optional function returning bool to stop execution early.
        on_exit: Optional callback function executed after command completion.
        process_callback: Optional callback receiving subprocess.Popen object.
        split_streams: Return (exit_code, stdout, stderr) instead of (exit_code, output).
        silent: Suppress error logging (debug logs still shown).
        priority: Process priority. String values: "verylow", "low", "normal", "high", "rt".
                 Unix also accepts int values -20 to 20.
        io_priority: IO priority. String values: "low", "normal", "high".
        heartbeat: Log heartbeat message every N seconds during execution.
        **kwargs: Additional arguments passed to subprocess.Popen.
    
    Returns:
        Tuple[int, Optional[Union[bytes, str]]] - (exit_code, output) by default
        Tuple[int, Optional[Union[bytes, str]], Optional[Union[bytes, str]]] - 
            (exit_code, stdout, stderr) when split_streams=True
            
    Raises:
        TimeoutExpired: When command exceeds timeout (converted to exit code -254)
        InterruptGetOutput: Base class for output capture on interruptions
        KbdInterruptGetOutput: On KeyboardInterrupt (converted to exit code -252)
        StopOnInterrupt: When stop_on returns True (converted to exit code -251)
        ValueError: For invalid arguments (converted to exit code -250)
        
    Examples:
        Basic usage:
        >>> exit_code, output = command_runner('echo hello')
        >>> print(exit_code, output.strip())
        0 hello
        
        With timeout:
        >>> exit_code, output = command_runner('sleep 10', timeout=5)
        >>> print(exit_code)  # -254 for timeout
        -254
        
        Cross-platform ping:
        >>> import os
        >>> cmd = 'ping 127.0.0.1 -n 2' if os.name == 'nt' else ['ping', '-c', '2', '127.0.0.1']
        >>> exit_code, output = command_runner(cmd)
        
        File output:
        >>> exit_code, _ = command_runner('ls -la', stdout='/tmp/output.txt')
        
        Live output:
        >>> exit_code, output = command_runner('ping 127.0.0.1', live_output=True)
        
        Queue output (requires method="poller"):
        >>> import queue
        >>> q = queue.Queue()
        >>> exit_code, _ = command_runner('ping 127.0.0.1', stdout=q, method='poller')
        >>> # Read from queue in another thread
        
        Custom stop condition:
        >>> def should_stop():
        ...     return some_condition_check()
        >>> exit_code, output = command_runner('long_command', stop_on=should_stop)
    """

Threaded Command Execution

Asynchronous version of command_runner that returns a Future object, enabling non-blocking command execution and integration with concurrent programming patterns.

def command_runner_threaded(*args, **kwargs):
    """
    Threaded version of command_runner returning concurrent.Future result.
    
    Available only on Python 3.3+ due to concurrent.futures requirement.
    Accepts the same arguments as command_runner but executes in a background
    thread and returns immediately with a Future object.
    
    Args:
        *args: Same arguments as command_runner
        **kwargs: Same keyword arguments as command_runner.
                 Special keyword '__no_threads=True' forces synchronous execution.
    
    Returns:
        concurrent.futures.Future: Future object containing the result.
                                  Call .result() to get (exit_code, output) tuple
                                  or .exception() to get any raised exceptions.
    
    Examples:
        Basic threaded execution:
        >>> future = command_runner_threaded('ping 127.0.0.1')
        >>> # Do other work while command runs
        >>> exit_code, output = future.result()  # Blocks until complete
        
        With queue for live output:
        >>> import queue
        >>> output_queue = queue.Queue()
        >>> future = command_runner_threaded('ping 127.0.0.1', 
        ...                                  stdout=output_queue, method='poller')
        >>> # Read from queue while command runs
        >>> while not future.done():
        ...     try:
        ...         line = output_queue.get(timeout=0.1)
        ...         if line is None:
        ...             break
        ...         print(line, end='')
        ...     except queue.Empty:
        ...         pass
        >>> exit_code, output = future.result()
        
        Exception handling:
        >>> future = command_runner_threaded('invalid_command')
        >>> try:
        ...     result = future.result()
        ... except Exception as e:
        ...     print(f"Command failed: {e}")
    """

Deferred Command Execution

Launches commands detached from the parent process with a specified delay. Useful for self-updating applications or cleanup operations that need to run after the parent process exits.

def deferred_command(command, defer_time=300):
    """
    Launch a detached command after a specified delay.
    
    Creates an independent shell process that waits for the specified time
    then executes the command. The command runs completely detached from
    the parent process and will continue even if the parent exits.
    
    Args:
        command (str): Shell command to execute after delay
        defer_time (int): Delay in seconds before execution (default: 300)
    
    Returns:
        None
        
    Examples:
        Auto-cleanup after 5 minutes:
        >>> deferred_command('rm /tmp/tempfile.dat', defer_time=300)
        
        Self-update scenario:
        >>> deferred_command('cp /tmp/newversion.exe /app/myapp.exe', defer_time=10)
        
        Log rotation:
        >>> deferred_command('gzip /var/log/app.log', defer_time=3600)
    """

Capture Methods

Command Runner supports two different output capture methods with different performance and feature characteristics:

Monitor Method (Default)

  • Performance: Lower CPU usage, fewer threads
  • Features: Basic timeout and stop condition support
  • Limitations: Cannot use queues/callbacks, limited partial output on interrupts
  • Best for: Simple command execution with minimal resource usage

Poller Method

  • Performance: Slightly higher CPU usage, more threads
  • Features: Full queue/callback support, complete partial output capture, live output
  • Capabilities: Real-time output processing, interactive command support
  • Best for: Advanced output handling, live monitoring, queue-based processing

Output Redirection Options

Command Runner supports multiple output redirection patterns:

Standard Capture (Default)

  • stdout=None: Capture output in return value
  • Returns complete output as string/bytes

File Redirection

  • stdout="filename": Write output directly to file
  • stderr="filename": Write errors to separate file
  • Files are opened in binary mode and written live

Queue Redirection (Poller Method Only)

  • stdout=queue.Queue(): Send output lines to queue
  • Enables real-time processing in separate threads
  • Queue receives None sentinel when complete

Callback Redirection (Poller Method Only)

  • stdout=callback_function: Call function with each output line
  • Enables custom real-time processing logic
  • Function called with string argument for each line

Null Redirection

  • stdout=False: Discard output (redirect to /dev/null or NUL)
  • stderr=False: Discard error output
  • Improves performance when output not needed

Error Handling and Exit Codes

Command Runner provides comprehensive error handling with consistent exit codes:

Standard Exit Codes

  • 0: Success (or any code in valid_exit_codes list)
  • > 0: Command-specific error codes

Special Exit Codes

  • -250: Invalid arguments to command_runner
  • -251: Custom stop_on function returned True
  • -252: KeyboardInterrupt during execution
  • -253: File not found or OS-level errors
  • -254: Timeout expired
  • -255: Unexpected exceptions

Partial Output Recovery

Even when commands fail due to timeouts or interruptions, command_runner attempts to capture and return partial output, enabling debugging and recovery scenarios.

Install with Tessl CLI

npx tessl i tessl/pypi-command-runner

docs

core-execution.md

index.md

privilege-elevation.md

process-management.md

utilities.md

tile.json