Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* The constructor parameter `channel_options` was renamed to `channels_defaults` to match the name used in `BaseApiClient`.
* The constructor now accepts a `connect` parameter, which is `True` by default. If set to `False`, the client will not connect to the server upon instantiation. You can connect later by calling the `connect()` method.

* The `frequenz-client-base` dependency was bumped to v0.8.0.

## New Features

- The client now inherits from `frequenz.client.base.BaseApiClient`, so it provides a few new features, like `disconnect()`ing or using it as a context manager. Please refer to the [`BaseApiClient` documentation](https://frequenz-floss.github.io/frequenz-client-base-python/latest/reference/frequenz/client/base/client/#frequenz.client.base.client.BaseApiClient) for more information on these features.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ requires-python = ">= 3.11, < 4"
dependencies = [
"frequenz-api-microgrid >= 0.15.3, < 0.16.0",
"frequenz-channels >= 1.0.0-rc1, < 2.0.0",
"frequenz-client-base >= 0.6.0, < 0.7",
"frequenz-client-base >= 0.8.0, < 0.9",
"grpcio >= 1.54.2, < 2",
"protobuf >= 4.21.6, < 6",
"timezonefinder >= 6.2.0, < 7",
Expand Down
31 changes: 23 additions & 8 deletions src/frequenz/client/microgrid/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

"""Client for requests to the Microgrid API."""

from __future__ import annotations

import asyncio
import logging
from collections.abc import Callable, Iterable, Set
Expand Down Expand Up @@ -31,7 +33,7 @@
)
from ._connection import Connection
from ._constants import RECEIVER_MAX_SIZE
from ._exception import ApiClientError
from ._exception import ApiClientError, ClientNotConnected
from ._metadata import Location, Metadata

DEFAULT_GRPC_CALL_TIMEOUT = 60.0
Expand Down Expand Up @@ -89,10 +91,20 @@ def __init__(
connect=connect,
channel_defaults=channel_defaults,
)
self._async_stub: microgrid_pb2_grpc.MicrogridAsyncStub = self.stub # type: ignore
self._broadcasters: dict[int, streaming.GrpcStreamBroadcaster[Any, Any]] = {}
self._retry_strategy = retry_strategy

@property
def stub(self) -> microgrid_pb2_grpc.MicrogridAsyncStub:
"""The gRPC stub for the API."""
if self.channel is None or self._stub is None:
raise ClientNotConnected(server_url=self.server_url, operation="stub")
# This type: ignore is needed because we need to cast the sync stub to
# the async stub, but we can't use cast because the async stub doesn't
# actually exists to the eyes of the interpreter, it only exists for the
# type-checker, so it can only be used for type hints.
return self._stub # type: ignore

async def components( # noqa: DOC502 (raises ApiClientError indirectly)
self,
) -> Iterable[Component]:
Expand All @@ -108,7 +120,7 @@ async def components( # noqa: DOC502 (raises ApiClientError indirectly)
"""
component_list = await client.call_stub_method(
self,
lambda: self._async_stub.ListComponents(
lambda: self.stub.ListComponents(
microgrid_pb2.ComponentFilter(),
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
),
Expand Down Expand Up @@ -145,7 +157,7 @@ async def metadata(self) -> Metadata:
try:
microgrid_metadata = await client.call_stub_method(
self,
lambda: self._async_stub.GetMicrogridMetadata(
lambda: self.stub.GetMicrogridMetadata(
Empty(),
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
),
Expand Down Expand Up @@ -192,7 +204,7 @@ async def connections( # noqa: DOC502 (raises ApiClientError indirectly)
self.components(),
client.call_stub_method(
self,
lambda: self._async_stub.ListConnections(
lambda: self.stub.ListConnections(
connection_filter,
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
),
Expand Down Expand Up @@ -249,12 +261,15 @@ async def _new_component_data_receiver(
broadcaster = streaming.GrpcStreamBroadcaster(
f"raw-component-data-{component_id}",
lambda: aiter(
self._async_stub.StreamComponentData(
self.stub.StreamComponentData(
microgrid_pb2.ComponentIdParam(id=component_id)
)
),
transform,
retry_strategy=self._retry_strategy,
# We don't expect any data stream to end, so if it is exhausted for any
# reason we want to keep retrying
retry_on_exhausted_stream=True,
)
self._broadcasters[component_id] = broadcaster
return broadcaster.new_receiver(maxsize=maxsize)
Expand Down Expand Up @@ -408,7 +423,7 @@ async def set_power( # noqa: DOC502 (raises ApiClientError indirectly)
"""
await client.call_stub_method(
self,
lambda: self._async_stub.SetPowerActive(
lambda: self.stub.SetPowerActive(
microgrid_pb2.SetPowerActiveParam(
component_id=component_id, power=power_w
),
Expand Down Expand Up @@ -447,7 +462,7 @@ async def set_bounds( # noqa: DOC503 (raises ApiClientError indirectly)
)
await client.call_stub_method(
self,
lambda: self._async_stub.AddInclusionBounds(
lambda: self.stub.AddInclusionBounds(
microgrid_pb2.SetBoundsParam(
component_id=component_id,
target_metric=target_metric,
Expand Down
1 change: 0 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def __init__(self, *, retry_strategy: retry.Strategy | None = None) -> None:
super().__init__("grpc://mock_host:1234", retry_strategy=retry_strategy)
self.mock_stub = mock_stub
self._stub = mock_stub # pylint: disable=protected-access
self._async_stub = mock_stub # pylint: disable=protected-access


async def test_components() -> None:
Expand Down
Loading