Executor: Programmer friendly subprocess wrapper
The `executor` package is a simple wrapper for Python's `subprocess` module, designed to simplify handling external commands on UNIX systems. It provides an object-oriented interface with proper argument escaping and error checking. Features include support for local commands, remote commands over SSH, execution within chroots, and concurrent command execution through command pools. The library's latest version is 23.2, released in November 2020, with an irregular release cadence.
Warnings
- breaking The `executor` library underwent a significant interface change from version 1.x to 2.x. In 1.x, `execute()` was the sole interface. In 2.x+, the `ExternalCommand` class was introduced for more flexible and asynchronous operations, with `execute()` becoming a wrapper around it. Code written for 1.x using only `execute()` for complex scenarios might need refactoring to leverage `ExternalCommand` in 2.x+.
- gotcha The `executor` package is explicitly designed for and tested on "UNIX systems." While it might function on other platforms to some extent, full compatibility and all features (like chroot or schroot integration) are not guaranteed on non-UNIX environments (e.g., Windows).
- gotcha By default, the `execute()` function raises an `ExternalCommandFailed` exception if the external command exits with a non-zero status code. This is a robust error-checking mechanism but can be unexpected if you intend to handle non-zero exit codes as part of normal program flow.
- gotcha The primary `execute()` function is synchronous and will block the Python interpreter until the external command completes. For long-running commands or to achieve non-blocking execution, you must use the `ExternalCommand` class directly, which provides `start()` for asynchronous initiation and `wait()` to block only when results are needed.
- breaking In `executor` version 14.0, the behavior of the `command` property changed. It became valid to set `input` and `shell` options without explicitly providing a `command` argument (which was previously mandatory). Additionally, the `command` property became mutable, allowing it to be changed using normal attribute assignment or reset with `del`.
Install
-
pip install executor
Imports
- execute
import executor; executor.execute()
from executor import execute
- ExternalCommand
from executor import ExternalCommand
- foreach
from executor.ssh.client import foreach
Quickstart
from executor import execute
import os
# Run a simple command and check its success
print(f"'true' command success: {execute('true')}")
print(f"'false' command success (without check): {execute('false', check=False)}")
# Provide input to a command and capture its output
output = execute('tr a-z A-Z', input='Hello Python Executor\n', capture=True)
print(f"Transformed output: {output.strip()}")
# Example of running a command that fails, demonstrating default error handling
try:
execute('non_existent_command')
except Exception as e:
print(f"Caught expected error for non-existent command: {e}")