Metadata-Version: 2.4
Name: frequenz-dispatch
Version: 1.0.3
Summary: A highlevel interface for the dispatch API
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
License: MIT
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-dispatch-python/
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-dispatch-python/releases
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-dispatch-python/issues
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-dispatch-python
Project-URL: Support, https://github.com/frequenz-floss/frequenz-dispatch-python/discussions/categories/support
Keywords: frequenz,python,actor,frequenz-dispatch,dispatch,highlevel,api
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: <4,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typing-extensions<5.0.0,>=4.13.0
Requires-Dist: frequenz-sdk<1.0.0-rc2300,>=1.0.0-rc2100
Requires-Dist: frequenz-channels<2.0.0,>=1.6.1
Requires-Dist: frequenz-client-dispatch<2.0.0,>=0.11.3
Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
Provides-Extra: dev-flake8
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
Provides-Extra: dev-formatting
Requires-Dist: black==25.12.0; extra == "dev-formatting"
Requires-Dist: isort==7.0.0; extra == "dev-formatting"
Provides-Extra: dev-mkdocs
Requires-Dist: black==25.12.0; extra == "dev-mkdocs"
Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
Requires-Dist: mkdocs-gen-files==0.6.0; extra == "dev-mkdocs"
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
Requires-Dist: mkdocs-material==9.7.3; extra == "dev-mkdocs"
Requires-Dist: mkdocstrings[python]==1.0.3; extra == "dev-mkdocs"
Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-mkdocs"
Provides-Extra: dev-mypy
Requires-Dist: mypy==1.19.1; extra == "dev-mypy"
Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
Requires-Dist: types-Markdown==3.10.2.20260211; extra == "dev-mypy"
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
Provides-Extra: dev-noxfile
Requires-Dist: uv==0.10.7; extra == "dev-noxfile"
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-noxfile"
Provides-Extra: dev-pylint
Requires-Dist: pylint==4.0.5; extra == "dev-pylint"
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
Provides-Extra: dev-pytest
Requires-Dist: pytest==9.0.2; extra == "dev-pytest"
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.8; extra == "dev-pytest"
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
Requires-Dist: time-machine==3.2.0; extra == "dev-pytest"
Provides-Extra: dev
Requires-Dist: frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
Dynamic: license-file

# Dispatch Highlevel Interface

[![Build Status](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
[![PyPI Package](https://img.shields.io/pypi/v/frequenz-dispatch)](https://pypi.org/project/frequenz-dispatch/)
[![Docs](https://img.shields.io/badge/docs-latest-informational)](https://frequenz-floss.github.io/frequenz-dispatch-python/)

## Introduction

The `frequenz-dispatch` library provides a high-level Python interface for
interacting with the Frequenz Dispatch API. This library enables you to
manage and monitor dispatch operations in microgrids, including lifecycle
events and running status changes of dispatch operations.

The main entry point is the [`Dispatcher`][dispatcher-class] class, which
provides methods for receiving dispatch lifecycle events and running status
updates, allowing you to build reactive applications that respond to dispatch
state changes.

## Supported Platforms

The following platforms are officially supported (tested):

- **Python:** 3.11, 3.13
- **Operating System:** Ubuntu Linux 24.04
- **Architectures:** amd64, arm64

## Installation

### Using pip

You can install the package from PyPI:

```bash
python3 -m pip install frequenz-dispatch
```

### Using pyproject.toml

Add the dependency to your `pyproject.toml` file:

```toml
[project]
dependencies = [
    "frequenz-dispatch >= 1.0.1, < 2",
]
```

> [!NOTE]
> We recommend pinning the dependency to the latest version for programs,
> like `"frequenz-dispatch == 1.0.1"`, and specifying a version range
> spanning one major version for libraries, like
> `"frequenz-dispatch >= 1.0.1, < 2"`. We follow [semver](https://semver.org/).

## Quick Start

Here's a minimal example to get you started with lifecycle events:

```python
import asyncio
import os

from frequenz.dispatch import Created, Deleted, Dispatcher, Updated

async def main() -> None:
    url = os.getenv("DISPATCH_API_URL", "grpc://localhost:50051")
    auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
    sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
    microgrid_id = 1

    async with Dispatcher(
        microgrid_id=microgrid_id,
        server_url=url,
        auth_key=auth_key,
        sign_secret=sign_secret,
    ) as dispatcher:
        events_receiver = dispatcher.new_lifecycle_events_receiver("MY_TYPE")

        async for event in events_receiver:
            match event:
                case Created(dispatch):
                    print(f"Created: {dispatch}")
                case Updated(dispatch):
                    print(f"Updated: {dispatch}")
                case Deleted(dispatch):
                    print(f"Deleted: {dispatch}")

asyncio.run(main())
```

The [`Dispatcher` class][dispatcher-class] provides two receiver methods:

* [`new_lifecycle_events_receiver()`][lifecycle-events]: Returns a receiver
  that sends a message whenever a Dispatch is created, updated or deleted.
* [`new_running_state_event_receiver()`][running-status-change]: Returns a
  receiver that sends a dispatch message whenever a dispatch is ready to be
  executed according to the schedule or the running status of the dispatch
  changed in a way that could potentially require the actor to start, stop or
  reconfigure itself.

### Example managing actors with dispatch events

```python
import os
from datetime import timedelta
from unittest.mock import MagicMock

from frequenz.channels import Receiver
from frequenz.sdk.actor import Actor

from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType

async def create_actor(
    dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]
) -> Actor:
    return MagicMock(dispatch=dispatch, receiver=receiver)

async def run() -> None:
    url = os.getenv(
        "DISPATCH_API_URL", "grpc://dispatch.api.example.com:50051"
    )
    auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
    sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")

    microgrid_id = 1

    async with Dispatcher(
        microgrid_id=microgrid_id,
        server_url=url,
        auth_key=auth_key,
        sign_secret=sign_secret,
    ) as dispatcher:
        await dispatcher.start_managing(
            dispatch_type="EXAMPLE_TYPE",
            actor_factory=create_actor,
            merge_strategy=MergeByType(),
            retry_interval=timedelta(seconds=10)
        )

        await dispatcher
```

## Documentation

For complete API documentation, examples, and advanced usage patterns, see
[the documentation][docs].

[dispatcher-class]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher
[lifecycle-events]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_lifecycle_events_receiver
[running-status-change]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_running_state_event_receiver
[docs]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/

## Contributing

If you want to know how to build this project and contribute to it, please
check out the [Contributing Guide](CONTRIBUTING.md).
