process-pilot


Nameprocess-pilot JSON
Version 0.3.5 PyPI version JSON
download
home_pagehttps://github.com/engineerjames/process-pilot
SummaryProcess manager for Python
upload_time2025-01-13 16:33:04
maintainerNone
docs_urlNone
authorJames Armes
requires_python<3.13,>=3.10
licenseNone
keywords process management monitoring
VCS
bugtrack_url
requirements sphinx sphinx-rtd-theme pylint pytest ruff mypy autopep8 types-pyyaml pytest-mock types-psutil pydantic pyyaml psutil myst-parser sphinxcontrib-mermaid
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Process Pilot

Process Pilot is a Python-based tool for managing and monitoring processes defined in a manifest file. It supports JSON and YAML formats for defining processes and their configurations.

## Features

- Load process configurations from JSON or YAML files.
- Manage process lifecycles with customizable hooks.
- Monitor process resource usage.
- Define shutdown strategies for processes.
- Define ready strategies to determine when launched processes are deemed to be "running".

## Installation

To install the dependencies, use [Poetry](https://python-poetry.org/):

```sh
poetry install
```

## Usage

You can use the `ProcessPilot` class directly in your Python code to manage processes defined in a YAML or JSON file.

### Process Control

You can restart specific processes by name:

```python
from process_pilot.pilot import ProcessPilot
from process_pilot.process import ProcessManifest
from pathlib import Path

# Load manifest and start ProcessPilot
manifest = ProcessManifest.from_json(Path("manifest.json"))
pilot = ProcessPilot(manifest)
pilot.start()

# Later, restart specific processes
try:
    pilot.restart_processes(["api_server", "worker"])
except ValueError as e:
    print(f"Error restarting processes: {e}")
```

### Example Usage

#### Using a JSON Manifest

```python
from pathlib import Path
from process_pilot.process import ProcessPilot, ProcessManifest

# Load the process manifest from a JSON file
manifest_path = Path("path/to/your/manifest.json")
manifest = ProcessManifest.from_json(manifest_path)

# Create a ProcessPilot instance with the loaded manifest
pilot = ProcessPilot(manifest)

# Start managing the processes
pilot.start()
```

#### Using a YAML Manifest

```python
from pathlib import Path
from process_pilot.process import ProcessPilot, ProcessManifest

# Load the process manifest from a YAML file
manifest_path = Path("path/to/your/manifest.yaml")
manifest = ProcessManifest.from_yaml(manifest_path)

# Create a ProcessPilot instance with the loaded manifest
pilot = ProcessPilot(manifest)

# Start managing the processes
pilot.start()
```

## Configuration

### Process Manifest

The process manifest defines the processes to be managed. It can be written in JSON or YAML format.

#### Parameters

- `name`: The name of the process. This should be unique within the manifest.
- `path`: The path to the executable or script to be run.
- `args`: A list of arguments to be passed to the process.
- `timeout`: The maximum time (in seconds) to wait for the process to stop.
- `shutdown_strategy`: The strategy to use when shutting down the process. Possible values are:
  - `do_not_restart`: Do not restart the process after it stops.
  - `restart`: Restart the process after it stops. This is the default.
  - `shutdown_everything`: Stop all processes when this process stops.
- `ready_strategy`: The strategy to use to determine when the process is ready. Possible values are:
  - `tcp`: The process is ready when it starts listening on a specified TCP port.
  - `pipe`: The process is ready when it writes a specific signal to a named pipe.
  - `file`: The process is ready when a specific file is created.
- `ready_timeout_sec`: The maximum time (in seconds) to wait for the process to be ready.
- `ready_params`: Additional parameters for the ready strategy. These vary based on the strategy:
  - For `tcp`, specify the `port` to check.
  - For `pipe`, specify the `path` to the named pipe.
  - For `file`, specify the `path` to the file.
- `dependencies`: A list of other process names that must be started before this process can be started.
- `env`: A dictionary of environment variables to set for the process.

The following is an example of a JSON manifest:

```json
{
  "processes": [
    {
      "name": "example",
      "path": "sleep",
      "args": ["5"],
      "timeout": 3,
      "shutdown_strategy": "do_not_restart",
      "ready_strategy": "tcp",
      "ready_timeout_sec": 10.0,
      "ready_params": {
        "port": 8080
      },
      "dependencies": ["another_process"],
      "env": {
        "ENV_VAR": "value"
      }
    }
  ]
}
```

The following is an example of a YAML manifest:

```yaml
processes:
    - name: example
        path: sleep
        args: ["5"]
        timeout: 1.0
        shutdown_strategy: do_not_restart
        ready_strategy: tcp
        ready_timeout_sec: 10.0
        ready_params:
            port: 8080
        dependencies:
            - another_process
        env:
            ENV_VAR: value
```

## Plugin System

Process Pilot supports a plugin system that allows users to extend its functionality with custom lifecycle hooks, ready strategies, and process statistics handlers. Each created plugin can provide all three of the aforementioned groups, or just one. Each registration function links a name to a function or set of functions. The name provided must match what is in the manifest.

### Plugin Registration Scoping

Plugins in Process Pilot have one distinct registration scope:

1. **Process-Specific Hooks**: Process lifecycle hooks, ready strategies, and process statistic handlers are only registered for processes that explicitly request them in their manifest configuration.

### Creating a Plugin

To create a plugin, define a class that inherits from `Plugin` and implement the required methods:

```python
# import statements...

class FileReadyPlugin(Plugin):
    def get_ready_strategies(self) -> dict[str, ReadyStrategyType]:
        return {
            "file": self._wait_file_ready,
        }

    def _wait_file_ready(self, process: "Process", ready_check_interval_secs: float) -> bool:
        file_path = process.ready_params.get("path")

        if not file_path:
            msg = "Path not specified for file ready strategy"
            raise RuntimeError(msg)

        file_path = Path(file_path)

        start_time = time.time()
        while (time.time() - start_time) < process.ready_timeout_sec:
            if file_path.exists():
                return True
            time.sleep(ready_check_interval_secs)
        return False
```

When creating plugins that implement a ready strategy it is important to keep in mind that you should always be checking readiness relative to
the start time--and always comparing the difference to the timeout value that is specified in the manifest. The
simplest example of this can be seen in the `FileReadyPlugin` shown above:

```python
start_time = time.time()
while (time.time() - start_time) < process.ready_timeout_sec:
    if file_path.exists():
        return True
    time.sleep(ready_check_interval_secs)

# Timeout
return False
```

Be careful not to use readiness checks that block the threads ability to check for a timeout condition.

### Plugin Registration

One way to use plugins with specific processes is to specify them in the manifest as shown below:

```json
{
  "processes": [
    {
      "name": "example_process",
      "path": "myapp",
      "lifecycle_hooks": ["custom_lifecycle_hooks"],
      "ready_strategy": "custom_strategy",
      "ready_timeout_sec": 10.0
    },
    {
      "name": "another_process",
      "path": "otherapp"
    }
  ]
}
```

In this example, you must have loaded a plugin that provides a lifecycle hook with the name "custom_lifecycle_hooks" and a plugin that implements a ready strategy named "custom_strategy"--they do not have to be in the same plugin, but they could be.

Plugins themselves are registered with process-pilot either in code directly:

```python
from pathlib import Path
from process_pilot.process import ProcessPilot, ProcessManifest
from custom_plugin import CustomPlugin

# Load manifest
manifest = ProcessManifest.from_json(Path("manifest.json"))

# Create pilot and register plugins
pilot = ProcessPilot(manifest)
pilot.register_plugins([CustomPlugin()])

# Start processes
pilot.start()
```

Or, by providing a plugin directory when the ProcessPilot object is constructed. See the API documentation for the ProcessPilot class, and the Plugin class for more details.

## Control Server Plugins

Control server plugins provide a way to add remote control capabilities to Process Pilot. A control server can be used to monitor and control processes at runtime through various interfaces (HTTP, TCP, Unix socket, etc.).

### Creating a Control Server Plugin

To create a control server plugin:

1. Create a class that implements your control server with at least these methods:

   - `__init__(self, pilot: ProcessPilot)` - Constructor that receives the pilot instance
   - `start(self)` - Called when Process Pilot starts
   - `stop(self)` - Called when Process Pilot shuts down

2. Create a plugin class that inherits from `Plugin` and registers your control server:

```python
from process_pilot.plugin import Plugin, ControlServerType
from process_pilot.pilot import ProcessPilot

class MyControlServer:
    def __init__(self, pilot: ProcessPilot):
        self.pilot = pilot

    def start(self):
        # Start your control server
        pass

    def stop(self):
        # Clean up resources
        pass

class MyControlServerPlugin(Plugin):
    def get_control_servers(self) -> dict[str, ControlServerType]:
        return {"my_server": lambda pilot: MyControlServer(pilot)}
```

3. Register your plugin and enable it in the manifest:

```python
pilot = ProcessPilot(manifest)
pilot.register_plugins([MyControlServerPlugin()])
```

```yaml
manifest:
  control_server: my_server # Enable your control server
  processes:
    # ... process definitions ...
```

### Control Server Lifecycle

The following diagram shows when the control server is started and stopped:

```mermaid
sequenceDiagram
    participant User
    participant ProcessPilot
    participant ControlServer
    participant Processes

    User->>ProcessPilot: Create(manifest)
    ProcessPilot->>ProcessPilot: Load plugins
    ProcessPilot->>ControlServer: Create

    User->>ProcessPilot: start()
    ProcessPilot->>ControlServer: start()
    ProcessPilot->>Processes: Start processes

    Note over ControlServer,Processes: Control server running...

    User->>ProcessPilot: shutdown()
    ProcessPilot->>Processes: Stop processes
    ProcessPilot->>ControlServer: stop()
    ProcessPilot->>User: Return
```

### Built-in Control Server Features

The ProcessPilot instance passed to the control server provides methods to:

- Start/stop/restart processes
- Get process statistics
- Access the process manifest
- Query process status

## Process Lifecycle Hooks

The following diagram illustrates the process lifecycle and when various hook functions are called:

```mermaid
graph TD
        A[Start Process Pilot] --> B[Initialize Processes]
        B --> C[Execute PRE_START Hooks]
        C --> D[Start Process]
        D --> E[Execute POST_START Hooks]
        E --> F[Monitor Process]
        F -->|Process Running| F
        F -->|Process Exits| G[Execute ON_SHUTDOWN Hooks]
        G --> H{Shutdown Strategy}
        H -->|restart| I[Restart Process]
        I --> J[Execute ON_RESTART Hooks]
        J --> F
        H -->|do_not_restart| K[Stop Monitoring]
        H -->|shutdown_everything| L[Stop All Processes]
```

Ready strategies tie in to this via:

```mermaid
graph TD
A[Start Process] --> B[Check Ready Strategy]
B -->|Strategy 1| C[Execute Strategy 1]
B -->|Strategy 2| D[Execute Strategy 2]
B -->|Strategy 3| E[Execute Strategy 3]
C --> F[Process Ready]
D --> F[Process Ready]
E --> F[Process Ready]
```

Statistic handlers tie into this via:

```mermaid
graph TD
        A[Monitor Process] --> B[Collect Statistics]
        B --> C[Execute Statistic Handlers]
        C --> D[Update Statistics]
        D --> A
```

## Ready Strategies

Process Pilot supports three different strategies to determine if a process is ready, but more strategies can be provided via plugins:

1. TCP Port Listening
2. Named Pipe Signal
3. File Presence

Each ready strategy is only relevant for determining when dependent processes should be started. That is, if a given process has no dependencies, then specifying a ready strategy isn't currently meaningful. The following diagrams illustrate how each strategy works:

### TCP Ready Strategy

```mermaid
sequenceDiagram
    participant PP as Process Pilot
    participant P as Process
    participant TCP as TCP Port
    PP->>P: Start Process
    activate P
    P->>TCP: Begin Listening
    loop Until Ready or Timeout
        PP->>TCP: Attempt Connection
        alt Port is Listening
            TCP-->>PP: Connection Success
            PP->>PP: Process Ready
        else Port not ready
            TCP-->>PP: Connection Failed
            Note over PP: Wait 0.1s
        end
    end
    deactivate P
```

### Named Pipe Ready Strategy

```mermaid
sequenceDiagram
    participant PP as Process Pilot
    participant P as Process
    participant Pipe as Named Pipe
    PP->>Pipe: Create Pipe
    PP->>P: Start Process
    activate P
    loop Until Ready or Timeout
        PP->>Pipe: Read Pipe
        alt Contains "ready"
            Pipe-->>PP: "ready"
            PP->>PP: Process Ready
        else Not Ready
            Pipe-->>PP: No Data/Error
            Note over PP: Wait 0.1s
        end
    end
    deactivate P
```

### File Ready Strategy

```mermaid
sequenceDiagram
    participant PP as Process Pilot
    participant P as Process
    participant FS as File System
    PP->>P: Start Process
    activate P
    loop Until Ready or Timeout
        PP->>FS: Check File
        alt File Exists
            FS-->>PP: File Found
            PP->>PP: Process Ready
        else No File
            FS-->>PP: No File
            Note over PP: Wait 0.1s
        end
    end
    deactivate P
```

Each strategy can be configured in the manifest:

```yaml
processes:
  - name: example
    path: myapp
    ready_strategy: tcp # or "pipe" or "file"
    ready_timeout_sec: 10.0
    ready_params:
      port: 8080 # for TCP
      path: "/tmp/ready.txt" # for File
```

## Dependency Graph Visualization

Process Pilot includes a tool to visualize process dependencies using Graphviz. This helps you understand and validate the relationships between your processes.

### Prerequisites

The graph visualization requires Graphviz to be installed on your system:

```sh
# Ubuntu/Debian
apt-get install graphviz

# macOS
brew install graphviz

# Windows
choco install graphviz
```

### Generating Dependency Graphs

You can generate a dependency graph from your process manifest using the `graph.py` module:

```sh
process-graph manifest.json --format png --output-dir ./graphs
```

### Command Line Options

- manifest_path: Path to your JSON or YAML manifest file (required)
- --format: Output format (png, svg, or pdf) - defaults to png
- --output-dir: Directory to save the generated graph
- --detailed: Include detailed process information in tooltips

### Graph Features

- Process nodes with their names
- Directed edges showing dependencies
- Color-coding for ready strategies:
  - Light blue: TCP ready strategy
  - Light green: File ready strategy
  - Light yellow: Pipe ready strategy
- Detailed tooltips (when using --detailed) showing:
  - Process path
  - Ready strategy
  - Timeout values

> NOTE: Detailed output is only available when the output is SVG

### Example

Given this manifest:

```json
{
  "processes": [
    {
      "name": "database",
      "path": "postgresql",
      "ready_strategy": "tcp",
      "ready_params": {
        "port": 5432
      }
    },
    {
      "name": "api",
      "path": "api_server",
      "dependencies": ["database"],
      "ready_strategy": "file",
      "ready_params": {
        "path": "/tmp/api_ready"
      }
    },
    {
      "name": "worker",
      "path": "worker_service",
      "dependencies": ["api", "database"]
    }
  ]
}
```

You could generate the graph via:

```sh
process-graph manifest.json --format svg
```

This will create a graph that will show:

- `database` node (light blue) with no dependencies
- `api` node (light green) depending on `database`
- `worker` node (white) depending

## Development

### Running Tests

To run the tests, use:

```sh
poetry run pytest
```

### Build the documentation

To build the documentation, run the following from the top level of the repository:

```sh
poetry run sphinx-build -b html docs docs/_build/html
```

### Linting and Formatting

To lint and format the code, use:

```sh
poetry run ruff check .
poetry run autopep8 --in-place --recursive .
```

## License

This project is licensed under the MIT License. See the [LICENSE](../LICENSE) file for details.

## Contributing

Contributions are welcome! Please open an issue or submit a pull request.

## Contact

For any inquiries, please contact James Armes at jamesleearmes@gmail.com.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/engineerjames/process-pilot",
    "name": "process-pilot",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<3.13,>=3.10",
    "maintainer_email": null,
    "keywords": "process, management, monitoring",
    "author": "James Armes",
    "author_email": "jamesleearmes@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/77/c8/ac06e41da1088840866f82ffd0aeea08217e78eb6802640368004102e347/process_pilot-0.3.5.tar.gz",
    "platform": null,
    "description": "# Process Pilot\n\nProcess Pilot is a Python-based tool for managing and monitoring processes defined in a manifest file. It supports JSON and YAML formats for defining processes and their configurations.\n\n## Features\n\n- Load process configurations from JSON or YAML files.\n- Manage process lifecycles with customizable hooks.\n- Monitor process resource usage.\n- Define shutdown strategies for processes.\n- Define ready strategies to determine when launched processes are deemed to be \"running\".\n\n## Installation\n\nTo install the dependencies, use [Poetry](https://python-poetry.org/):\n\n```sh\npoetry install\n```\n\n## Usage\n\nYou can use the `ProcessPilot` class directly in your Python code to manage processes defined in a YAML or JSON file.\n\n### Process Control\n\nYou can restart specific processes by name:\n\n```python\nfrom process_pilot.pilot import ProcessPilot\nfrom process_pilot.process import ProcessManifest\nfrom pathlib import Path\n\n# Load manifest and start ProcessPilot\nmanifest = ProcessManifest.from_json(Path(\"manifest.json\"))\npilot = ProcessPilot(manifest)\npilot.start()\n\n# Later, restart specific processes\ntry:\n    pilot.restart_processes([\"api_server\", \"worker\"])\nexcept ValueError as e:\n    print(f\"Error restarting processes: {e}\")\n```\n\n### Example Usage\n\n#### Using a JSON Manifest\n\n```python\nfrom pathlib import Path\nfrom process_pilot.process import ProcessPilot, ProcessManifest\n\n# Load the process manifest from a JSON file\nmanifest_path = Path(\"path/to/your/manifest.json\")\nmanifest = ProcessManifest.from_json(manifest_path)\n\n# Create a ProcessPilot instance with the loaded manifest\npilot = ProcessPilot(manifest)\n\n# Start managing the processes\npilot.start()\n```\n\n#### Using a YAML Manifest\n\n```python\nfrom pathlib import Path\nfrom process_pilot.process import ProcessPilot, ProcessManifest\n\n# Load the process manifest from a YAML file\nmanifest_path = Path(\"path/to/your/manifest.yaml\")\nmanifest = ProcessManifest.from_yaml(manifest_path)\n\n# Create a ProcessPilot instance with the loaded manifest\npilot = ProcessPilot(manifest)\n\n# Start managing the processes\npilot.start()\n```\n\n## Configuration\n\n### Process Manifest\n\nThe process manifest defines the processes to be managed. It can be written in JSON or YAML format.\n\n#### Parameters\n\n- `name`: The name of the process. This should be unique within the manifest.\n- `path`: The path to the executable or script to be run.\n- `args`: A list of arguments to be passed to the process.\n- `timeout`: The maximum time (in seconds) to wait for the process to stop.\n- `shutdown_strategy`: The strategy to use when shutting down the process. Possible values are:\n  - `do_not_restart`: Do not restart the process after it stops.\n  - `restart`: Restart the process after it stops. This is the default.\n  - `shutdown_everything`: Stop all processes when this process stops.\n- `ready_strategy`: The strategy to use to determine when the process is ready. Possible values are:\n  - `tcp`: The process is ready when it starts listening on a specified TCP port.\n  - `pipe`: The process is ready when it writes a specific signal to a named pipe.\n  - `file`: The process is ready when a specific file is created.\n- `ready_timeout_sec`: The maximum time (in seconds) to wait for the process to be ready.\n- `ready_params`: Additional parameters for the ready strategy. These vary based on the strategy:\n  - For `tcp`, specify the `port` to check.\n  - For `pipe`, specify the `path` to the named pipe.\n  - For `file`, specify the `path` to the file.\n- `dependencies`: A list of other process names that must be started before this process can be started.\n- `env`: A dictionary of environment variables to set for the process.\n\nThe following is an example of a JSON manifest:\n\n```json\n{\n  \"processes\": [\n    {\n      \"name\": \"example\",\n      \"path\": \"sleep\",\n      \"args\": [\"5\"],\n      \"timeout\": 3,\n      \"shutdown_strategy\": \"do_not_restart\",\n      \"ready_strategy\": \"tcp\",\n      \"ready_timeout_sec\": 10.0,\n      \"ready_params\": {\n        \"port\": 8080\n      },\n      \"dependencies\": [\"another_process\"],\n      \"env\": {\n        \"ENV_VAR\": \"value\"\n      }\n    }\n  ]\n}\n```\n\nThe following is an example of a YAML manifest:\n\n```yaml\nprocesses:\n    - name: example\n        path: sleep\n        args: [\"5\"]\n        timeout: 1.0\n        shutdown_strategy: do_not_restart\n        ready_strategy: tcp\n        ready_timeout_sec: 10.0\n        ready_params:\n            port: 8080\n        dependencies:\n            - another_process\n        env:\n            ENV_VAR: value\n```\n\n## Plugin System\n\nProcess Pilot supports a plugin system that allows users to extend its functionality with custom lifecycle hooks, ready strategies, and process statistics handlers. Each created plugin can provide all three of the aforementioned groups, or just one. Each registration function links a name to a function or set of functions. The name provided must match what is in the manifest.\n\n### Plugin Registration Scoping\n\nPlugins in Process Pilot have one distinct registration scope:\n\n1. **Process-Specific Hooks**: Process lifecycle hooks, ready strategies, and process statistic handlers are only registered for processes that explicitly request them in their manifest configuration.\n\n### Creating a Plugin\n\nTo create a plugin, define a class that inherits from `Plugin` and implement the required methods:\n\n```python\n# import statements...\n\nclass FileReadyPlugin(Plugin):\n    def get_ready_strategies(self) -> dict[str, ReadyStrategyType]:\n        return {\n            \"file\": self._wait_file_ready,\n        }\n\n    def _wait_file_ready(self, process: \"Process\", ready_check_interval_secs: float) -> bool:\n        file_path = process.ready_params.get(\"path\")\n\n        if not file_path:\n            msg = \"Path not specified for file ready strategy\"\n            raise RuntimeError(msg)\n\n        file_path = Path(file_path)\n\n        start_time = time.time()\n        while (time.time() - start_time) < process.ready_timeout_sec:\n            if file_path.exists():\n                return True\n            time.sleep(ready_check_interval_secs)\n        return False\n```\n\nWhen creating plugins that implement a ready strategy it is important to keep in mind that you should always be checking readiness relative to\nthe start time--and always comparing the difference to the timeout value that is specified in the manifest. The\nsimplest example of this can be seen in the `FileReadyPlugin` shown above:\n\n```python\nstart_time = time.time()\nwhile (time.time() - start_time) < process.ready_timeout_sec:\n    if file_path.exists():\n        return True\n    time.sleep(ready_check_interval_secs)\n\n# Timeout\nreturn False\n```\n\nBe careful not to use readiness checks that block the threads ability to check for a timeout condition.\n\n### Plugin Registration\n\nOne way to use plugins with specific processes is to specify them in the manifest as shown below:\n\n```json\n{\n  \"processes\": [\n    {\n      \"name\": \"example_process\",\n      \"path\": \"myapp\",\n      \"lifecycle_hooks\": [\"custom_lifecycle_hooks\"],\n      \"ready_strategy\": \"custom_strategy\",\n      \"ready_timeout_sec\": 10.0\n    },\n    {\n      \"name\": \"another_process\",\n      \"path\": \"otherapp\"\n    }\n  ]\n}\n```\n\nIn this example, you must have loaded a plugin that provides a lifecycle hook with the name \"custom_lifecycle_hooks\" and a plugin that implements a ready strategy named \"custom_strategy\"--they do not have to be in the same plugin, but they could be.\n\nPlugins themselves are registered with process-pilot either in code directly:\n\n```python\nfrom pathlib import Path\nfrom process_pilot.process import ProcessPilot, ProcessManifest\nfrom custom_plugin import CustomPlugin\n\n# Load manifest\nmanifest = ProcessManifest.from_json(Path(\"manifest.json\"))\n\n# Create pilot and register plugins\npilot = ProcessPilot(manifest)\npilot.register_plugins([CustomPlugin()])\n\n# Start processes\npilot.start()\n```\n\nOr, by providing a plugin directory when the ProcessPilot object is constructed. See the API documentation for the ProcessPilot class, and the Plugin class for more details.\n\n## Control Server Plugins\n\nControl server plugins provide a way to add remote control capabilities to Process Pilot. A control server can be used to monitor and control processes at runtime through various interfaces (HTTP, TCP, Unix socket, etc.).\n\n### Creating a Control Server Plugin\n\nTo create a control server plugin:\n\n1. Create a class that implements your control server with at least these methods:\n\n   - `__init__(self, pilot: ProcessPilot)` - Constructor that receives the pilot instance\n   - `start(self)` - Called when Process Pilot starts\n   - `stop(self)` - Called when Process Pilot shuts down\n\n2. Create a plugin class that inherits from `Plugin` and registers your control server:\n\n```python\nfrom process_pilot.plugin import Plugin, ControlServerType\nfrom process_pilot.pilot import ProcessPilot\n\nclass MyControlServer:\n    def __init__(self, pilot: ProcessPilot):\n        self.pilot = pilot\n\n    def start(self):\n        # Start your control server\n        pass\n\n    def stop(self):\n        # Clean up resources\n        pass\n\nclass MyControlServerPlugin(Plugin):\n    def get_control_servers(self) -> dict[str, ControlServerType]:\n        return {\"my_server\": lambda pilot: MyControlServer(pilot)}\n```\n\n3. Register your plugin and enable it in the manifest:\n\n```python\npilot = ProcessPilot(manifest)\npilot.register_plugins([MyControlServerPlugin()])\n```\n\n```yaml\nmanifest:\n  control_server: my_server # Enable your control server\n  processes:\n    # ... process definitions ...\n```\n\n### Control Server Lifecycle\n\nThe following diagram shows when the control server is started and stopped:\n\n```mermaid\nsequenceDiagram\n    participant User\n    participant ProcessPilot\n    participant ControlServer\n    participant Processes\n\n    User->>ProcessPilot: Create(manifest)\n    ProcessPilot->>ProcessPilot: Load plugins\n    ProcessPilot->>ControlServer: Create\n\n    User->>ProcessPilot: start()\n    ProcessPilot->>ControlServer: start()\n    ProcessPilot->>Processes: Start processes\n\n    Note over ControlServer,Processes: Control server running...\n\n    User->>ProcessPilot: shutdown()\n    ProcessPilot->>Processes: Stop processes\n    ProcessPilot->>ControlServer: stop()\n    ProcessPilot->>User: Return\n```\n\n### Built-in Control Server Features\n\nThe ProcessPilot instance passed to the control server provides methods to:\n\n- Start/stop/restart processes\n- Get process statistics\n- Access the process manifest\n- Query process status\n\n## Process Lifecycle Hooks\n\nThe following diagram illustrates the process lifecycle and when various hook functions are called:\n\n```mermaid\ngraph TD\n        A[Start Process Pilot] --> B[Initialize Processes]\n        B --> C[Execute PRE_START Hooks]\n        C --> D[Start Process]\n        D --> E[Execute POST_START Hooks]\n        E --> F[Monitor Process]\n        F -->|Process Running| F\n        F -->|Process Exits| G[Execute ON_SHUTDOWN Hooks]\n        G --> H{Shutdown Strategy}\n        H -->|restart| I[Restart Process]\n        I --> J[Execute ON_RESTART Hooks]\n        J --> F\n        H -->|do_not_restart| K[Stop Monitoring]\n        H -->|shutdown_everything| L[Stop All Processes]\n```\n\nReady strategies tie in to this via:\n\n```mermaid\ngraph TD\nA[Start Process] --> B[Check Ready Strategy]\nB -->|Strategy 1| C[Execute Strategy 1]\nB -->|Strategy 2| D[Execute Strategy 2]\nB -->|Strategy 3| E[Execute Strategy 3]\nC --> F[Process Ready]\nD --> F[Process Ready]\nE --> F[Process Ready]\n```\n\nStatistic handlers tie into this via:\n\n```mermaid\ngraph TD\n        A[Monitor Process] --> B[Collect Statistics]\n        B --> C[Execute Statistic Handlers]\n        C --> D[Update Statistics]\n        D --> A\n```\n\n## Ready Strategies\n\nProcess Pilot supports three different strategies to determine if a process is ready, but more strategies can be provided via plugins:\n\n1. TCP Port Listening\n2. Named Pipe Signal\n3. File Presence\n\nEach ready strategy is only relevant for determining when dependent processes should be started. That is, if a given process has no dependencies, then specifying a ready strategy isn't currently meaningful. The following diagrams illustrate how each strategy works:\n\n### TCP Ready Strategy\n\n```mermaid\nsequenceDiagram\n    participant PP as Process Pilot\n    participant P as Process\n    participant TCP as TCP Port\n    PP->>P: Start Process\n    activate P\n    P->>TCP: Begin Listening\n    loop Until Ready or Timeout\n        PP->>TCP: Attempt Connection\n        alt Port is Listening\n            TCP-->>PP: Connection Success\n            PP->>PP: Process Ready\n        else Port not ready\n            TCP-->>PP: Connection Failed\n            Note over PP: Wait 0.1s\n        end\n    end\n    deactivate P\n```\n\n### Named Pipe Ready Strategy\n\n```mermaid\nsequenceDiagram\n    participant PP as Process Pilot\n    participant P as Process\n    participant Pipe as Named Pipe\n    PP->>Pipe: Create Pipe\n    PP->>P: Start Process\n    activate P\n    loop Until Ready or Timeout\n        PP->>Pipe: Read Pipe\n        alt Contains \"ready\"\n            Pipe-->>PP: \"ready\"\n            PP->>PP: Process Ready\n        else Not Ready\n            Pipe-->>PP: No Data/Error\n            Note over PP: Wait 0.1s\n        end\n    end\n    deactivate P\n```\n\n### File Ready Strategy\n\n```mermaid\nsequenceDiagram\n    participant PP as Process Pilot\n    participant P as Process\n    participant FS as File System\n    PP->>P: Start Process\n    activate P\n    loop Until Ready or Timeout\n        PP->>FS: Check File\n        alt File Exists\n            FS-->>PP: File Found\n            PP->>PP: Process Ready\n        else No File\n            FS-->>PP: No File\n            Note over PP: Wait 0.1s\n        end\n    end\n    deactivate P\n```\n\nEach strategy can be configured in the manifest:\n\n```yaml\nprocesses:\n  - name: example\n    path: myapp\n    ready_strategy: tcp # or \"pipe\" or \"file\"\n    ready_timeout_sec: 10.0\n    ready_params:\n      port: 8080 # for TCP\n      path: \"/tmp/ready.txt\" # for File\n```\n\n## Dependency Graph Visualization\n\nProcess Pilot includes a tool to visualize process dependencies using Graphviz. This helps you understand and validate the relationships between your processes.\n\n### Prerequisites\n\nThe graph visualization requires Graphviz to be installed on your system:\n\n```sh\n# Ubuntu/Debian\napt-get install graphviz\n\n# macOS\nbrew install graphviz\n\n# Windows\nchoco install graphviz\n```\n\n### Generating Dependency Graphs\n\nYou can generate a dependency graph from your process manifest using the `graph.py` module:\n\n```sh\nprocess-graph manifest.json --format png --output-dir ./graphs\n```\n\n### Command Line Options\n\n- manifest_path: Path to your JSON or YAML manifest file (required)\n- --format: Output format (png, svg, or pdf) - defaults to png\n- --output-dir: Directory to save the generated graph\n- --detailed: Include detailed process information in tooltips\n\n### Graph Features\n\n- Process nodes with their names\n- Directed edges showing dependencies\n- Color-coding for ready strategies:\n  - Light blue: TCP ready strategy\n  - Light green: File ready strategy\n  - Light yellow: Pipe ready strategy\n- Detailed tooltips (when using --detailed) showing:\n  - Process path\n  - Ready strategy\n  - Timeout values\n\n> NOTE: Detailed output is only available when the output is SVG\n\n### Example\n\nGiven this manifest:\n\n```json\n{\n  \"processes\": [\n    {\n      \"name\": \"database\",\n      \"path\": \"postgresql\",\n      \"ready_strategy\": \"tcp\",\n      \"ready_params\": {\n        \"port\": 5432\n      }\n    },\n    {\n      \"name\": \"api\",\n      \"path\": \"api_server\",\n      \"dependencies\": [\"database\"],\n      \"ready_strategy\": \"file\",\n      \"ready_params\": {\n        \"path\": \"/tmp/api_ready\"\n      }\n    },\n    {\n      \"name\": \"worker\",\n      \"path\": \"worker_service\",\n      \"dependencies\": [\"api\", \"database\"]\n    }\n  ]\n}\n```\n\nYou could generate the graph via:\n\n```sh\nprocess-graph manifest.json --format svg\n```\n\nThis will create a graph that will show:\n\n- `database` node (light blue) with no dependencies\n- `api` node (light green) depending on `database`\n- `worker` node (white) depending\n\n## Development\n\n### Running Tests\n\nTo run the tests, use:\n\n```sh\npoetry run pytest\n```\n\n### Build the documentation\n\nTo build the documentation, run the following from the top level of the repository:\n\n```sh\npoetry run sphinx-build -b html docs docs/_build/html\n```\n\n### Linting and Formatting\n\nTo lint and format the code, use:\n\n```sh\npoetry run ruff check .\npoetry run autopep8 --in-place --recursive .\n```\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE](../LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please open an issue or submit a pull request.\n\n## Contact\n\nFor any inquiries, please contact James Armes at jamesleearmes@gmail.com.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Process manager for Python",
    "version": "0.3.5",
    "project_urls": {
        "Documentation": "https://process-pilot.readthedocs.io/",
        "Homepage": "https://github.com/engineerjames/process-pilot",
        "Repository": "https://github.com/engineerjames/process-pilot"
    },
    "split_keywords": [
        "process",
        " management",
        " monitoring"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "74a7cc27c627a9a3bf9e4bafa3153fbac44e88c2da0cc81e2016afd0c9509563",
                "md5": "352ac28e2aaa1d4a1b568b7b39b0c585",
                "sha256": "82112bb87d092e1930fe23d24536b1769bd66c5aee364a1c8f6b53506d77e20b"
            },
            "downloads": -1,
            "filename": "process_pilot-0.3.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "352ac28e2aaa1d4a1b568b7b39b0c585",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<3.13,>=3.10",
            "size": 23483,
            "upload_time": "2025-01-13T16:33:01",
            "upload_time_iso_8601": "2025-01-13T16:33:01.009582Z",
            "url": "https://files.pythonhosted.org/packages/74/a7/cc27c627a9a3bf9e4bafa3153fbac44e88c2da0cc81e2016afd0c9509563/process_pilot-0.3.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "77c8ac06e41da1088840866f82ffd0aeea08217e78eb6802640368004102e347",
                "md5": "204a130b0eaae0b4ecee9bfd6d6ffc84",
                "sha256": "eb4e36273354e8316e88aa3323fd4202f71ea0018950bd9ca1184b692b96e96c"
            },
            "downloads": -1,
            "filename": "process_pilot-0.3.5.tar.gz",
            "has_sig": false,
            "md5_digest": "204a130b0eaae0b4ecee9bfd6d6ffc84",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<3.13,>=3.10",
            "size": 23610,
            "upload_time": "2025-01-13T16:33:04",
            "upload_time_iso_8601": "2025-01-13T16:33:04.914856Z",
            "url": "https://files.pythonhosted.org/packages/77/c8/ac06e41da1088840866f82ffd0aeea08217e78eb6802640368004102e347/process_pilot-0.3.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-01-13 16:33:04",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "engineerjames",
    "github_project": "process-pilot",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "sphinx",
            "specs": []
        },
        {
            "name": "sphinx-rtd-theme",
            "specs": []
        },
        {
            "name": "pylint",
            "specs": []
        },
        {
            "name": "pytest",
            "specs": []
        },
        {
            "name": "ruff",
            "specs": []
        },
        {
            "name": "mypy",
            "specs": []
        },
        {
            "name": "autopep8",
            "specs": []
        },
        {
            "name": "types-pyyaml",
            "specs": []
        },
        {
            "name": "pytest-mock",
            "specs": []
        },
        {
            "name": "types-psutil",
            "specs": []
        },
        {
            "name": "pydantic",
            "specs": []
        },
        {
            "name": "pyyaml",
            "specs": []
        },
        {
            "name": "psutil",
            "specs": []
        },
        {
            "name": "myst-parser",
            "specs": []
        },
        {
            "name": "sphinxcontrib-mermaid",
            "specs": []
        }
    ],
    "lcname": "process-pilot"
}
        
Elapsed time: 0.43565s