Examples

This page provides practical examples of using supervisor-pydantic for common use cases.

Basic Examples

Running a Simple Worker Process

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)

config = SupervisorConvenienceConfiguration(
    port="*:9001",
    working_dir="/tmp/worker-supervisor",
    program={
        "worker": ProgramConfiguration(
            command="python my_worker.py",
            autostart=True,
            autorestart=True,
            startsecs=5,
        ),
    },
)

config.write()
config.start(daemon=True)

Multiple Programs

Run multiple programs under a single supervisor instance:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)

config = SupervisorConvenienceConfiguration(
    port="*:9001",
    working_dir="/tmp/multi-supervisor",
    program={
        "web": ProgramConfiguration(
            command="python -m http.server 8080",
            directory="/var/www",
        ),
        "worker": ProgramConfiguration(
            command="celery -A tasks worker",
            numprocs=4,  # Run 4 worker processes
        ),
        "scheduler": ProgramConfiguration(
            command="celery -A tasks beat",
        ),
    },
)

config.write()
config.start(daemon=True)

With Authentication

Secure your supervisor HTTP interface with username and password:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)

config = SupervisorConvenienceConfiguration(
    port="*:9001",
    username="admin",
    password="secure-password-123",
    working_dir="/tmp/secure-supervisor",
    program={
        "app": ProgramConfiguration(
            command="python app.py",
        ),
    },
)

config.write()
config.start(daemon=True)

Advanced Examples

Environment Variables

Pass environment variables to your programs:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)

config = SupervisorConvenienceConfiguration(
    port="*:9001",
    working_dir="/tmp/env-supervisor",
    program={
        "app": ProgramConfiguration(
            command="python app.py",
            environment={
                "DATABASE_URL": "postgresql://localhost/mydb",
                "REDIS_URL": "redis://localhost:6379",
                "LOG_LEVEL": "INFO",
            },
        ),
    },
)

Custom Exit Codes

Configure which exit codes are considered successful:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)

config = SupervisorConvenienceConfiguration(
    port="*:9001",
    working_dir="/tmp/exitcode-supervisor",
    exitcodes=[0, 1, 2],  # Consider 0, 1, and 2 as successful exits
    program={
        "batch-job": ProgramConfiguration(
            command="python batch_job.py",
            autorestart=False,  # Don't restart after completion
            exitcodes=[0, 1],  # Program-specific exit codes
        ),
    },
)

Using Groups

Organize programs into groups for batch control:

from supervisor_pydantic import (
    SupervisorConfiguration,
    SupervisordConfiguration,
    InetHttpServerConfiguration,
    ProgramConfiguration,
    GroupConfiguration,
)
from pathlib import Path

config = SupervisorConfiguration(
    supervisord=SupervisordConfiguration(
        logfile=Path("/var/log/supervisord.log"),
        pidfile=Path("/var/run/supervisord.pid"),
    ),
    inet_http_server=InetHttpServerConfiguration(port="*:9001"),
    program={
        "web-1": ProgramConfiguration(command="gunicorn app:app -b :8001"),
        "web-2": ProgramConfiguration(command="gunicorn app:app -b :8002"),
        "worker-1": ProgramConfiguration(command="celery -A tasks worker"),
        "worker-2": ProgramConfiguration(command="celery -A tasks worker"),
    },
    group={
        "web-servers": GroupConfiguration(programs=["web-1", "web-2"]),
        "workers": GroupConfiguration(programs=["worker-1", "worker-2"]),
    },
    config_path=Path("/etc/supervisor/supervisord.conf"),
    working_dir=Path("/var/supervisor"),
)

# Now you can control groups:
# supervisorctl start web-servers:*
# supervisorctl stop workers:*

Event Listeners

Configure event listeners for monitoring:

from supervisor_pydantic import (
    SupervisorConfiguration,
    SupervisordConfiguration,
    InetHttpServerConfiguration,
    ProgramConfiguration,
    EventListenerConfiguration,
)
from pathlib import Path

config = SupervisorConfiguration(
    supervisord=SupervisordConfiguration(
        logfile=Path("/var/log/supervisord.log"),
        pidfile=Path("/var/run/supervisord.pid"),
    ),
    inet_http_server=InetHttpServerConfiguration(port="*:9001"),
    program={
        "app": ProgramConfiguration(command="python app.py"),
    },
    eventlistener={
        "crashmail": EventListenerConfiguration(
            command="crashmail -a -m admin@example.com",
            events=["PROCESS_STATE_EXITED"],
        ),
    },
    config_path=Path("/etc/supervisor/supervisord.conf"),
    working_dir=Path("/var/supervisor"),
)

Remote Management with XML-RPC Client

Use the XML-RPC client to manage supervisor remotely:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    SupervisorRemoteXMLRPCClient,
    ProgramConfiguration,
)

# Create configuration
config = SupervisorConvenienceConfiguration(
    port="*:9001",
    host="remote-server.example.com",
    working_dir="/tmp/remote-supervisor",
    program={
        "app": ProgramConfiguration(command="python app.py"),
    },
)

# Connect to running supervisor
client = SupervisorRemoteXMLRPCClient(config)

# Get process info
info = client.getProcessInfo("app")
print(f"Process state: {info.state}")

# Start/stop processes
client.startProcess("app")
client.stopProcess("app")

# Get all process info
all_info = client.getAllProcessInfo()
for proc in all_info:
    print(f"{proc.name}: {proc.state}")

Using with Hydra

supervisor-pydantic integrates with Hydra for configuration management:

# config.yaml
supervisor:
  _target_: supervisor_pydantic.SupervisorConvenienceConfiguration
  port: "*:9001"
  working_dir: /tmp/hydra-supervisor
  program:
    worker:
      _target_: supervisor_pydantic.ProgramConfiguration
      command: python worker.py
import hydra
from omegaconf import DictConfig

@hydra.main(config_path=".", config_name="config")
def main(cfg: DictConfig):
    from hydra.utils import instantiate

    config = instantiate(cfg.supervisor)
    config.write()
    config.start(daemon=True)

if __name__ == "__main__":
    main()

Programmatic Lifecycle Management

Complete lifecycle management example:

from supervisor_pydantic import (
    SupervisorConvenienceConfiguration,
    ProgramConfiguration,
)
import time

def run_supervised_job():
    config = SupervisorConvenienceConfiguration(
        port="*:9001",
        working_dir="/tmp/lifecycle-supervisor",
        program={
            "job": ProgramConfiguration(
                command="python long_running_job.py",
                autorestart=False,
            ),
        },
    )

    try:
        # Write config and start
        config.write()
        config.start(daemon=True)

        # Wait for startup
        for _ in range(10):
            if config.running():
                break
            time.sleep(1)
        else:
            raise RuntimeError("Supervisor failed to start")

        print("Supervisor started successfully")

        # ... do work or monitor ...

    finally:
        # Clean shutdown
        if config.running():
            config.stop()

            # Wait for shutdown
            for _ in range(10):
                if not config.running():
                    break
                time.sleep(1)
            else:
                # Force kill if graceful shutdown fails
                config.kill()

        # Clean up files
        config.rmdir()
        print("Cleanup complete")

if __name__ == "__main__":
    run_supervised_job()