0
# Core Command Execution
1
2
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.
3
4
## Capabilities
5
6
### Main Command Execution
7
8
Executes shell commands with extensive configuration options for timeout handling, output capture, and cross-platform compatibility. Supports both synchronous and asynchronous execution patterns.
9
10
```python { .api }
11
def command_runner(
12
command, # Union[str, List[str]] - Command to execute
13
valid_exit_codes=False, # Union[List[int], bool] - Accepted exit codes
14
timeout=3600, # Optional[int] - Timeout in seconds (default: 3600)
15
shell=False, # bool - Use shell execution
16
encoding=None, # Optional[Union[str, bool]] - Output encoding
17
stdin=None, # Optional[Union[int, str, Callable, queue.Queue]]
18
stdout=None, # Optional[Union[int, str, Callable, queue.Queue]]
19
stderr=None, # Optional[Union[int, str, Callable, queue.Queue]]
20
no_close_queues=False, # Optional[bool] - Keep queues open after execution
21
windows_no_window=False, # bool - Hide console window on Windows
22
live_output=False, # bool - Display output during execution
23
method="monitor", # str - Capture method ("monitor" or "poller")
24
check_interval=0.05, # float - Polling interval in seconds
25
stop_on=None, # Callable - Custom stop condition function
26
on_exit=None, # Callable - Callback executed after completion
27
process_callback=None, # Callable - Callback with process information
28
split_streams=False, # bool - Return separate stdout/stderr tuples
29
silent=False, # bool - Suppress error logging
30
priority=None, # Union[int, str] - Process priority level
31
io_priority=None, # str - IO priority level
32
heartbeat=0, # int - Heartbeat logging interval (seconds)
33
**kwargs # Any - Additional subprocess.Popen arguments
34
):
35
"""
36
Execute shell commands with comprehensive error handling and output capture.
37
38
This function provides a robust interface for command execution that handles:
39
- Cross-platform command formatting and execution
40
- Reliable timeout enforcement with process tree termination
41
- Flexible output redirection (files, callbacks, queues, live display)
42
- Comprehensive encoding handling for different platforms
43
- Process priority and IO priority management
44
- Partial output capture on interruptions and timeouts
45
46
Args:
47
command: Command to execute. Can be string (shell=True) or list of strings.
48
String commands are automatically split on Unix when shell=False.
49
valid_exit_codes: Exit codes to treat as successful. False (default) means
50
only 0 is valid. True means any exit code is valid.
51
List means specific codes are valid.
52
timeout: Maximum execution time in seconds. None disables timeout.
53
shell: Whether to execute via shell. Required for complex shell commands.
54
encoding: Output encoding. None uses platform default (utf-8/cp437).
55
False returns raw bytes. String specifies custom encoding.
56
stdin: Input redirection. Can be subprocess constant, string filename,
57
callable function, or queue.Queue object.
58
stdout: Output redirection. None captures to return value, False discards,
59
string filename writes to file, callable sends to function,
60
queue.Queue sends to queue.
61
stderr: Error output redirection. Similar to stdout. None redirects to stdout.
62
no_close_queues: Keep queues open after execution (don't send None sentinel).
63
windows_no_window: Hide console window on Windows (Python 3.7+).
64
live_output: Show command output on screen during execution.
65
method: Capture method. "monitor" (default) uses lower CPU but limited features.
66
"poller" enables queues/callbacks and partial output on interrupts.
67
check_interval: Polling interval for timeout checks and output reading.
68
stop_on: Optional function returning bool to stop execution early.
69
on_exit: Optional callback function executed after command completion.
70
process_callback: Optional callback receiving subprocess.Popen object.
71
split_streams: Return (exit_code, stdout, stderr) instead of (exit_code, output).
72
silent: Suppress error logging (debug logs still shown).
73
priority: Process priority. String values: "verylow", "low", "normal", "high", "rt".
74
Unix also accepts int values -20 to 20.
75
io_priority: IO priority. String values: "low", "normal", "high".
76
heartbeat: Log heartbeat message every N seconds during execution.
77
**kwargs: Additional arguments passed to subprocess.Popen.
78
79
Returns:
80
Tuple[int, Optional[Union[bytes, str]]] - (exit_code, output) by default
81
Tuple[int, Optional[Union[bytes, str]], Optional[Union[bytes, str]]] -
82
(exit_code, stdout, stderr) when split_streams=True
83
84
Raises:
85
TimeoutExpired: When command exceeds timeout (converted to exit code -254)
86
InterruptGetOutput: Base class for output capture on interruptions
87
KbdInterruptGetOutput: On KeyboardInterrupt (converted to exit code -252)
88
StopOnInterrupt: When stop_on returns True (converted to exit code -251)
89
ValueError: For invalid arguments (converted to exit code -250)
90
91
Examples:
92
Basic usage:
93
>>> exit_code, output = command_runner('echo hello')
94
>>> print(exit_code, output.strip())
95
0 hello
96
97
With timeout:
98
>>> exit_code, output = command_runner('sleep 10', timeout=5)
99
>>> print(exit_code) # -254 for timeout
100
-254
101
102
Cross-platform ping:
103
>>> import os
104
>>> cmd = 'ping 127.0.0.1 -n 2' if os.name == 'nt' else ['ping', '-c', '2', '127.0.0.1']
105
>>> exit_code, output = command_runner(cmd)
106
107
File output:
108
>>> exit_code, _ = command_runner('ls -la', stdout='/tmp/output.txt')
109
110
Live output:
111
>>> exit_code, output = command_runner('ping 127.0.0.1', live_output=True)
112
113
Queue output (requires method="poller"):
114
>>> import queue
115
>>> q = queue.Queue()
116
>>> exit_code, _ = command_runner('ping 127.0.0.1', stdout=q, method='poller')
117
>>> # Read from queue in another thread
118
119
Custom stop condition:
120
>>> def should_stop():
121
... return some_condition_check()
122
>>> exit_code, output = command_runner('long_command', stop_on=should_stop)
123
"""
124
```
125
126
### Threaded Command Execution
127
128
Asynchronous version of command_runner that returns a Future object, enabling non-blocking command execution and integration with concurrent programming patterns.
129
130
```python { .api }
131
def command_runner_threaded(*args, **kwargs):
132
"""
133
Threaded version of command_runner returning concurrent.Future result.
134
135
Available only on Python 3.3+ due to concurrent.futures requirement.
136
Accepts the same arguments as command_runner but executes in a background
137
thread and returns immediately with a Future object.
138
139
Args:
140
*args: Same arguments as command_runner
141
**kwargs: Same keyword arguments as command_runner.
142
Special keyword '__no_threads=True' forces synchronous execution.
143
144
Returns:
145
concurrent.futures.Future: Future object containing the result.
146
Call .result() to get (exit_code, output) tuple
147
or .exception() to get any raised exceptions.
148
149
Examples:
150
Basic threaded execution:
151
>>> future = command_runner_threaded('ping 127.0.0.1')
152
>>> # Do other work while command runs
153
>>> exit_code, output = future.result() # Blocks until complete
154
155
With queue for live output:
156
>>> import queue
157
>>> output_queue = queue.Queue()
158
>>> future = command_runner_threaded('ping 127.0.0.1',
159
... stdout=output_queue, method='poller')
160
>>> # Read from queue while command runs
161
>>> while not future.done():
162
... try:
163
... line = output_queue.get(timeout=0.1)
164
... if line is None:
165
... break
166
... print(line, end='')
167
... except queue.Empty:
168
... pass
169
>>> exit_code, output = future.result()
170
171
Exception handling:
172
>>> future = command_runner_threaded('invalid_command')
173
>>> try:
174
... result = future.result()
175
... except Exception as e:
176
... print(f"Command failed: {e}")
177
"""
178
```
179
180
### Deferred Command Execution
181
182
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.
183
184
```python { .api }
185
def deferred_command(command, defer_time=300):
186
"""
187
Launch a detached command after a specified delay.
188
189
Creates an independent shell process that waits for the specified time
190
then executes the command. The command runs completely detached from
191
the parent process and will continue even if the parent exits.
192
193
Args:
194
command (str): Shell command to execute after delay
195
defer_time (int): Delay in seconds before execution (default: 300)
196
197
Returns:
198
None
199
200
Examples:
201
Auto-cleanup after 5 minutes:
202
>>> deferred_command('rm /tmp/tempfile.dat', defer_time=300)
203
204
Self-update scenario:
205
>>> deferred_command('cp /tmp/newversion.exe /app/myapp.exe', defer_time=10)
206
207
Log rotation:
208
>>> deferred_command('gzip /var/log/app.log', defer_time=3600)
209
"""
210
```
211
212
## Capture Methods
213
214
Command Runner supports two different output capture methods with different performance and feature characteristics:
215
216
### Monitor Method (Default)
217
218
- **Performance**: Lower CPU usage, fewer threads
219
- **Features**: Basic timeout and stop condition support
220
- **Limitations**: Cannot use queues/callbacks, limited partial output on interrupts
221
- **Best for**: Simple command execution with minimal resource usage
222
223
### Poller Method
224
225
- **Performance**: Slightly higher CPU usage, more threads
226
- **Features**: Full queue/callback support, complete partial output capture, live output
227
- **Capabilities**: Real-time output processing, interactive command support
228
- **Best for**: Advanced output handling, live monitoring, queue-based processing
229
230
## Output Redirection Options
231
232
Command Runner supports multiple output redirection patterns:
233
234
### Standard Capture (Default)
235
- `stdout=None`: Capture output in return value
236
- Returns complete output as string/bytes
237
238
### File Redirection
239
- `stdout="filename"`: Write output directly to file
240
- `stderr="filename"`: Write errors to separate file
241
- Files are opened in binary mode and written live
242
243
### Queue Redirection (Poller Method Only)
244
- `stdout=queue.Queue()`: Send output lines to queue
245
- Enables real-time processing in separate threads
246
- Queue receives None sentinel when complete
247
248
### Callback Redirection (Poller Method Only)
249
- `stdout=callback_function`: Call function with each output line
250
- Enables custom real-time processing logic
251
- Function called with string argument for each line
252
253
### Null Redirection
254
- `stdout=False`: Discard output (redirect to /dev/null or NUL)
255
- `stderr=False`: Discard error output
256
- Improves performance when output not needed
257
258
## Error Handling and Exit Codes
259
260
Command Runner provides comprehensive error handling with consistent exit codes:
261
262
### Standard Exit Codes
263
- `0`: Success (or any code in valid_exit_codes list)
264
- `> 0`: Command-specific error codes
265
266
### Special Exit Codes
267
- `-250`: Invalid arguments to command_runner
268
- `-251`: Custom stop_on function returned True
269
- `-252`: KeyboardInterrupt during execution
270
- `-253`: File not found or OS-level errors
271
- `-254`: Timeout expired
272
- `-255`: Unexpected exceptions
273
274
### Partial Output Recovery
275
Even when commands fail due to timeouts or interruptions, command_runner attempts to capture and return partial output, enabling debugging and recovery scenarios.