Skip to content

Commit 1300a1b

Browse files
committed
Deal with missing type info in microgrid API calls
grpc.aio doesn't have type information, and there are all sorts of tricks in it, so some funtions that look like they are not async really are (because they return a awaitable). For example [`unary_unary()`] returns a [`UnaryUnaryMultiCallable`] which returns a [`UnaryUnaryCall`] which is `awaitable`. Also there seem to be wrong type info regarding `SetBounds()`, saying `request_iterator` is a required argument but it is not. [`stream_unary()`] returns a [`StreamUnaryMultiCallable`] which takes an optional `request_iterator`. [`unary_unary()`]: https://github.com/grpc/grpc/blob/v1.51.3/src/python/grpcio/grpc/aio/_base_channel.py#L271-L276 [`UnaryUnaryMultiCallable`]: https://github.com/grpc/grpc/blob/v1.51.3/src/python/grpcio/grpc/aio/_base_channel.py#L28-L41 [`UnaryUnaryCall`]: https://github.com/grpc/grpc/blob/v1.51.3/src/python/grpcio/grpc/aio/_base_call.py#L138-L149 ['stream_unary()']: https://github.com/grpc/grpc/blob/v1.51.3/src/python/grpcio/grpc/aio/_base_channel.py#L313-L318 [`StreamUnaryMultiCallable`]: https://github.com/grpc/grpc/blob/v1.51.3/src/python/grpcio/grpc/aio/_base_channel.py#L106-L118 Signed-off-by: Leandro Lucarella <[email protected]>
1 parent e6bc6cd commit 1300a1b

File tree

1 file changed

+41
-11
lines changed

1 file changed

+41
-11
lines changed

src/frequenz/sdk/microgrid/client/_client.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66
import asyncio
77
import logging
88
from abc import ABC, abstractmethod
9-
from typing import Any, Callable, Dict, Iterable, Optional, Set, TypeVar
9+
from typing import (
10+
Any,
11+
Awaitable,
12+
Callable,
13+
Dict,
14+
Iterable,
15+
Optional,
16+
Set,
17+
TypeVar,
18+
cast,
19+
)
1020

1121
import grpc
1222
from frequenz.api.microgrid import common_pb2 as common_pb
@@ -210,10 +220,12 @@ async def components(self) -> Iterable[Component]:
210220
when the api call exceeded timeout
211221
"""
212222
try:
223+
# grpc.aio is missing types and mypy thinks this is not awaitable,
224+
# but it is
213225
component_list = await self.api.ListComponents(
214226
microgrid_pb.ComponentFilter(),
215-
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
216-
)
227+
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
228+
) # type: ignore[misc]
217229
except grpc.aio.AioRpcError as err:
218230
msg = f"Failed to list components. Microgrid API: {self.target}. Err: {err.details()}"
219231
raise grpc.aio.AioRpcError(
@@ -266,8 +278,14 @@ async def connections(
266278
try:
267279
valid_components, all_connections = await asyncio.gather(
268280
self.components(),
269-
self.api.ListConnections(
270-
connection_filter, timeout=int(DEFAULT_GRPC_CALL_TIMEOUT)
281+
# grpc.aio is missing types and mypy thinks this is not
282+
# awaitable, but it is
283+
cast(
284+
Awaitable[microgrid_pb.ConnectionList],
285+
self.api.ListConnections(
286+
connection_filter,
287+
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
288+
),
271289
),
272290
)
273291
except grpc.aio.AioRpcError as err:
@@ -321,7 +339,9 @@ async def _component_data_task(
321339
call = self.api.GetComponentData(
322340
microgrid_pb.ComponentIdParam(id=component_id),
323341
)
324-
async for msg in call:
342+
# grpc.aio is missing types and mypy thinks this is not
343+
# async iterable, but it is
344+
async for msg in call: # type: ignore[attr-defined]
325345
await sender.send(transform(msg))
326346
except grpc.aio.AioRpcError as err:
327347
api_details = f"Microgrid API: {self.target}."
@@ -550,20 +570,24 @@ async def set_power(self, component_id: int, power_w: int) -> Empty:
550570
"""
551571
try:
552572
if power_w >= 0:
573+
# grpc.aio is missing types and mypy thinks this is not
574+
# async iterable, but it is
553575
result: Empty = await self.api.Charge(
554576
microgrid_pb.PowerLevelParam(
555577
component_id=component_id, power_w=power_w
556578
),
557-
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
558-
)
579+
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
580+
) # type: ignore[misc]
559581
else:
582+
# grpc.aio is missing types and mypy thinks this is not
583+
# async iterable, but it is
560584
power_w *= -1
561585
result = await self.api.Discharge(
562586
microgrid_pb.PowerLevelParam(
563587
component_id=component_id, power_w=power_w
564588
),
565-
timeout=int(DEFAULT_GRPC_CALL_TIMEOUT),
566-
)
589+
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
590+
) # type: ignore[misc]
567591
except grpc.aio.AioRpcError as err:
568592
msg = f"Failed to set power. Microgrid API: {self.target}. Err: {err.details()}"
569593
raise grpc.aio.AioRpcError(
@@ -600,8 +624,14 @@ async def set_bounds(
600624
if lower > 0:
601625
raise ValueError(f"Lower bound {upper} must be less than or equal to 0.")
602626

603-
set_bounds_call = self.api.SetBounds(timeout=int(DEFAULT_GRPC_CALL_TIMEOUT))
627+
# grpc.aio is missing types and mypy thinks request_iterator is
628+
# a required argument, but it is not
629+
set_bounds_call = self.api.SetBounds(
630+
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
631+
) # type: ignore[call-arg]
604632
try:
633+
# grpc.aio is missing types and mypy thinks set_bounds_call can be Empty
634+
assert not isinstance(set_bounds_call, Empty)
605635
await set_bounds_call.write(
606636
microgrid_pb.SetBoundsParam(
607637
component_id=component_id,

0 commit comments

Comments
 (0)