Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial structure #9

Merged
merged 8 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# For more details on the configuration please see:
# https://github.com/marketplace/actions/labeler

# TODO(cookiecutter): Add different parts of the source
# For example:
#
# "part:module":
Expand Down Expand Up @@ -65,4 +64,4 @@
"part:actor":
- changed-files:
- any-glob-to-any-file:
- "src/frequenz/dispatch/actor/**"
- "src/frequenz/dispatch/actor.py"
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
--net=host \
--platform linux/${{ matrix.arch }} \
localhost/nox-cross-arch:latest \
bash -c "pip install -e .[dev-noxfile]; nox --install-only -e ${{ matrix.nox-session }}; pip freeze; nox -e ${{ matrix.nox-session }}"
bash -c "pip install -e .[dev-noxfile]; nox --install-only -e ${{ matrix.nox-session }}; pip freeze; nox -R -e ${{ matrix.nox-session }}"
timeout-minutes: 30

# This ensures that the runner has access to the pip cache.
Expand Down
40 changes: 24 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,30 @@ It provides two channels for clients:
## Example Usage

```python
async def run():
# dispatch helper sends out dispatches when they are due
dispatch_arrived = dispatch_helper.new_dispatches().new_receiver()
dispatch_ready = dispatch_helper.ready_dispatches().new_receiver()

async for selected in select(dispatch_ready, dispatch_arrived):
if selected_from(selected, dispatch_arrived):
dispatch = selected.value
match dispatch.type:
case DISPATCH_TYPE_BATTERY_CHARGE:
battery_pool = microgrid.battery_pool(dispatch.battery_set, task_id)
battery_pool.set_charge(dispatch.power)
...
if selected_from(selected, dispatch_ready):
dispatch = selected.value
log.info("New dispatch arrived %s", dispatch)
async def run():
# dispatch helper sends out dispatches when they are due
dispatch_arrived = dispatch_helper.updated_dispatches().new_receiver()
dispatch_ready = dispatch_helper.ready_dispatches().new_receiver()

async for selected in select(dispatch_ready, dispatch_arrived):
if selected_from(selected, dispatch_ready):
dispatch = selected.value
match dispatch.type:
case DISPATCH_TYPE_BATTERY_CHARGE:
battery_pool = microgrid.battery_pool(dispatch.battery_set, task_id)
battery_pool.set_charge(dispatch.power)
...
if selected_from(selected, dispatch_arrived):
match selected.value:
case Created(dispatch):
log.info("New dispatch arrived %s", dispatch)
...
case Updated(dispatch):
log.info("Dispatch updated %s", dispatch)
...
case Deleted(dispatch):
log.info("Dispatch deleted %s", dispatch)
...
```

## Supported Platforms
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ plugins:
# See https://mkdocstrings.github.io/python/usage/#import for details
- https://docs.python.org/3/objects.inv
- https://frequenz-floss.github.io/frequenz-channels-python/v0.16/objects.inv
- https://frequenz-floss.github.io/frequenz-sdk-python/v0.25/objects.inv
- https://frequenz-floss.github.io/frequenz-sdk-python/v1.0-pre/objects.inv
- https://typing-extensions.readthedocs.io/en/stable/objects.inv
- https://frequenz-floss.github.io/frequenz-client-dispatch-python/v0.1/objects.inv
# Note this plugin must be loaded after mkdocstrings to be able to use macros
Expand Down
26 changes: 19 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ description = "A highlevel interface for the dispatch API"
readme = "README.md"
license = { text = "MIT" }
keywords = ["frequenz", "python", "actor", "frequenz-dispatch", "dispatch", "highlevel", "api"]
# TODO(cookiecutter): Remove and add more classifiers if appropriate
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
Expand All @@ -26,21 +25,27 @@ classifiers = [
"Typing :: Typed",
]
requires-python = ">= 3.11, < 4"
# TODO(cookiecutter): Remove and add more dependencies if appropriate
dependencies = [
"python-dateutil >= 2.8.2, < 3.0",
"typing-extensions == 4.10.0",
# Make sure to update the version for cross-referencing also in the
# mkdocs.yml file when changing the version here (look for the config key
# plugins.mkdocstrings.handlers.python.import)
"frequenz-sdk == 0.25.2",
"frequenz-sdk == v1.0.0-rc5",
"frequenz-channels == 1.0.0b2",
"frequenz-dispatch-client @ git+https://github.com/frequenz-floss/frequenz-client-dispatch-python.git@00dcb3c",
llucax marked this conversation as resolved.
Show resolved Hide resolved
"frequenz-api-dispatch @ git+https://github.com/frequenz-floss/frequenz-api-dispatch.git@223315c",
# "frequenz-api-dispatch >= 0.13.0, < 0.14",
"frequenz-client-base >= 0.2.1, < 0.3.0",
# Directly use unreleased commit until the first release
"frequenz-client-common @ git+https://github.com/frequenz-floss/frequenz-client-common-python.git@5e6e7b7",
llucax marked this conversation as resolved.
Show resolved Hide resolved
]
dynamic = ["version"]

