Platform agnostic command and shell execution tool with timeout handling, live output capture, and UAC/sudo privilege elevation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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)
"""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}")
"""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)
"""Command Runner supports two different output capture methods with different performance and feature characteristics:
Command Runner supports multiple output redirection patterns:
stdout=None: Capture output in return valuestdout="filename": Write output directly to filestderr="filename": Write errors to separate filestdout=queue.Queue(): Send output lines to queuestdout=callback_function: Call function with each output linestdout=False: Discard output (redirect to /dev/null or NUL)stderr=False: Discard error outputCommand Runner provides comprehensive error handling with consistent exit codes:
0: Success (or any code in valid_exit_codes list)> 0: Command-specific error 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 exceptionsEven 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