diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 09460d26..f66e822c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,12 +2,6 @@ ## Summary - - -## Upgrading - - - ## New Features * `dispatch-cli` supports now the parameter `--type` and `--running` to filter the list of running services by type and status, respectively. @@ -15,6 +9,4 @@ * `default_timeout: timedelta` (default: 60 seconds) * `stream_timeout: timedelta` (default: 5 minutes) -## Bug Fixes - - +* `frequenz.client.dispatch.TargetComponents` is now public, and its type has changed from `list[int] | list[ComponentCategory]` to `list[ComponentId] | list[ComponentCategory]`. This change introduces a new dependency on `frequenz-client-microgrid` (>= v0.7.0, < 0.8.0) for the `frequenz.client.microgrid.ComponentId` type. diff --git a/pyproject.toml b/pyproject.toml index 96323ef3..81ec1c7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ dependencies = [ "frequenz-api-dispatch == 1.0.0-rc1", "frequenz-client-base >= 0.8.0, < 0.10.0", "frequenz-client-common >= 0.1.0, < 0.4.0", + "frequenz-client-microgrid >= 0.7.0, < 0.8.0", "grpcio >= 1.66.1, < 2", "python-dateutil >= 2.8.2, < 3.0", ] diff --git a/src/frequenz/client/dispatch/__init__.py b/src/frequenz/client/dispatch/__init__.py index 62fd807e..b17e1c44 100644 --- a/src/frequenz/client/dispatch/__init__.py +++ b/src/frequenz/client/dispatch/__init__.py @@ -4,5 +4,6 @@ """Dispatch API client for Python.""" from ._client import DispatchApiClient +from .types import TargetComponents -__all__ = ["DispatchApiClient"] +__all__ = ["DispatchApiClient", "TargetComponents"] diff --git a/src/frequenz/client/dispatch/_cli_types.py b/src/frequenz/client/dispatch/_cli_types.py index 40a899ee..1a0c9f71 100644 --- a/src/frequenz/client/dispatch/_cli_types.py +++ b/src/frequenz/client/dispatch/_cli_types.py @@ -12,6 +12,7 @@ from tzlocal import get_localzone from frequenz.client.common.microgrid.components import ComponentCategory +from frequenz.client.microgrid import ComponentId # Disable a false positive from pylint # pylint: disable=inconsistent-return-statements @@ -140,7 +141,7 @@ class TargetComponentParamType(click.ParamType): def convert( self, value: Any, param: click.Parameter | None, ctx: click.Context | None - ) -> list[ComponentCategory] | list[int]: + ) -> list[ComponentCategory] | list[ComponentId]: """Convert the input value into a list of ComponentCategory or IDs. Args: @@ -152,6 +153,8 @@ def convert( A list of component ids or component categories. """ if isinstance(value, list): # Already a list + if all(isinstance(item, int) for item in value): + return list(map(ComponentId, value)) return value values = value.split(",") @@ -162,7 +165,7 @@ def convert( error: Exception | None = None # Attempt to parse component ids try: - return [int(id) for id in values] + return [ComponentId(int(id)) for id in values] except ValueError as e: error = e diff --git a/src/frequenz/client/dispatch/test/generator.py b/src/frequenz/client/dispatch/test/generator.py index c00ab843..c4070f31 100644 --- a/src/frequenz/client/dispatch/test/generator.py +++ b/src/frequenz/client/dispatch/test/generator.py @@ -7,6 +7,7 @@ from datetime import datetime, timedelta, timezone from frequenz.client.common.microgrid.components import ComponentCategory +from frequenz.client.microgrid import ComponentId from .._internal_types import rounded_start_time from ..recurrence import EndCriteria, Frequency, RecurrenceRule, Weekday @@ -99,7 +100,7 @@ def generate_dispatch(self) -> Dispatch: for _ in range(self._rng.randint(1, 10)) ], [ - self._rng.randint(1, 100) + ComponentId(self._rng.randint(1, 100)) for _ in range(self._rng.randint(1, 10)) ], ] diff --git a/src/frequenz/client/dispatch/types.py b/src/frequenz/client/dispatch/types.py index 1f8cc6bf..fd6ad22d 100644 --- a/src/frequenz/client/dispatch/types.py +++ b/src/frequenz/client/dispatch/types.py @@ -24,13 +24,15 @@ # pylint: enable=no-name-in-module from frequenz.client.common.microgrid.components import ComponentCategory +from frequenz.client.microgrid import ComponentId from .recurrence import Frequency, RecurrenceRule, Weekday -TargetComponents = list[int] | list[ComponentCategory] +TargetComponents = list[ComponentId] | list[ComponentCategory] """One or more target components specifying which components a dispatch targets. -It can be a list of component IDs or a list of categories. +It can be a list of `ComponentId` instances (representing component IDs) +or a list of `ComponentCategory` instances (representing categories). """ @@ -50,7 +52,9 @@ def _target_components_from_protobuf( """ match pb_target.WhichOneof("components"): case "component_ids": - id_list: list[int] = list(pb_target.component_ids.ids) + id_list: list[ComponentId] = list( + map(ComponentId, pb_target.component_ids.ids) + ) return id_list case "component_categories": category_list: list[ComponentCategory] = list( @@ -80,8 +84,11 @@ def _target_components_to_protobuf( """ pb_target = PBTargetComponents() match target: - case list(component_ids) if all(isinstance(id, int) for id in component_ids): - pb_target.component_ids.ids.extend(cast(list[int], component_ids)) + case list(component_ids) if all( + isinstance(id, ComponentId) for id in component_ids + ): + # pylint: disable-next=unnecessary-lambda + pb_target.component_ids.ids.extend(map(lambda x: int(x), component_ids)) case list(categories) if all( isinstance(cat, ComponentCategory) for cat in categories ): diff --git a/tests/test_cli.py b/tests/test_cli.py index 6be30d08..b234a376 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -22,6 +22,7 @@ ) from frequenz.client.dispatch.test.client import ALL_KEY, FakeClient from frequenz.client.dispatch.types import Dispatch +from frequenz.client.microgrid import ComponentId TEST_NOW = datetime(2023, 1, 1, 0, 0, 0, tzinfo=timezone.utc) """Arbitrary time used as NOW for testing.""" @@ -68,7 +69,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -91,7 +92,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -113,7 +114,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -128,7 +129,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -156,7 +157,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -169,7 +170,7 @@ def mock_client(fake_client: FakeClient) -> Generator[None, None, None]: type="filtered", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=1800), - target=[3], + target=list(map(ComponentId, [3])), active=True, dry_run=False, payload={}, @@ -247,7 +248,7 @@ async def test_list_command( "test", timedelta(hours=2), timedelta(seconds=3600), - [1, 2, 3], + list(map(ComponentId, [1, 2, 3])), {"dry_run": True}, RecurrenceRule(), 0, @@ -376,7 +377,7 @@ async def test_create_command( expected_type: str, expected_start_time_delta: timedelta | Literal["NOW"], expected_duration: timedelta, - expected_target: list[int] | list[ComponentCategory], + expected_target: list[ComponentId] | list[ComponentCategory], expected_options: dict[str, Any], expected_reccurence: RecurrenceRule | None, expected_return_code: int, @@ -528,7 +529,7 @@ async def test_create_command( type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[500, 501], + target=list(map(ComponentId, [500, 501])), active=True, dry_run=False, payload={}, @@ -558,7 +559,7 @@ async def test_create_command( '{"key": "value"}', ], { - "target": [400, 401], + "target": list(map(ComponentId, [400, 401])), "recurrence": RecurrenceRule( frequency=Frequency.DAILY, interval=5, @@ -574,7 +575,7 @@ async def test_create_command( "payload": {"key": "value"}, }, 0, - """ target=[400, 401], + """ target=[ComponentId(400), ComponentId(401)], active=True, dry_run=False, payload={'key': 'value'}, @@ -633,7 +634,7 @@ async def test_update_command( type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, @@ -680,7 +681,7 @@ async def test_get_command( type="test", start_time=datetime(2023, 1, 1, 0, 0, 0), duration=timedelta(seconds=3600), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={}, diff --git a/tests/test_proto.py b/tests/test_proto.py index 1a4f1e8c..6dfbc451 100644 --- a/tests/test_proto.py +++ b/tests/test_proto.py @@ -18,13 +18,14 @@ _target_components_from_protobuf, _target_components_to_protobuf, ) +from frequenz.client.microgrid import ComponentId def test_target_components() -> None: """Test the target components.""" for components in ( - [1, 2, 3], - [10, 20, 30], + list(map(ComponentId, [1, 2, 3])), + list(map(ComponentId, [10, 20, 30])), [ComponentCategory.BATTERY], [ComponentCategory.GRID], [ComponentCategory.METER], @@ -99,7 +100,7 @@ def test_dispatch() -> None: start_time=datetime(2024, 10, 10, tzinfo=timezone.utc), end_time=datetime(2024, 10, 20, tzinfo=timezone.utc), duration=timedelta(days=10), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={"key": "value"}, @@ -163,7 +164,7 @@ def test_dispatch_create_request_with_no_recurrence() -> None: type="test", start_time=datetime(2024, 10, 10, tzinfo=timezone.utc), duration=timedelta(days=10), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={"key": "value"}, @@ -180,7 +181,7 @@ def test_dispatch_create_start_immediately() -> None: type="test", start_time="NOW", duration=timedelta(days=10), - target=[1, 2, 3], + target=list(map(ComponentId, [1, 2, 3])), active=True, dry_run=False, payload={"key": "value"},