Subprocess Monitor
A robust Python service for managing, monitoring, and spawning subprocesses with advanced lifecycle management features. Provides both a Python API and CLI interface for subprocess operations using async/await patterns.
Table of Contents
- Why Subprocess Monitor?
- Key Features
- Installation
- Quick Start
- Use Cases vs Built-in subprocess
- API Reference
- CLI Usage
- Architecture
- Development
Why Subprocess Monitor?
While Python's built-in subprocess module is excellent for basic process management, subprocess-monitor provides enterprise-grade features for complex subprocess orchestration:
Problems with Built-in subprocess
- No centralized management: Each subprocess is isolated
- Manual lifecycle tracking: You must track PIDs and process states yourself
- No real-time monitoring: Limited ability to stream output in real-time
- Complex async patterns: Difficult to integrate with async/await workflows
- No process persistence: Processes die with parent termination
- Limited observability: No built-in logging or monitoring
Subprocess Monitor Solutions
- Centralized service: Single point of control for all subprocesses
- Automatic lifecycle management: Built-in process tracking and cleanup
- Real-time streaming: WebSocket-based output streaming
- Async-first design: Native async/await support throughout
- Process persistence: Processes can outlive parent applications
- Rich observability: Comprehensive logging and monitoring
- Cross-platform compatibility: Works on Windows, Linux, and macOS
- REST API: HTTP endpoints for remote process management
- Type safety: Full TypeScript-style typing with runtime validation
Key Features
🎯 Centralized Process Management
- Single service manages multiple subprocesses
- Automatic PID tracking and process ownership
- Built-in process cleanup and resource management
🔄 Real-time Monitoring
- WebSocket subscriptions for live output streaming
- Process state change notifications
- Resource usage monitoring
⚡ Async/Await Native
- Built on aiohttp for high-performance async operations
- Non-blocking subprocess operations
- Concurrent process management
🛡️ Enterprise Security
- Runtime type validation prevents injection attacks
- Secure port binding prevents race conditions
- Process isolation and sandboxing
🌐 Cross-Platform
- Windows, Linux, and macOS support
- Platform-specific optimizations
- Unified API across all platforms
🔌 Multiple Interfaces
- Python API for programmatic access
- CLI for terminal operations
- REST API for remote management
- WebSocket API for real-time updates
Installation
# Using pip
pip install subprocess-monitor
# Using uv (recommended)
uv add subprocess-monitor
# From source
git clone https://github.com/JulianKimmig/subprocess_monitor
cd subprocess-monitor
uv sync
Quick Start
Python API
import asyncio
from subprocess_monitor import SubprocessMonitor
async def main():
# For server-side usage
monitor = SubprocessMonitor(host="localhost", port=8080)
server_task = asyncio.create_task(monitor.run())
# For client-side usage (more common)
from subprocess_monitor.helper import send_spawn_request, get_status
# Start a subprocess via API
response = await send_spawn_request(
"python", ["-c", "print('Hello World')"],
env={"MY_VAR": "value"}, host="localhost", port=8080
)
pid = response["pid"]
print(f"Started process {pid}")
# Get status
processes = await get_status(host="localhost", port=8080)
print(f"Active processes: {processes}")
asyncio.run(main())
CLI Usage
# Start the monitor service
subprocess-monitor start --port 8080
# Spawn a process
subprocess-monitor spawn python -c "print('Hello World')"
# Check status
subprocess-monitor status
# Stop a process
subprocess-monitor stop <pid>
# Subscribe to process output
subprocess-monitor subscribe <pid>
Use Cases vs Built-in subprocess
When to Use Built-in subprocess
✅ Simple one-off commands
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
✅ Synchronous workflows
with subprocess.Popen(["python", "script.py"]) as proc:
proc.wait()
✅ Basic process communication
proc = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout, stderr = proc.communicate(input="print('hello')")
When to Use Subprocess Monitor
✅ Long-running process orchestration
# Built-in subprocess - complex manual management
processes = []
for i in range(10):
proc = subprocess.Popen(["worker.py", f"--id={i}"])
processes.append(proc)
# Subprocess Monitor - simple centralized management
monitor = SubprocessMonitor()
for i in range(10):
await monitor.start_subprocess({"cmd": "python", "args": ["worker.py", f"--id={i}"]})
✅ Real-time output monitoring
# Built-in subprocess - polling required
proc = subprocess.Popen(["tail", "-f", "log.txt"], stdout=subprocess.PIPE)
for line in iter(proc.stdout.readline, b''):
print(line.decode())
# Subprocess Monitor - WebSocket streaming
async with websockets.connect(f"ws://localhost:8080/subscribe?pid={pid}") as ws:
async for message in ws:
data = json.loads(message)
print(f"{data['stream']}: {data['data']}")
✅ Async/await workflows
# Built-in subprocess - complex async integration
async def run_process():
proc = await asyncio.create_subprocess_exec("python", "script.py")
await proc.wait()
# Subprocess Monitor - native async support
async def run_process():
pid = await monitor.start_subprocess({"cmd": "python", "args": ["script.py"]})
# Process runs in background - monitor via subscribe_output endpoint
✅ Process lifecycle management
# Built-in subprocess - manual cleanup
import atexit
import signal
processes = []
def cleanup():
for proc in processes:
proc.terminate()
proc.wait()
atexit.register(cleanup)
signal.signal(signal.SIGTERM, lambda s, f: cleanup())
# Subprocess Monitor - automatic cleanup
monitor = SubprocessMonitor() # Handles all cleanup automatically
✅ Remote process management
# Built-in subprocess - local only
subprocess.run(["python", "script.py"])
# Subprocess Monitor - remote management via REST API
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.post("http://remote:8080/spawn", json={
"cmd": "python", "args": ["script.py"]
}) as resp:
result = await resp.json()
print(f"Started remote process {result['pid']}")
✅ Multi-service coordination
# Start multiple interdependent services
services = [
{"cmd": "redis-server", "args": []},
{"cmd": "python", "args": ["api_server.py"]},
{"cmd": "python", "args": ["worker.py"]},
{"cmd": "nginx", "args": ["-c", "nginx.conf"]}
]
monitor = SubprocessMonitor()
pids = []
for service in services:
pid = await monitor.start_subprocess(service)
pids.append(pid)
await asyncio.sleep(1) # Staggered startup
# Monitor all services via WebSocket (client-side implementation needed)
# WebSocket endpoint: ws://localhost:8080/subscribe?pid={pid}
API Reference
SubprocessMonitor Class
Constructor
SubprocessMonitor(
host: str = "localhost",
port: Optional[int] = None,
check_interval: float = 2.0,
logger: Optional[logging.Logger] = None
)
Methods
async start_subprocess(request: SpawnProcessRequest) -> int
- Spawns a new subprocess
- Returns the process PID
- Automatically tracks process lifecycle
async stop_subprocess(pid: int) -> bool
- Terminates a subprocess by PID
- Returns True if successful
- Handles cleanup automatically
Note: The SubprocessMonitor class provides internal process management. For client access, use the helper functions or REST API endpoints:
- GET /: Returns list of active process PIDs
- POST /spawn: Spawn a new subprocess
- POST /stop: Stop a subprocess by PID
- WebSocket /subscribe?pid={pid}: Subscribe to real-time process output
Helper Functions
async send_spawn_request(cmd: str, args: List[str], env: Dict[str, str] = None, host: str = None, port: int = None) -> SpawnRequestResponse
async send_stop_request(pid: int, host: str = None, port: int = None) -> StopRequestResponse
async get_status(host: str = None, port: int = None) -> List[int]
CLI Usage
Starting the Service
# Start with default settings
subprocess-monitor start
# Start with custom port
subprocess-monitor start --port 8080
# Start with custom host and port
subprocess-monitor start --host 0.0.0.0 --port 8080
Process Management
# Spawn a simple command
subprocess-monitor spawn echo "Hello World"
# Spawn with arguments
subprocess-monitor spawn python script.py --arg1 value1
# Spawn with environment variables
subprocess-monitor spawn --env VAR1=value1 --env VAR2=value2 python script.py
# Check active processes
subprocess-monitor status
# Stop a process
subprocess-monitor stop 12345
# Subscribe to process output
subprocess-monitor subscribe 12345
Environment Variables
SUBPROCESS_MONITOR_HOST: Default host (default: localhost)SUBPROCESS_MONITOR_PORT: Default port (default: 5000)SUBPROCESS_MONITOR_PID: Monitor service PID (auto-set)
Architecture
Core Components
subprocess_monitor/
├── __init__.py # Package exports
├── __main__.py # CLI entry point
├── subprocess_monitor.py # Core service logic
├── helper.py # Client helper functions
├── types.py # TypedDict definitions
├── defaults.py # Configuration constants
└── validation.py # Runtime type validation
Key Design Patterns
Async/Await First: Built on asyncio and aiohttp for high-performance async operations
Type Safety: Full typing with runtime validation using TypedDict and custom validators
Resource Management: Automatic cleanup of processes, sockets, and WebSocket connections
Cross-Platform: Unified API with platform-specific optimizations
Security: Input validation, secure port binding, and process isolation
Service Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ CLI Client │ │ Python API │ │ HTTP Client │
└─────────┬───────┘ └─────────┬────────┘ └─────────┬───────┘
│ │ │
└──────────────────────┼───────────────────────┘
│
┌────────────▼────────────┐
│ Subprocess Monitor │
│ (aiohttp server) │
└────────────┬────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌───────▼────────┐ ┌─────────▼────────┐ ┌─────────▼────────┐
│ REST Endpoints │ │ WebSocket Stream │ │ Process Manager │
│ /spawn │ │ /subscribe │ │ - Lifecycle │
│ /stop │ │ Real-time I/O │ │ - Monitoring │
│ /status │ │ JSON messages │ │ - Cleanup │
└────────────────┘ └──────────────────┘ └──────────────────┘
Development
Setup
# Clone and setup
git clone https://github.com/JulianKimmig/subprocess_monitor
cd subprocess-monitor
uv sync
# Install pre-commit hooks
uv run pre-commit install
Testing
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=subprocess_monitor
# Run specific test
uv run pytest tests/test_sm.py::TestHelperFunctions::test_send_spawn_request
Code Quality
# Format code
uv run ruff format .
# Lint code
uv run ruff check . --fix
# Type checking
uv run flake8
# Security scanning
uv run vulture subprocess_monitor
# Run all checks
uv run pre-commit run --all-files
Project Structure
- Type Safety: Uses TypedDict for API contracts with runtime validation
- Async Patterns: Built on asyncio for non-blocking operations
- Testing: Comprehensive test suite with IsolatedAsyncioTestCase
- Security: Input validation and secure resource management
- Cross-Platform: Works on Windows, Linux, and macOS
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run the full test suite
- Submit a pull request
Support
- 🐛 Bug Reports: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📚 Documentation: Wiki
Metadata
Release files for subprocess-monitor 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| subprocess_monitor-0.4.0.tar.gz | 122.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| subprocess_monitor-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 141.8 kB
Release files / subprocess_monitor-0.4.0.tar.gz
| Download URL | subprocess_monitor-0.4.0.tar.gz |
|---|---|
| Size | 122.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bcb40d351d8ee501e7291064cc29e5459e406b51bc7739803b1a1b430bb20aeb
|
|
BLAKE2b-256 checksum How to use checksums |
945a8b70bd3d6340c8c39e05bc68b28c22beae2aa1b908a16001cd71873f3c0b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 23, 2025.
Transparency logRelease files / subprocess_monitor-0.4.0-py3-none-any.whl
| Download URL | subprocess_monitor-0.4.0-py3-none-any.whl |
|---|---|
| Size | 19.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d931b0ecb5bd7ba33be62e9cd24c785a3b6ffe332e0addfe3b5acd689a6847b3
|
|
BLAKE2b-256 checksum How to use checksums |
caf0e78794052f46181bd881455e25e726dcb3494502dc1b15d9883764c02b41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 23, 2025.
Transparency log