frequenz-dispatch 1.0.2__tar.gz → 1.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- frequenz_dispatch-1.0.3/PKG-INFO +225 -0
- frequenz_dispatch-1.0.3/README.md +155 -0
- frequenz_dispatch-1.0.3/RELEASE_NOTES.md +22 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/pyproject.toml +19 -19
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_bg_service.py +21 -8
- frequenz_dispatch-1.0.3/src/frequenz_dispatch.egg-info/PKG-INFO +225 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/requires.txt +18 -18
- frequenz_dispatch-1.0.2/PKG-INFO +0 -136
- frequenz_dispatch-1.0.2/README.md +0 -66
- frequenz_dispatch-1.0.2/RELEASE_NOTES.md +0 -5
- frequenz_dispatch-1.0.2/src/frequenz_dispatch.egg-info/PKG-INFO +0 -136
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/LICENSE +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/MANIFEST.in +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/setup.cfg +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/__init__.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_actor_dispatcher.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_dispatch.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_dispatcher.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_event.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_merge_strategies.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/conftest.py +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/py.typed +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/SOURCES.txt +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/dependency_links.txt +0 -0
- {frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-dispatch
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: A highlevel interface for the dispatch API
|
|
5
|
+
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-dispatch-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-dispatch-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-dispatch-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-dispatch-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-dispatch-python/discussions/categories/support
|
|
12
|
+
Keywords: frequenz,python,actor,frequenz-dispatch,dispatch,highlevel,api
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: <4,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typing-extensions<5.0.0,>=4.13.0
|
|
24
|
+
Requires-Dist: frequenz-sdk<1.0.0-rc2300,>=1.0.0-rc2100
|
|
25
|
+
Requires-Dist: frequenz-channels<2.0.0,>=1.6.1
|
|
26
|
+
Requires-Dist: frequenz-client-dispatch<2.0.0,>=0.11.3
|
|
27
|
+
Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
|
|
28
|
+
Provides-Extra: dev-flake8
|
|
29
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
30
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
31
|
+
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
32
|
+
Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
|
|
33
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
34
|
+
Provides-Extra: dev-formatting
|
|
35
|
+
Requires-Dist: black==25.12.0; extra == "dev-formatting"
|
|
36
|
+
Requires-Dist: isort==7.0.0; extra == "dev-formatting"
|
|
37
|
+
Provides-Extra: dev-mkdocs
|
|
38
|
+
Requires-Dist: black==25.12.0; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocs-gen-files==0.6.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
44
|
+
Requires-Dist: mkdocs-material==9.7.3; extra == "dev-mkdocs"
|
|
45
|
+
Requires-Dist: mkdocstrings[python]==1.0.3; extra == "dev-mkdocs"
|
|
46
|
+
Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
|
|
47
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-mkdocs"
|
|
48
|
+
Provides-Extra: dev-mypy
|
|
49
|
+
Requires-Dist: mypy==1.19.1; extra == "dev-mypy"
|
|
50
|
+
Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
|
|
51
|
+
Requires-Dist: types-Markdown==3.10.2.20260211; extra == "dev-mypy"
|
|
52
|
+
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
53
|
+
Provides-Extra: dev-noxfile
|
|
54
|
+
Requires-Dist: uv==0.10.7; extra == "dev-noxfile"
|
|
55
|
+
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-noxfile"
|
|
57
|
+
Provides-Extra: dev-pylint
|
|
58
|
+
Requires-Dist: pylint==4.0.5; extra == "dev-pylint"
|
|
59
|
+
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
60
|
+
Provides-Extra: dev-pytest
|
|
61
|
+
Requires-Dist: pytest==9.0.2; extra == "dev-pytest"
|
|
62
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.8; extra == "dev-pytest"
|
|
63
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
64
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
|
|
65
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
66
|
+
Requires-Dist: time-machine==3.2.0; extra == "dev-pytest"
|
|
67
|
+
Provides-Extra: dev
|
|
68
|
+
Requires-Dist: frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
69
|
+
Dynamic: license-file
|
|
70
|
+
|
|
71
|
+
# Dispatch Highlevel Interface
|
|
72
|
+
|
|
73
|
+
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
74
|
+
[](https://pypi.org/project/frequenz-dispatch/)
|
|
75
|
+
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
76
|
+
|
|
77
|
+
## Introduction
|
|
78
|
+
|
|
79
|
+
The `frequenz-dispatch` library provides a high-level Python interface for
|
|
80
|
+
interacting with the Frequenz Dispatch API. This library enables you to
|
|
81
|
+
manage and monitor dispatch operations in microgrids, including lifecycle
|
|
82
|
+
events and running status changes of dispatch operations.
|
|
83
|
+
|
|
84
|
+
The main entry point is the [`Dispatcher`][dispatcher-class] class, which
|
|
85
|
+
provides methods for receiving dispatch lifecycle events and running status
|
|
86
|
+
updates, allowing you to build reactive applications that respond to dispatch
|
|
87
|
+
state changes.
|
|
88
|
+
|
|
89
|
+
## Supported Platforms
|
|
90
|
+
|
|
91
|
+
The following platforms are officially supported (tested):
|
|
92
|
+
|
|
93
|
+
- **Python:** 3.11, 3.13
|
|
94
|
+
- **Operating System:** Ubuntu Linux 24.04
|
|
95
|
+
- **Architectures:** amd64, arm64
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
### Using pip
|
|
100
|
+
|
|
101
|
+
You can install the package from PyPI:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python3 -m pip install frequenz-dispatch
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Using pyproject.toml
|
|
108
|
+
|
|
109
|
+
Add the dependency to your `pyproject.toml` file:
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
[project]
|
|
113
|
+
dependencies = [
|
|
114
|
+
"frequenz-dispatch >= 1.0.1, < 2",
|
|
115
|
+
]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> [!NOTE]
|
|
119
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
120
|
+
> like `"frequenz-dispatch == 1.0.1"`, and specifying a version range
|
|
121
|
+
> spanning one major version for libraries, like
|
|
122
|
+
> `"frequenz-dispatch >= 1.0.1, < 2"`. We follow [semver](https://semver.org/).
|
|
123
|
+
|
|
124
|
+
## Quick Start
|
|
125
|
+
|
|
126
|
+
Here's a minimal example to get you started with lifecycle events:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import asyncio
|
|
130
|
+
import os
|
|
131
|
+
|
|
132
|
+
from frequenz.dispatch import Created, Deleted, Dispatcher, Updated
|
|
133
|
+
|
|
134
|
+
async def main() -> None:
|
|
135
|
+
url = os.getenv("DISPATCH_API_URL", "grpc://localhost:50051")
|
|
136
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
137
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
138
|
+
microgrid_id = 1
|
|
139
|
+
|
|
140
|
+
async with Dispatcher(
|
|
141
|
+
microgrid_id=microgrid_id,
|
|
142
|
+
server_url=url,
|
|
143
|
+
auth_key=auth_key,
|
|
144
|
+
sign_secret=sign_secret,
|
|
145
|
+
) as dispatcher:
|
|
146
|
+
events_receiver = dispatcher.new_lifecycle_events_receiver("MY_TYPE")
|
|
147
|
+
|
|
148
|
+
async for event in events_receiver:
|
|
149
|
+
match event:
|
|
150
|
+
case Created(dispatch):
|
|
151
|
+
print(f"Created: {dispatch}")
|
|
152
|
+
case Updated(dispatch):
|
|
153
|
+
print(f"Updated: {dispatch}")
|
|
154
|
+
case Deleted(dispatch):
|
|
155
|
+
print(f"Deleted: {dispatch}")
|
|
156
|
+
|
|
157
|
+
asyncio.run(main())
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The [`Dispatcher` class][dispatcher-class] provides two receiver methods:
|
|
161
|
+
|
|
162
|
+
* [`new_lifecycle_events_receiver()`][lifecycle-events]: Returns a receiver
|
|
163
|
+
that sends a message whenever a Dispatch is created, updated or deleted.
|
|
164
|
+
* [`new_running_state_event_receiver()`][running-status-change]: Returns a
|
|
165
|
+
receiver that sends a dispatch message whenever a dispatch is ready to be
|
|
166
|
+
executed according to the schedule or the running status of the dispatch
|
|
167
|
+
changed in a way that could potentially require the actor to start, stop or
|
|
168
|
+
reconfigure itself.
|
|
169
|
+
|
|
170
|
+
### Example managing actors with dispatch events
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
import os
|
|
174
|
+
from datetime import timedelta
|
|
175
|
+
from unittest.mock import MagicMock
|
|
176
|
+
|
|
177
|
+
from frequenz.channels import Receiver
|
|
178
|
+
from frequenz.sdk.actor import Actor
|
|
179
|
+
|
|
180
|
+
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
181
|
+
|
|
182
|
+
async def create_actor(
|
|
183
|
+
dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]
|
|
184
|
+
) -> Actor:
|
|
185
|
+
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
186
|
+
|
|
187
|
+
async def run() -> None:
|
|
188
|
+
url = os.getenv(
|
|
189
|
+
"DISPATCH_API_URL", "grpc://dispatch.api.example.com:50051"
|
|
190
|
+
)
|
|
191
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
192
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
193
|
+
|
|
194
|
+
microgrid_id = 1
|
|
195
|
+
|
|
196
|
+
async with Dispatcher(
|
|
197
|
+
microgrid_id=microgrid_id,
|
|
198
|
+
server_url=url,
|
|
199
|
+
auth_key=auth_key,
|
|
200
|
+
sign_secret=sign_secret,
|
|
201
|
+
) as dispatcher:
|
|
202
|
+
await dispatcher.start_managing(
|
|
203
|
+
dispatch_type="EXAMPLE_TYPE",
|
|
204
|
+
actor_factory=create_actor,
|
|
205
|
+
merge_strategy=MergeByType(),
|
|
206
|
+
retry_interval=timedelta(seconds=10)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
await dispatcher
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Documentation
|
|
213
|
+
|
|
214
|
+
For complete API documentation, examples, and advanced usage patterns, see
|
|
215
|
+
[the documentation][docs].
|
|
216
|
+
|
|
217
|
+
[dispatcher-class]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher
|
|
218
|
+
[lifecycle-events]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_lifecycle_events_receiver
|
|
219
|
+
[running-status-change]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_running_state_event_receiver
|
|
220
|
+
[docs]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
If you want to know how to build this project and contribute to it, please
|
|
225
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Dispatch Highlevel Interface
|
|
2
|
+
|
|
3
|
+
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
4
|
+
[](https://pypi.org/project/frequenz-dispatch/)
|
|
5
|
+
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
The `frequenz-dispatch` library provides a high-level Python interface for
|
|
10
|
+
interacting with the Frequenz Dispatch API. This library enables you to
|
|
11
|
+
manage and monitor dispatch operations in microgrids, including lifecycle
|
|
12
|
+
events and running status changes of dispatch operations.
|
|
13
|
+
|
|
14
|
+
The main entry point is the [`Dispatcher`][dispatcher-class] class, which
|
|
15
|
+
provides methods for receiving dispatch lifecycle events and running status
|
|
16
|
+
updates, allowing you to build reactive applications that respond to dispatch
|
|
17
|
+
state changes.
|
|
18
|
+
|
|
19
|
+
## Supported Platforms
|
|
20
|
+
|
|
21
|
+
The following platforms are officially supported (tested):
|
|
22
|
+
|
|
23
|
+
- **Python:** 3.11, 3.13
|
|
24
|
+
- **Operating System:** Ubuntu Linux 24.04
|
|
25
|
+
- **Architectures:** amd64, arm64
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Using pip
|
|
30
|
+
|
|
31
|
+
You can install the package from PyPI:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python3 -m pip install frequenz-dispatch
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Using pyproject.toml
|
|
38
|
+
|
|
39
|
+
Add the dependency to your `pyproject.toml` file:
|
|
40
|
+
|
|
41
|
+
```toml
|
|
42
|
+
[project]
|
|
43
|
+
dependencies = [
|
|
44
|
+
"frequenz-dispatch >= 1.0.1, < 2",
|
|
45
|
+
]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> [!NOTE]
|
|
49
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
50
|
+
> like `"frequenz-dispatch == 1.0.1"`, and specifying a version range
|
|
51
|
+
> spanning one major version for libraries, like
|
|
52
|
+
> `"frequenz-dispatch >= 1.0.1, < 2"`. We follow [semver](https://semver.org/).
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
Here's a minimal example to get you started with lifecycle events:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import asyncio
|
|
60
|
+
import os
|
|
61
|
+
|
|
62
|
+
from frequenz.dispatch import Created, Deleted, Dispatcher, Updated
|
|
63
|
+
|
|
64
|
+
async def main() -> None:
|
|
65
|
+
url = os.getenv("DISPATCH_API_URL", "grpc://localhost:50051")
|
|
66
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
67
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
68
|
+
microgrid_id = 1
|
|
69
|
+
|
|
70
|
+
async with Dispatcher(
|
|
71
|
+
microgrid_id=microgrid_id,
|
|
72
|
+
server_url=url,
|
|
73
|
+
auth_key=auth_key,
|
|
74
|
+
sign_secret=sign_secret,
|
|
75
|
+
) as dispatcher:
|
|
76
|
+
events_receiver = dispatcher.new_lifecycle_events_receiver("MY_TYPE")
|
|
77
|
+
|
|
78
|
+
async for event in events_receiver:
|
|
79
|
+
match event:
|
|
80
|
+
case Created(dispatch):
|
|
81
|
+
print(f"Created: {dispatch}")
|
|
82
|
+
case Updated(dispatch):
|
|
83
|
+
print(f"Updated: {dispatch}")
|
|
84
|
+
case Deleted(dispatch):
|
|
85
|
+
print(f"Deleted: {dispatch}")
|
|
86
|
+
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The [`Dispatcher` class][dispatcher-class] provides two receiver methods:
|
|
91
|
+
|
|
92
|
+
* [`new_lifecycle_events_receiver()`][lifecycle-events]: Returns a receiver
|
|
93
|
+
that sends a message whenever a Dispatch is created, updated or deleted.
|
|
94
|
+
* [`new_running_state_event_receiver()`][running-status-change]: Returns a
|
|
95
|
+
receiver that sends a dispatch message whenever a dispatch is ready to be
|
|
96
|
+
executed according to the schedule or the running status of the dispatch
|
|
97
|
+
changed in a way that could potentially require the actor to start, stop or
|
|
98
|
+
reconfigure itself.
|
|
99
|
+
|
|
100
|
+
### Example managing actors with dispatch events
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import os
|
|
104
|
+
from datetime import timedelta
|
|
105
|
+
from unittest.mock import MagicMock
|
|
106
|
+
|
|
107
|
+
from frequenz.channels import Receiver
|
|
108
|
+
from frequenz.sdk.actor import Actor
|
|
109
|
+
|
|
110
|
+
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
111
|
+
|
|
112
|
+
async def create_actor(
|
|
113
|
+
dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]
|
|
114
|
+
) -> Actor:
|
|
115
|
+
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
116
|
+
|
|
117
|
+
async def run() -> None:
|
|
118
|
+
url = os.getenv(
|
|
119
|
+
"DISPATCH_API_URL", "grpc://dispatch.api.example.com:50051"
|
|
120
|
+
)
|
|
121
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
122
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
123
|
+
|
|
124
|
+
microgrid_id = 1
|
|
125
|
+
|
|
126
|
+
async with Dispatcher(
|
|
127
|
+
microgrid_id=microgrid_id,
|
|
128
|
+
server_url=url,
|
|
129
|
+
auth_key=auth_key,
|
|
130
|
+
sign_secret=sign_secret,
|
|
131
|
+
) as dispatcher:
|
|
132
|
+
await dispatcher.start_managing(
|
|
133
|
+
dispatch_type="EXAMPLE_TYPE",
|
|
134
|
+
actor_factory=create_actor,
|
|
135
|
+
merge_strategy=MergeByType(),
|
|
136
|
+
retry_interval=timedelta(seconds=10)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
await dispatcher
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Documentation
|
|
143
|
+
|
|
144
|
+
For complete API documentation, examples, and advanced usage patterns, see
|
|
145
|
+
[the documentation][docs].
|
|
146
|
+
|
|
147
|
+
[dispatcher-class]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher
|
|
148
|
+
[lifecycle-events]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_lifecycle_events_receiver
|
|
149
|
+
[running-status-change]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_running_state_event_receiver
|
|
150
|
+
[docs]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/
|
|
151
|
+
|
|
152
|
+
## Contributing
|
|
153
|
+
|
|
154
|
+
If you want to know how to build this project and contribute to it, please
|
|
155
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Dispatch Highlevel Interface Release Notes
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
<!-- Here goes a general summary of what this release is about -->
|
|
6
|
+
|
|
7
|
+
## Upgrading
|
|
8
|
+
|
|
9
|
+
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
|
|
10
|
+
|
|
11
|
+
## New Features
|
|
12
|
+
|
|
13
|
+
<!-- Here goes the main new features and examples or instructions on how to use them -->
|
|
14
|
+
|
|
15
|
+
## Bug Fixes
|
|
16
|
+
|
|
17
|
+
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
|
|
18
|
+
|
|
19
|
+
* Fixed bugs that could cause a dispatch actor not to stop when an update
|
|
20
|
+
arrives exactly at the moment a dispatch window closes. Also fixed a
|
|
21
|
+
related issue that could cause incorrect event ordering when multiple
|
|
22
|
+
dispatches are updated concurrently.
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
[build-system]
|
|
5
5
|
requires = [
|
|
6
6
|
"setuptools == 80.9.0",
|
|
7
|
-
"setuptools_scm[toml] == 9.2.
|
|
8
|
-
"frequenz-repo-config[lib] == 0.13.
|
|
7
|
+
"setuptools_scm[toml] == 9.2.2",
|
|
8
|
+
"frequenz-repo-config[lib] == 0.13.8",
|
|
9
9
|
]
|
|
10
10
|
build-backend = "setuptools.build_meta"
|
|
11
11
|
|
|
@@ -53,48 +53,48 @@ email = "floss@frequenz.com"
|
|
|
53
53
|
dev-flake8 = [
|
|
54
54
|
"flake8 == 7.3.0",
|
|
55
55
|
"flake8-docstrings == 1.7.0",
|
|
56
|
-
"flake8-pyproject == 1.2.
|
|
56
|
+
"flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
|
|
57
57
|
"pydoclint == 0.8.3",
|
|
58
58
|
"pydocstyle == 6.3.0",
|
|
59
59
|
]
|
|
60
|
-
dev-formatting = ["black == 25.
|
|
60
|
+
dev-formatting = ["black == 25.12.0", "isort == 7.0.0"]
|
|
61
61
|
dev-mkdocs = [
|
|
62
|
-
"black == 25.
|
|
63
|
-
"Markdown==3.10",
|
|
62
|
+
"black == 25.12.0",
|
|
63
|
+
"Markdown==3.10.2",
|
|
64
64
|
"mike == 2.1.3",
|
|
65
|
-
"mkdocs-gen-files == 0.
|
|
65
|
+
"mkdocs-gen-files == 0.6.0",
|
|
66
66
|
"mkdocs-literate-nav == 0.6.2",
|
|
67
67
|
"mkdocs-macros-plugin == 1.5.0",
|
|
68
|
-
"mkdocs-material == 9.7.
|
|
69
|
-
"mkdocstrings[python] == 0.
|
|
68
|
+
"mkdocs-material == 9.7.3",
|
|
69
|
+
"mkdocstrings[python] == 1.0.3",
|
|
70
70
|
"mkdocstrings-python == 1.18.2",
|
|
71
|
-
"frequenz-repo-config[lib] == 0.13.
|
|
71
|
+
"frequenz-repo-config[lib] == 0.13.8",
|
|
72
72
|
]
|
|
73
73
|
dev-mypy = [
|
|
74
|
-
"mypy == 1.19.
|
|
74
|
+
"mypy == 1.19.1",
|
|
75
75
|
# This dependency introduces breaking changes in patch releases
|
|
76
76
|
"grpc-stubs == 1.53.0.6",
|
|
77
|
-
"types-Markdown == 3.10.
|
|
77
|
+
"types-Markdown == 3.10.2.20260211",
|
|
78
78
|
# For checking the noxfile, docs/ script, and tests
|
|
79
79
|
"frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
80
80
|
]
|
|
81
81
|
dev-noxfile = [
|
|
82
|
-
"uv == 0.
|
|
82
|
+
"uv == 0.10.7",
|
|
83
83
|
"nox == 2025.11.12",
|
|
84
|
-
"frequenz-repo-config[lib] == 0.13.
|
|
84
|
+
"frequenz-repo-config[lib] == 0.13.8",
|
|
85
85
|
]
|
|
86
86
|
dev-pylint = [
|
|
87
|
-
"pylint ==
|
|
87
|
+
"pylint == 4.0.5",
|
|
88
88
|
# For checking the noxfile, docs/ script, and tests
|
|
89
89
|
"frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]",
|
|
90
90
|
]
|
|
91
91
|
dev-pytest = [
|
|
92
|
-
"pytest ==
|
|
93
|
-
"frequenz-repo-config[extra-lint-examples] == 0.13.
|
|
92
|
+
"pytest == 9.0.2",
|
|
93
|
+
"frequenz-repo-config[extra-lint-examples] == 0.13.8",
|
|
94
94
|
"pytest-mock == 3.15.1",
|
|
95
|
-
"pytest-asyncio == 1.
|
|
95
|
+
"pytest-asyncio == 1.3.0",
|
|
96
96
|
"async-solipsism == 0.8",
|
|
97
|
-
"time-machine == 2.
|
|
97
|
+
"time-machine == 3.2.0",
|
|
98
98
|
]
|
|
99
99
|
dev = [
|
|
100
100
|
"frequenz-dispatch[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
|
|
@@ -13,7 +13,7 @@ from collections.abc import Mapping
|
|
|
13
13
|
from contextlib import closing
|
|
14
14
|
from dataclasses import dataclass, field
|
|
15
15
|
from datetime import datetime, timedelta, timezone
|
|
16
|
-
from heapq import heappop, heappush
|
|
16
|
+
from heapq import heapify, heappop, heappush
|
|
17
17
|
|
|
18
18
|
import grpc.aio
|
|
19
19
|
from frequenz.channels import Broadcast, Receiver, select, selected_from
|
|
@@ -228,7 +228,7 @@ class DispatchScheduler(BackgroundService):
|
|
|
228
228
|
"""Start the background service."""
|
|
229
229
|
self._tasks.add(asyncio.create_task(self._run()))
|
|
230
230
|
|
|
231
|
-
async def _run(self) -> None:
|
|
231
|
+
async def _run(self) -> None: # pylint: disable=too-many-branches
|
|
232
232
|
"""Run the background service."""
|
|
233
233
|
_logger.info(
|
|
234
234
|
"Starting dispatching background service for microgrid %s",
|
|
@@ -486,11 +486,19 @@ class DispatchScheduler(BackgroundService):
|
|
|
486
486
|
# Dispatch was updated
|
|
487
487
|
elif dispatch and old_dispatch:
|
|
488
488
|
# Remove potentially existing scheduled event
|
|
489
|
-
self._remove_scheduled(old_dispatch)
|
|
489
|
+
removed = self._remove_scheduled(old_dispatch)
|
|
490
490
|
|
|
491
491
|
# Check if the change requires an immediate notification
|
|
492
492
|
if self._update_changed_running_state(dispatch, old_dispatch):
|
|
493
493
|
await self._send_running_state_change(dispatch)
|
|
494
|
+
elif removed is not None and removed.priority == 1 and not dispatch.started:
|
|
495
|
+
# priority == 1 means a stop event (see QueueItem.__init__).
|
|
496
|
+
# If we removed a pending stop event and the dispatch is no
|
|
497
|
+
# longer started, the update arrived exactly at the stop
|
|
498
|
+
# boundary. The timer would have delivered the stop event, but
|
|
499
|
+
# _remove_scheduled consumed it first. Send the notification
|
|
500
|
+
# here so the actor is not left running past the window end.
|
|
501
|
+
await self._send_running_state_change(dispatch)
|
|
494
502
|
|
|
495
503
|
if dispatch.started:
|
|
496
504
|
self._schedule_stop(dispatch)
|
|
@@ -507,21 +515,26 @@ class DispatchScheduler(BackgroundService):
|
|
|
507
515
|
timer.reset(interval=due_at - datetime.now(timezone.utc))
|
|
508
516
|
_logger.debug("Next event scheduled at %s", self._scheduled_events[0].time)
|
|
509
517
|
|
|
510
|
-
def _remove_scheduled(self, dispatch: Dispatch) ->
|
|
518
|
+
def _remove_scheduled(self, dispatch: Dispatch) -> "QueueItem | None":
|
|
511
519
|
"""Remove a dispatch from the scheduled events.
|
|
512
520
|
|
|
513
521
|
Args:
|
|
514
522
|
dispatch: The dispatch to remove.
|
|
515
523
|
|
|
516
524
|
Returns:
|
|
517
|
-
|
|
525
|
+
The removed queue item, or None if not found.
|
|
518
526
|
"""
|
|
519
527
|
for idx, item in enumerate(self._scheduled_events):
|
|
520
528
|
if dispatch.id == item.dispatch.id:
|
|
521
529
|
self._scheduled_events.pop(idx)
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
530
|
+
# heappop() only removes the root (index 0) and does not accept
|
|
531
|
+
# an index argument, so we use list.pop(idx) instead. After
|
|
532
|
+
# removing an arbitrary element the heap property is broken and
|
|
533
|
+
# must be restored explicitly.
|
|
534
|
+
heapify(self._scheduled_events)
|
|
535
|
+
return item
|
|
536
|
+
|
|
537
|
+
return None
|
|
525
538
|
|
|
526
539
|
def _schedule_start(self, dispatch: Dispatch) -> None:
|
|
527
540
|
"""Schedule a dispatch to start.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frequenz-dispatch
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: A highlevel interface for the dispatch API
|
|
5
|
+
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-dispatch-python/
|
|
8
|
+
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-dispatch-python/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-dispatch-python/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-dispatch-python
|
|
11
|
+
Project-URL: Support, https://github.com/frequenz-floss/frequenz-dispatch-python/discussions/categories/support
|
|
12
|
+
Keywords: frequenz,python,actor,frequenz-dispatch,dispatch,highlevel,api
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: <4,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: typing-extensions<5.0.0,>=4.13.0
|
|
24
|
+
Requires-Dist: frequenz-sdk<1.0.0-rc2300,>=1.0.0-rc2100
|
|
25
|
+
Requires-Dist: frequenz-channels<2.0.0,>=1.6.1
|
|
26
|
+
Requires-Dist: frequenz-client-dispatch<2.0.0,>=0.11.3
|
|
27
|
+
Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
|
|
28
|
+
Provides-Extra: dev-flake8
|
|
29
|
+
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
30
|
+
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
31
|
+
Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
|
|
32
|
+
Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
|
|
33
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
34
|
+
Provides-Extra: dev-formatting
|
|
35
|
+
Requires-Dist: black==25.12.0; extra == "dev-formatting"
|
|
36
|
+
Requires-Dist: isort==7.0.0; extra == "dev-formatting"
|
|
37
|
+
Provides-Extra: dev-mkdocs
|
|
38
|
+
Requires-Dist: black==25.12.0; extra == "dev-mkdocs"
|
|
39
|
+
Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
|
|
40
|
+
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
41
|
+
Requires-Dist: mkdocs-gen-files==0.6.0; extra == "dev-mkdocs"
|
|
42
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
43
|
+
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
44
|
+
Requires-Dist: mkdocs-material==9.7.3; extra == "dev-mkdocs"
|
|
45
|
+
Requires-Dist: mkdocstrings[python]==1.0.3; extra == "dev-mkdocs"
|
|
46
|
+
Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
|
|
47
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-mkdocs"
|
|
48
|
+
Provides-Extra: dev-mypy
|
|
49
|
+
Requires-Dist: mypy==1.19.1; extra == "dev-mypy"
|
|
50
|
+
Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
|
|
51
|
+
Requires-Dist: types-Markdown==3.10.2.20260211; extra == "dev-mypy"
|
|
52
|
+
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
53
|
+
Provides-Extra: dev-noxfile
|
|
54
|
+
Requires-Dist: uv==0.10.7; extra == "dev-noxfile"
|
|
55
|
+
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
56
|
+
Requires-Dist: frequenz-repo-config[lib]==0.13.8; extra == "dev-noxfile"
|
|
57
|
+
Provides-Extra: dev-pylint
|
|
58
|
+
Requires-Dist: pylint==4.0.5; extra == "dev-pylint"
|
|
59
|
+
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
60
|
+
Provides-Extra: dev-pytest
|
|
61
|
+
Requires-Dist: pytest==9.0.2; extra == "dev-pytest"
|
|
62
|
+
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.8; extra == "dev-pytest"
|
|
63
|
+
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
64
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
|
|
65
|
+
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
66
|
+
Requires-Dist: time-machine==3.2.0; extra == "dev-pytest"
|
|
67
|
+
Provides-Extra: dev
|
|
68
|
+
Requires-Dist: frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
69
|
+
Dynamic: license-file
|
|
70
|
+
|
|
71
|
+
# Dispatch Highlevel Interface
|
|
72
|
+
|
|
73
|
+
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
74
|
+
[](https://pypi.org/project/frequenz-dispatch/)
|
|
75
|
+
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
76
|
+
|
|
77
|
+
## Introduction
|
|
78
|
+
|
|
79
|
+
The `frequenz-dispatch` library provides a high-level Python interface for
|
|
80
|
+
interacting with the Frequenz Dispatch API. This library enables you to
|
|
81
|
+
manage and monitor dispatch operations in microgrids, including lifecycle
|
|
82
|
+
events and running status changes of dispatch operations.
|
|
83
|
+
|
|
84
|
+
The main entry point is the [`Dispatcher`][dispatcher-class] class, which
|
|
85
|
+
provides methods for receiving dispatch lifecycle events and running status
|
|
86
|
+
updates, allowing you to build reactive applications that respond to dispatch
|
|
87
|
+
state changes.
|
|
88
|
+
|
|
89
|
+
## Supported Platforms
|
|
90
|
+
|
|
91
|
+
The following platforms are officially supported (tested):
|
|
92
|
+
|
|
93
|
+
- **Python:** 3.11, 3.13
|
|
94
|
+
- **Operating System:** Ubuntu Linux 24.04
|
|
95
|
+
- **Architectures:** amd64, arm64
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
### Using pip
|
|
100
|
+
|
|
101
|
+
You can install the package from PyPI:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python3 -m pip install frequenz-dispatch
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Using pyproject.toml
|
|
108
|
+
|
|
109
|
+
Add the dependency to your `pyproject.toml` file:
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
[project]
|
|
113
|
+
dependencies = [
|
|
114
|
+
"frequenz-dispatch >= 1.0.1, < 2",
|
|
115
|
+
]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> [!NOTE]
|
|
119
|
+
> We recommend pinning the dependency to the latest version for programs,
|
|
120
|
+
> like `"frequenz-dispatch == 1.0.1"`, and specifying a version range
|
|
121
|
+
> spanning one major version for libraries, like
|
|
122
|
+
> `"frequenz-dispatch >= 1.0.1, < 2"`. We follow [semver](https://semver.org/).
|
|
123
|
+
|
|
124
|
+
## Quick Start
|
|
125
|
+
|
|
126
|
+
Here's a minimal example to get you started with lifecycle events:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import asyncio
|
|
130
|
+
import os
|
|
131
|
+
|
|
132
|
+
from frequenz.dispatch import Created, Deleted, Dispatcher, Updated
|
|
133
|
+
|
|
134
|
+
async def main() -> None:
|
|
135
|
+
url = os.getenv("DISPATCH_API_URL", "grpc://localhost:50051")
|
|
136
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
137
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
138
|
+
microgrid_id = 1
|
|
139
|
+
|
|
140
|
+
async with Dispatcher(
|
|
141
|
+
microgrid_id=microgrid_id,
|
|
142
|
+
server_url=url,
|
|
143
|
+
auth_key=auth_key,
|
|
144
|
+
sign_secret=sign_secret,
|
|
145
|
+
) as dispatcher:
|
|
146
|
+
events_receiver = dispatcher.new_lifecycle_events_receiver("MY_TYPE")
|
|
147
|
+
|
|
148
|
+
async for event in events_receiver:
|
|
149
|
+
match event:
|
|
150
|
+
case Created(dispatch):
|
|
151
|
+
print(f"Created: {dispatch}")
|
|
152
|
+
case Updated(dispatch):
|
|
153
|
+
print(f"Updated: {dispatch}")
|
|
154
|
+
case Deleted(dispatch):
|
|
155
|
+
print(f"Deleted: {dispatch}")
|
|
156
|
+
|
|
157
|
+
asyncio.run(main())
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The [`Dispatcher` class][dispatcher-class] provides two receiver methods:
|
|
161
|
+
|
|
162
|
+
* [`new_lifecycle_events_receiver()`][lifecycle-events]: Returns a receiver
|
|
163
|
+
that sends a message whenever a Dispatch is created, updated or deleted.
|
|
164
|
+
* [`new_running_state_event_receiver()`][running-status-change]: Returns a
|
|
165
|
+
receiver that sends a dispatch message whenever a dispatch is ready to be
|
|
166
|
+
executed according to the schedule or the running status of the dispatch
|
|
167
|
+
changed in a way that could potentially require the actor to start, stop or
|
|
168
|
+
reconfigure itself.
|
|
169
|
+
|
|
170
|
+
### Example managing actors with dispatch events
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
import os
|
|
174
|
+
from datetime import timedelta
|
|
175
|
+
from unittest.mock import MagicMock
|
|
176
|
+
|
|
177
|
+
from frequenz.channels import Receiver
|
|
178
|
+
from frequenz.sdk.actor import Actor
|
|
179
|
+
|
|
180
|
+
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
181
|
+
|
|
182
|
+
async def create_actor(
|
|
183
|
+
dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]
|
|
184
|
+
) -> Actor:
|
|
185
|
+
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
186
|
+
|
|
187
|
+
async def run() -> None:
|
|
188
|
+
url = os.getenv(
|
|
189
|
+
"DISPATCH_API_URL", "grpc://dispatch.api.example.com:50051"
|
|
190
|
+
)
|
|
191
|
+
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "my-api-key")
|
|
192
|
+
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
193
|
+
|
|
194
|
+
microgrid_id = 1
|
|
195
|
+
|
|
196
|
+
async with Dispatcher(
|
|
197
|
+
microgrid_id=microgrid_id,
|
|
198
|
+
server_url=url,
|
|
199
|
+
auth_key=auth_key,
|
|
200
|
+
sign_secret=sign_secret,
|
|
201
|
+
) as dispatcher:
|
|
202
|
+
await dispatcher.start_managing(
|
|
203
|
+
dispatch_type="EXAMPLE_TYPE",
|
|
204
|
+
actor_factory=create_actor,
|
|
205
|
+
merge_strategy=MergeByType(),
|
|
206
|
+
retry_interval=timedelta(seconds=10)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
await dispatcher
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Documentation
|
|
213
|
+
|
|
214
|
+
For complete API documentation, examples, and advanced usage patterns, see
|
|
215
|
+
[the documentation][docs].
|
|
216
|
+
|
|
217
|
+
[dispatcher-class]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher
|
|
218
|
+
[lifecycle-events]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_lifecycle_events_receiver
|
|
219
|
+
[running-status-change]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.new_running_state_event_receiver
|
|
220
|
+
[docs]: https://frequenz-floss.github.io/frequenz-dispatch-python/latest/
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
If you want to know how to build this project and contribute to it, please
|
|
225
|
+
check out the [Contributing Guide](CONTRIBUTING.md).
|
{frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/requires.txt
RENAMED
|
@@ -10,45 +10,45 @@ frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-
|
|
|
10
10
|
[dev-flake8]
|
|
11
11
|
flake8==7.3.0
|
|
12
12
|
flake8-docstrings==1.7.0
|
|
13
|
-
flake8-pyproject==1.2.
|
|
13
|
+
flake8-pyproject==1.2.4
|
|
14
14
|
pydoclint==0.8.3
|
|
15
15
|
pydocstyle==6.3.0
|
|
16
16
|
|
|
17
17
|
[dev-formatting]
|
|
18
|
-
black==25.
|
|
19
|
-
isort==
|
|
18
|
+
black==25.12.0
|
|
19
|
+
isort==7.0.0
|
|
20
20
|
|
|
21
21
|
[dev-mkdocs]
|
|
22
|
-
black==25.
|
|
23
|
-
Markdown==3.10
|
|
22
|
+
black==25.12.0
|
|
23
|
+
Markdown==3.10.2
|
|
24
24
|
mike==2.1.3
|
|
25
|
-
mkdocs-gen-files==0.
|
|
25
|
+
mkdocs-gen-files==0.6.0
|
|
26
26
|
mkdocs-literate-nav==0.6.2
|
|
27
27
|
mkdocs-macros-plugin==1.5.0
|
|
28
|
-
mkdocs-material==9.7.
|
|
29
|
-
mkdocstrings[python]==0.
|
|
28
|
+
mkdocs-material==9.7.3
|
|
29
|
+
mkdocstrings[python]==1.0.3
|
|
30
30
|
mkdocstrings-python==1.18.2
|
|
31
|
-
frequenz-repo-config[lib]==0.13.
|
|
31
|
+
frequenz-repo-config[lib]==0.13.8
|
|
32
32
|
|
|
33
33
|
[dev-mypy]
|
|
34
|
-
mypy==1.19.
|
|
34
|
+
mypy==1.19.1
|
|
35
35
|
grpc-stubs==1.53.0.6
|
|
36
|
-
types-Markdown==3.10.
|
|
36
|
+
types-Markdown==3.10.2.20260211
|
|
37
37
|
frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
38
38
|
|
|
39
39
|
[dev-noxfile]
|
|
40
|
-
uv==0.
|
|
40
|
+
uv==0.10.7
|
|
41
41
|
nox==2025.11.12
|
|
42
|
-
frequenz-repo-config[lib]==0.13.
|
|
42
|
+
frequenz-repo-config[lib]==0.13.8
|
|
43
43
|
|
|
44
44
|
[dev-pylint]
|
|
45
|
-
pylint==
|
|
45
|
+
pylint==4.0.5
|
|
46
46
|
frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]
|
|
47
47
|
|
|
48
48
|
[dev-pytest]
|
|
49
|
-
pytest==
|
|
50
|
-
frequenz-repo-config[extra-lint-examples]==0.13.
|
|
49
|
+
pytest==9.0.2
|
|
50
|
+
frequenz-repo-config[extra-lint-examples]==0.13.8
|
|
51
51
|
pytest-mock==3.15.1
|
|
52
|
-
pytest-asyncio==1.
|
|
52
|
+
pytest-asyncio==1.3.0
|
|
53
53
|
async-solipsism==0.8
|
|
54
|
-
time-machine==2.
|
|
54
|
+
time-machine==3.2.0
|
frequenz_dispatch-1.0.2/PKG-INFO
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: frequenz-dispatch
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: A highlevel interface for the dispatch API
|
|
5
|
-
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-dispatch-python/
|
|
8
|
-
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-dispatch-python/releases
|
|
9
|
-
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-dispatch-python/issues
|
|
10
|
-
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-dispatch-python
|
|
11
|
-
Project-URL: Support, https://github.com/frequenz-floss/frequenz-dispatch-python/discussions/categories/support
|
|
12
|
-
Keywords: frequenz,python,actor,frequenz-dispatch,dispatch,highlevel,api
|
|
13
|
-
Classifier: Development Status :: 3 - Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Classifier: Typing :: Typed
|
|
20
|
-
Requires-Python: <4,>=3.11
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: typing-extensions<5.0.0,>=4.13.0
|
|
24
|
-
Requires-Dist: frequenz-sdk<1.0.0-rc2300,>=1.0.0-rc2100
|
|
25
|
-
Requires-Dist: frequenz-channels<2.0.0,>=1.6.1
|
|
26
|
-
Requires-Dist: frequenz-client-dispatch<2.0.0,>=0.11.3
|
|
27
|
-
Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
|
|
28
|
-
Provides-Extra: dev-flake8
|
|
29
|
-
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
30
|
-
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
31
|
-
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
32
|
-
Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
|
|
33
|
-
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
34
|
-
Provides-Extra: dev-formatting
|
|
35
|
-
Requires-Dist: black==25.11.0; extra == "dev-formatting"
|
|
36
|
-
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
37
|
-
Provides-Extra: dev-mkdocs
|
|
38
|
-
Requires-Dist: black==25.11.0; extra == "dev-mkdocs"
|
|
39
|
-
Requires-Dist: Markdown==3.10; extra == "dev-mkdocs"
|
|
40
|
-
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
41
|
-
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
42
|
-
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
43
|
-
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
44
|
-
Requires-Dist: mkdocs-material==9.7.0; extra == "dev-mkdocs"
|
|
45
|
-
Requires-Dist: mkdocstrings[python]==0.30.1; extra == "dev-mkdocs"
|
|
46
|
-
Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
|
|
47
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-mkdocs"
|
|
48
|
-
Provides-Extra: dev-mypy
|
|
49
|
-
Requires-Dist: mypy==1.19.0; extra == "dev-mypy"
|
|
50
|
-
Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
|
|
51
|
-
Requires-Dist: types-Markdown==3.10.0.20251106; extra == "dev-mypy"
|
|
52
|
-
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
53
|
-
Provides-Extra: dev-noxfile
|
|
54
|
-
Requires-Dist: uv==0.9.13; extra == "dev-noxfile"
|
|
55
|
-
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
56
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-noxfile"
|
|
57
|
-
Provides-Extra: dev-pylint
|
|
58
|
-
Requires-Dist: pylint==3.3.8; extra == "dev-pylint"
|
|
59
|
-
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
60
|
-
Provides-Extra: dev-pytest
|
|
61
|
-
Requires-Dist: pytest==8.4.2; extra == "dev-pytest"
|
|
62
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.5; extra == "dev-pytest"
|
|
63
|
-
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
64
|
-
Requires-Dist: pytest-asyncio==1.2.0; extra == "dev-pytest"
|
|
65
|
-
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
66
|
-
Requires-Dist: time-machine==2.19.0; extra == "dev-pytest"
|
|
67
|
-
Provides-Extra: dev
|
|
68
|
-
Requires-Dist: frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
69
|
-
Dynamic: license-file
|
|
70
|
-
|
|
71
|
-
# Dispatch Highlevel Interface
|
|
72
|
-
|
|
73
|
-
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
74
|
-
[](https://pypi.org/project/frequenz-dispatch/)
|
|
75
|
-
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
76
|
-
|
|
77
|
-
## Introduction
|
|
78
|
-
|
|
79
|
-
A highlevel interface for the dispatch API.
|
|
80
|
-
|
|
81
|
-
See [the documentation](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch) for more information.
|
|
82
|
-
|
|
83
|
-
## Usage
|
|
84
|
-
|
|
85
|
-
The [`Dispatcher` class](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher), the main entry point for the API, provides two channels:
|
|
86
|
-
|
|
87
|
-
* [Lifecycle events](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.lifecycle_events): A channel that sends a message whenever a [Dispatch][frequenz.dispatch.Dispatch] is created, updated or deleted.
|
|
88
|
-
* [Running status change](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.running_status_change): 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.
|
|
89
|
-
|
|
90
|
-
### Example using the running status change channel
|
|
91
|
-
|
|
92
|
-
```python
|
|
93
|
-
import os
|
|
94
|
-
from unittest.mock import MagicMock
|
|
95
|
-
from datetime import timedelta
|
|
96
|
-
|
|
97
|
-
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
98
|
-
|
|
99
|
-
async def create_actor(dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]) -> Actor:
|
|
100
|
-
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
101
|
-
|
|
102
|
-
async def run():
|
|
103
|
-
url = os.getenv("DISPATCH_API_URL", "grpc://dispatch.url.goes.here.example.com")
|
|
104
|
-
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "some-key")
|
|
105
|
-
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
106
|
-
|
|
107
|
-
microgrid_id = 1
|
|
108
|
-
|
|
109
|
-
async with Dispatcher(
|
|
110
|
-
microgrid_id=microgrid_id,
|
|
111
|
-
server_url=url,
|
|
112
|
-
auth_key=auth_key,
|
|
113
|
-
sign_secret=sign_secret,
|
|
114
|
-
) as dispatcher:
|
|
115
|
-
await dispatcher.start_managing(
|
|
116
|
-
dispatch_type="EXAMPLE_TYPE",
|
|
117
|
-
actor_factory=create_actor,
|
|
118
|
-
merge_strategy=MergeByType(),
|
|
119
|
-
retry_interval=timedelta(seconds=10)
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
await dispatcher
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Supported Platforms
|
|
126
|
-
|
|
127
|
-
The following platforms are officially supported (tested):
|
|
128
|
-
|
|
129
|
-
- **Python:** 3.11
|
|
130
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
131
|
-
- **Architectures:** amd64, arm64
|
|
132
|
-
|
|
133
|
-
## Contributing
|
|
134
|
-
|
|
135
|
-
If you want to know how to build this project and contribute to it, please
|
|
136
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Dispatch Highlevel Interface
|
|
2
|
-
|
|
3
|
-
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
4
|
-
[](https://pypi.org/project/frequenz-dispatch/)
|
|
5
|
-
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
6
|
-
|
|
7
|
-
## Introduction
|
|
8
|
-
|
|
9
|
-
A highlevel interface for the dispatch API.
|
|
10
|
-
|
|
11
|
-
See [the documentation](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch) for more information.
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
The [`Dispatcher` class](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher), the main entry point for the API, provides two channels:
|
|
16
|
-
|
|
17
|
-
* [Lifecycle events](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.lifecycle_events): A channel that sends a message whenever a [Dispatch][frequenz.dispatch.Dispatch] is created, updated or deleted.
|
|
18
|
-
* [Running status change](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.running_status_change): 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.
|
|
19
|
-
|
|
20
|
-
### Example using the running status change channel
|
|
21
|
-
|
|
22
|
-
```python
|
|
23
|
-
import os
|
|
24
|
-
from unittest.mock import MagicMock
|
|
25
|
-
from datetime import timedelta
|
|
26
|
-
|
|
27
|
-
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
28
|
-
|
|
29
|
-
async def create_actor(dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]) -> Actor:
|
|
30
|
-
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
31
|
-
|
|
32
|
-
async def run():
|
|
33
|
-
url = os.getenv("DISPATCH_API_URL", "grpc://dispatch.url.goes.here.example.com")
|
|
34
|
-
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "some-key")
|
|
35
|
-
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
36
|
-
|
|
37
|
-
microgrid_id = 1
|
|
38
|
-
|
|
39
|
-
async with Dispatcher(
|
|
40
|
-
microgrid_id=microgrid_id,
|
|
41
|
-
server_url=url,
|
|
42
|
-
auth_key=auth_key,
|
|
43
|
-
sign_secret=sign_secret,
|
|
44
|
-
) as dispatcher:
|
|
45
|
-
await dispatcher.start_managing(
|
|
46
|
-
dispatch_type="EXAMPLE_TYPE",
|
|
47
|
-
actor_factory=create_actor,
|
|
48
|
-
merge_strategy=MergeByType(),
|
|
49
|
-
retry_interval=timedelta(seconds=10)
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
await dispatcher
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Supported Platforms
|
|
56
|
-
|
|
57
|
-
The following platforms are officially supported (tested):
|
|
58
|
-
|
|
59
|
-
- **Python:** 3.11
|
|
60
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
61
|
-
- **Architectures:** amd64, arm64
|
|
62
|
-
|
|
63
|
-
## Contributing
|
|
64
|
-
|
|
65
|
-
If you want to know how to build this project and contribute to it, please
|
|
66
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: frequenz-dispatch
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: A highlevel interface for the dispatch API
|
|
5
|
-
Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-dispatch-python/
|
|
8
|
-
Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-dispatch-python/releases
|
|
9
|
-
Project-URL: Issues, https://github.com/frequenz-floss/frequenz-dispatch-python/issues
|
|
10
|
-
Project-URL: Repository, https://github.com/frequenz-floss/frequenz-dispatch-python
|
|
11
|
-
Project-URL: Support, https://github.com/frequenz-floss/frequenz-dispatch-python/discussions/categories/support
|
|
12
|
-
Keywords: frequenz,python,actor,frequenz-dispatch,dispatch,highlevel,api
|
|
13
|
-
Classifier: Development Status :: 3 - Alpha
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
-
Classifier: Typing :: Typed
|
|
20
|
-
Requires-Python: <4,>=3.11
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
License-File: LICENSE
|
|
23
|
-
Requires-Dist: typing-extensions<5.0.0,>=4.13.0
|
|
24
|
-
Requires-Dist: frequenz-sdk<1.0.0-rc2300,>=1.0.0-rc2100
|
|
25
|
-
Requires-Dist: frequenz-channels<2.0.0,>=1.6.1
|
|
26
|
-
Requires-Dist: frequenz-client-dispatch<2.0.0,>=0.11.3
|
|
27
|
-
Requires-Dist: frequenz-client-base<0.12.0,>=0.11.0
|
|
28
|
-
Provides-Extra: dev-flake8
|
|
29
|
-
Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
|
|
30
|
-
Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
|
|
31
|
-
Requires-Dist: flake8-pyproject==1.2.3; extra == "dev-flake8"
|
|
32
|
-
Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
|
|
33
|
-
Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
|
|
34
|
-
Provides-Extra: dev-formatting
|
|
35
|
-
Requires-Dist: black==25.11.0; extra == "dev-formatting"
|
|
36
|
-
Requires-Dist: isort==6.0.1; extra == "dev-formatting"
|
|
37
|
-
Provides-Extra: dev-mkdocs
|
|
38
|
-
Requires-Dist: black==25.11.0; extra == "dev-mkdocs"
|
|
39
|
-
Requires-Dist: Markdown==3.10; extra == "dev-mkdocs"
|
|
40
|
-
Requires-Dist: mike==2.1.3; extra == "dev-mkdocs"
|
|
41
|
-
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "dev-mkdocs"
|
|
42
|
-
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "dev-mkdocs"
|
|
43
|
-
Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
|
|
44
|
-
Requires-Dist: mkdocs-material==9.7.0; extra == "dev-mkdocs"
|
|
45
|
-
Requires-Dist: mkdocstrings[python]==0.30.1; extra == "dev-mkdocs"
|
|
46
|
-
Requires-Dist: mkdocstrings-python==1.18.2; extra == "dev-mkdocs"
|
|
47
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-mkdocs"
|
|
48
|
-
Provides-Extra: dev-mypy
|
|
49
|
-
Requires-Dist: mypy==1.19.0; extra == "dev-mypy"
|
|
50
|
-
Requires-Dist: grpc-stubs==1.53.0.6; extra == "dev-mypy"
|
|
51
|
-
Requires-Dist: types-Markdown==3.10.0.20251106; extra == "dev-mypy"
|
|
52
|
-
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
|
|
53
|
-
Provides-Extra: dev-noxfile
|
|
54
|
-
Requires-Dist: uv==0.9.13; extra == "dev-noxfile"
|
|
55
|
-
Requires-Dist: nox==2025.11.12; extra == "dev-noxfile"
|
|
56
|
-
Requires-Dist: frequenz-repo-config[lib]==0.13.5; extra == "dev-noxfile"
|
|
57
|
-
Provides-Extra: dev-pylint
|
|
58
|
-
Requires-Dist: pylint==3.3.8; extra == "dev-pylint"
|
|
59
|
-
Requires-Dist: frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
|
|
60
|
-
Provides-Extra: dev-pytest
|
|
61
|
-
Requires-Dist: pytest==8.4.2; extra == "dev-pytest"
|
|
62
|
-
Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.13.5; extra == "dev-pytest"
|
|
63
|
-
Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
|
|
64
|
-
Requires-Dist: pytest-asyncio==1.2.0; extra == "dev-pytest"
|
|
65
|
-
Requires-Dist: async-solipsism==0.8; extra == "dev-pytest"
|
|
66
|
-
Requires-Dist: time-machine==2.19.0; extra == "dev-pytest"
|
|
67
|
-
Provides-Extra: dev
|
|
68
|
-
Requires-Dist: frequenz-dispatch[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
|
|
69
|
-
Dynamic: license-file
|
|
70
|
-
|
|
71
|
-
# Dispatch Highlevel Interface
|
|
72
|
-
|
|
73
|
-
[](https://github.com/frequenz-floss/frequenz-dispatch-python/actions/workflows/ci.yaml)
|
|
74
|
-
[](https://pypi.org/project/frequenz-dispatch/)
|
|
75
|
-
[](https://frequenz-floss.github.io/frequenz-dispatch-python/)
|
|
76
|
-
|
|
77
|
-
## Introduction
|
|
78
|
-
|
|
79
|
-
A highlevel interface for the dispatch API.
|
|
80
|
-
|
|
81
|
-
See [the documentation](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch) for more information.
|
|
82
|
-
|
|
83
|
-
## Usage
|
|
84
|
-
|
|
85
|
-
The [`Dispatcher` class](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher), the main entry point for the API, provides two channels:
|
|
86
|
-
|
|
87
|
-
* [Lifecycle events](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.lifecycle_events): A channel that sends a message whenever a [Dispatch][frequenz.dispatch.Dispatch] is created, updated or deleted.
|
|
88
|
-
* [Running status change](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.running_status_change): 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.
|
|
89
|
-
|
|
90
|
-
### Example using the running status change channel
|
|
91
|
-
|
|
92
|
-
```python
|
|
93
|
-
import os
|
|
94
|
-
from unittest.mock import MagicMock
|
|
95
|
-
from datetime import timedelta
|
|
96
|
-
|
|
97
|
-
from frequenz.dispatch import Dispatcher, DispatchInfo, MergeByType
|
|
98
|
-
|
|
99
|
-
async def create_actor(dispatch: DispatchInfo, receiver: Receiver[DispatchInfo]) -> Actor:
|
|
100
|
-
return MagicMock(dispatch=dispatch, receiver=receiver)
|
|
101
|
-
|
|
102
|
-
async def run():
|
|
103
|
-
url = os.getenv("DISPATCH_API_URL", "grpc://dispatch.url.goes.here.example.com")
|
|
104
|
-
auth_key = os.getenv("DISPATCH_API_AUTH_KEY", "some-key")
|
|
105
|
-
sign_secret = os.getenv("DISPATCH_API_SIGN_SECRET")
|
|
106
|
-
|
|
107
|
-
microgrid_id = 1
|
|
108
|
-
|
|
109
|
-
async with Dispatcher(
|
|
110
|
-
microgrid_id=microgrid_id,
|
|
111
|
-
server_url=url,
|
|
112
|
-
auth_key=auth_key,
|
|
113
|
-
sign_secret=sign_secret,
|
|
114
|
-
) as dispatcher:
|
|
115
|
-
await dispatcher.start_managing(
|
|
116
|
-
dispatch_type="EXAMPLE_TYPE",
|
|
117
|
-
actor_factory=create_actor,
|
|
118
|
-
merge_strategy=MergeByType(),
|
|
119
|
-
retry_interval=timedelta(seconds=10)
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
await dispatcher
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Supported Platforms
|
|
126
|
-
|
|
127
|
-
The following platforms are officially supported (tested):
|
|
128
|
-
|
|
129
|
-
- **Python:** 3.11
|
|
130
|
-
- **Operating System:** Ubuntu Linux 20.04
|
|
131
|
-
- **Architectures:** amd64, arm64
|
|
132
|
-
|
|
133
|
-
## Contributing
|
|
134
|
-
|
|
135
|
-
If you want to know how to build this project and contribute to it, please
|
|
136
|
-
check out the [Contributing Guide](CONTRIBUTING.md).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_actor_dispatcher.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz/dispatch/_merge_strategies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{frequenz_dispatch-1.0.2 → frequenz_dispatch-1.0.3}/src/frequenz_dispatch.egg-info/top_level.txt
RENAMED
|
File without changes
|