diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 99f21e273..cd03d5165 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -72,6 +72,8 @@ - The `actor.ChannelRegistry` is now type-aware. +- The error messages when `BackgroundService` implementations don't call `super().__init__()` have been improved. + ## Bug Fixes - 0W power requests are now not adjusted to exclusion bounds by the `PowerManager` and `PowerDistributor`, and are sent over to the microgrid API directly. diff --git a/src/frequenz/sdk/actor/_background_service.py b/src/frequenz/sdk/actor/_background_service.py index 64e55cb9b..4b970ebe0 100644 --- a/src/frequenz/sdk/actor/_background_service.py +++ b/src/frequenz/sdk/actor/_background_service.py @@ -90,6 +90,7 @@ def name(self) -> str: Returns: The name of this background service. """ + self._assert_initialized() return self._name @property @@ -107,6 +108,7 @@ def tasks(self) -> collections.abc.Set[asyncio.Task[Any]]: Returns: The set of running tasks spawned by this background service. """ + self._assert_initialized() return self._tasks @property @@ -118,6 +120,7 @@ def is_running(self) -> bool: Returns: Whether this background service is running. """ + self._assert_initialized() return any(not task.done() for task in self._tasks) def cancel(self, msg: str | None = None) -> None: @@ -126,6 +129,7 @@ def cancel(self, msg: str | None = None) -> None: Args: msg: The message to be passed to the tasks being cancelled. """ + self._assert_initialized() for task in self._tasks: task.cancel(msg) @@ -144,6 +148,7 @@ async def stop(self, msg: str | None = None) -> None: [//]: # (# noqa: DAR401 rest) """ + self._assert_initialized() if not self._tasks: return self.cancel(msg) @@ -166,6 +171,7 @@ async def __aenter__(self) -> Self: Returns: This background service. """ + self._assert_initialized() self.start() return self @@ -196,6 +202,7 @@ async def wait(self) -> None: exception (`CancelError` is not considered an error and not returned in the exception group). """ + self._assert_initialized() # We need to account for tasks that were created between when we started # awaiting and we finished awaiting. while self._tasks: @@ -251,3 +258,10 @@ def __str__(self) -> str: A string representation of this instance. """ return f"{type(self).__name__}[{self._name}]" + + def _assert_initialized(self) -> None: + """Assert this instance is initialized.""" + assert hasattr(self, "_tasks"), ( + f"{type(self).__name__} instance is not initialized. " + "Make sure to call `super().__init__()` in your __init__() method." + )