0
# Process Management
1
2
Core functionality for managing and orchestrating multiple concurrent processes with lifecycle management, output handling, and signal forwarding. The process management system enables running multiple services simultaneously with proper coordination and cleanup.
3
4
## Capabilities
5
6
### Manager Class
7
8
The Manager class orchestrates multiple processes, handles signals, and manages process lifecycle events. It provides a high-level interface for running multiple processes concurrently with proper event handling and cleanup.
9
10
```python { .api }
11
class Manager:
12
"""
13
Manager is responsible for running multiple external processes in parallel
14
managing the events that result (starting, stopping, printing).
15
"""
16
17
def __init__(self, printer=None):
18
"""
19
Initialize manager with optional printer.
20
21
Parameters:
22
- printer: Printer instance for output formatting (defaults to stdout printer)
23
"""
24
25
def add_process(self, name, cmd, quiet=False, env=None, cwd=None):
26
"""
27
Add a process to this manager instance. The process will not be started
28
until loop() is called.
29
30
Parameters:
31
- name: str, unique process name
32
- cmd: str, command to execute
33
- quiet: bool, whether to suppress process output
34
- env: dict, environment variables for the process
35
- cwd: str, working directory for the process
36
37
Returns:
38
Process: The created process object
39
"""
40
41
def loop(self):
42
"""
43
Start all the added processes and multiplex their output onto the bound
44
printer. If one process terminates, all others will be terminated.
45
This method blocks until all processes have terminated.
46
"""
47
48
def terminate(self):
49
"""
50
Terminate all processes managed by this ProcessManager.
51
"""
52
53
def kill(self):
54
"""
55
Kill all processes managed by this ProcessManager forcefully.
56
"""
57
58
# Properties
59
returncode: Optional[int] # Return code after loop() finishes
60
```
61
62
### Process Class
63
64
Wrapper around subprocess with event forwarding and output handling. Provides a clean interface for individual process management with lifecycle events.
65
66
```python { .api }
67
class Process:
68
"""
69
A utility wrapper around subprocess.Popen that stores attributes needed
70
by Honcho and supports forwarding process lifecycle events and output to a queue.
71
"""
72
73
def __init__(self, cmd, name=None, colour=None, quiet=False, env=None, cwd=None):
74
"""
75
Initialize a process wrapper.
76
77
Parameters:
78
- cmd: str, command to execute
79
- name: str, process name for identification
80
- colour: str, ANSI color code for output
81
- quiet: bool, whether to suppress output
82
- env: dict, environment variables
83
- cwd: str, working directory
84
"""
85
86
def run(self, events=None, ignore_signals=False):
87
"""
88
Run the process and forward events to the queue.
89
90
Parameters:
91
- events: multiprocessing.Queue, event queue for forwarding messages
92
- ignore_signals: bool, whether to ignore SIGINT/SIGTERM
93
"""
94
95
# Properties
96
cmd: str
97
colour: Optional[str]
98
quiet: bool
99
name: Optional[str]
100
env: Dict[str, str]
101
cwd: Optional[str]
102
```
103
104
### Enhanced Subprocess
105
106
Custom Popen subclass with platform-specific optimizations and proper signal handling for process groups.
107
108
```python { .api }
109
class Popen(subprocess.Popen):
110
"""
111
Enhanced subprocess.Popen with platform-specific optimizations.
112
"""
113
114
def __init__(self, cmd, **kwargs):
115
"""
116
Initialize enhanced Popen with default options for Honcho.
117
118
Parameters:
119
- cmd: str, command to execute
120
- **kwargs: additional subprocess options
121
"""
122
```
123
124
### Process Manager Utilities
125
126
Cross-platform process management utilities for terminating and killing process groups.
127
128
```python { .api }
129
class ProcessManager:
130
"""
131
Cross-platform process management utilities.
132
"""
133
134
def terminate(self, pid):
135
"""
136
Terminate process group by PID.
137
138
Parameters:
139
- pid: int, process ID to terminate
140
"""
141
142
def kill(self, pid):
143
"""
144
Kill process group by PID forcefully.
145
146
Parameters:
147
- pid: int, process ID to kill
148
"""
149
```
150
151
## Usage Examples
152
153
### Basic Process Management
154
155
```python
156
import sys
157
from honcho.manager import Manager
158
from honcho.printer import Printer
159
160
# Create manager with custom printer settings
161
manager = Manager(Printer(sys.stdout, colour=True, prefix=True))
162
163
# Add multiple processes
164
manager.add_process('web', 'python app.py', env={'PORT': '5000'})
165
manager.add_process('worker', 'python worker.py', quiet=True)
166
manager.add_process('scheduler', 'python scheduler.py', cwd='/app/scheduler')
167
168
# Start all processes and wait for completion
169
manager.loop()
170
171
# Exit with appropriate return code
172
sys.exit(manager.returncode)
173
```
174
175
### Custom Process Wrapper
176
177
```python
178
from honcho.process import Process
179
import multiprocessing
180
181
# Create event queue for process communication
182
events = multiprocessing.Queue()
183
184
# Create and run individual process
185
process = Process(
186
cmd='python long_running_task.py',
187
name='task',
188
colour='32', # Green
189
env={'DEBUG': '1'}
190
)
191
192
# Run process in background thread or multiprocessing
193
import multiprocessing
194
p = multiprocessing.Process(target=process.run, args=(events, False))
195
p.start()
196
197
# Handle events from queue
198
while True:
199
try:
200
msg = events.get(timeout=1.0)
201
print(f"Event: {msg.type}, Data: {msg.data}")
202
if msg.type == 'stop':
203
break
204
except queue.Empty:
205
continue
206
207
p.join()
208
```
209
210
### Signal Handling
211
212
```python
213
import signal
214
from honcho.manager import Manager
215
216
manager = Manager()
217
manager.add_process('app', 'python app.py')
218
219
# The manager automatically handles SIGINT and SIGTERM
220
# to gracefully terminate all child processes
221
222
try:
223
manager.loop()
224
except KeyboardInterrupt:
225
print("Received interrupt, shutting down...")
226
finally:
227
# Manager automatically cleans up processes
228
sys.exit(manager.returncode or 0)
229
```
230
231
## Constants
232
233
```python { .api }
234
KILL_WAIT = 5 # Seconds to wait before forceful kill
235
SIGNALS = {
236
signal.SIGINT: {'name': 'SIGINT', 'rc': 130},
237
signal.SIGTERM: {'name': 'SIGTERM', 'rc': 143},
238
}
239
SYSTEM_PRINTER_NAME = 'system' # Name for system messages
240
ON_WINDOWS = 'win32' in str(sys.platform).lower() # Platform detection
241
```