[[project.authors]]
name = "Frequenz Energy-as-a-Service GmbH"
email = "[email protected]"

# TODO(cookiecutter): Remove and add more optional dependencies if appropriate
[project.optional-dependencies]
dev-flake8 = [
"flake8 == 7.0.0",
Expand All @@ -64,11 +69,13 @@ dev-mkdocs = [
dev-mypy = [
"mypy == 1.8.0",
"types-Markdown == 3.5.0.20240129",
"types-python-dateutil==2.8.19.20240311",
# For checking the noxfile, docs/ script, and tests
"frequenz-dispatch[dev-mkdocs,dev-noxfile,dev-pytest]",
]
dev-noxfile = [
"nox == 2023.4.22",
"uv == 0.1.14",
llucax marked this conversation as resolved.
Show resolved Hide resolved
"nox == 2024.3.02",
"frequenz-repo-config[lib] == 0.9.1",
]
dev-pylint = [
Expand All @@ -82,6 +89,7 @@ dev-pytest = [
"pytest-mock == 3.12.0",
"pytest-asyncio == 0.23.5",
"async-solipsism == 0.5",
"time-machine == 2.14.0",
]
dev = [
"frequenz-dispatch[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]",
Expand Down Expand Up @@ -159,11 +167,15 @@ namespace_packages = true
# used but getting the original ignored error when removing the type: ignore.
# See for example: https://github.com/python/mypy/issues/2960
#no_incremental = true
packages = ["frequenz.actor.dispatch"]
packages = ["frequenz.dispatch"]
strict = true

[[tool.mypy.overrides]]
module = ["mkdocs_macros.*"]
module = [
"mkdocs_macros.*",
"async_solipsism",
"async_solipsism.*",
]
ignore_missing_imports = true

[tool.setuptools_scm]
Expand Down
25 changes: 0 additions & 25 deletions src/frequenz/actor/dispatch/__init__.py

This file was deleted.

78 changes: 78 additions & 0 deletions src/frequenz/dispatch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# License: MIT
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH

"""A highlevel interface for the dispatch API."""

import grpc.aio
from frequenz.channels import Broadcast, Receiver
from frequenz.client.dispatch.types import Dispatch

from frequenz.dispatch.actor import DispatchActor, DispatchEvent

__all__ = ["Dispatcher"]
llucax marked this conversation as resolved.
Show resolved Hide resolved


class Dispatcher:
llucax marked this conversation as resolved.
Show resolved Hide resolved
"""A highlevel interface for the dispatch API.

This class provides a highlevel interface to the dispatch API.
It provides two channels:

One that sends a dispatch event message whenever a dispatch is created, updated or deleted.

The other sends a dispatch message whenever a dispatch is ready to be
executed according to the schedule.

allows to receive new dispatches and ready dispatches.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

This class provides a highlevel interface to the dispatch API. It 
allows for receiving of new dispatches, and dispatches labelled as 'ready'.

I would also suggest adding a small blurb about the difference between the two.


Example:
```python
from frequenz.dispatch import Dispatcher

async def run():
dispatcher = Dispatcher(API_CONNECTION_INFO)
dispatcher.start() # this will start the actor
dispatch_arrived = dispatcher.new_dispatches().new_receiver()
dispatch_ready = dispatcher.ready_dispatches().new_receiver()
```
"""

def __init__(
self, microgrid_id: int, grpc_channel: grpc.aio.Channel, svc_addr: str
):
"""Initialize the dispatcher.

Args:
microgrid_id: The microgrid id.
grpc_channel: The gRPC channel.
svc_addr: The service address.
"""
self._ready_channel = Broadcast[Dispatch]("ready_dispatches")
self._updated_channel = Broadcast[DispatchEvent]("new_dispatches")
self._actor = DispatchActor(
microgrid_id,
grpc_channel,
svc_addr,
self._updated_channel.new_sender(),
self._ready_channel.new_sender(),
)

async def start(self) -> None:
"""Start the actor."""
self._actor.start()

def updated_dispatches(self) -> Receiver[DispatchEvent]:
llucax marked this conversation as resolved.
Show resolved Hide resolved
"""Return new, updated or deleted dispatches receiver.

Returns:
A new receiver for new dispatches.
"""
return self._updated_channel.new_receiver()
llucax marked this conversation as resolved.
Show resolved Hide resolved

def ready_dispatches(self) -> Receiver[Dispatch]:
llucax marked this conversation as resolved.
Show resolved Hide resolved
"""Return ready dispatches receiver.

Returns:
A new receiver for ready dispatches.
"""
return self._ready_channel.new_receiver()
Loading
Loading