diff --git a/src/asphalt/core/_component.py b/src/asphalt/core/_component.py index 73784992..c59daed1 100644 --- a/src/asphalt/core/_component.py +++ b/src/asphalt/core/_component.py @@ -49,13 +49,14 @@ class ContainerComponent(Component): :vartype component_configs: Dict[str, Optional[Dict[str, Any]]] """ - __slots__ = "child_components", "component_configs" + __slots__ = "child_components", "component_configs", "_started" def __init__( self, components: dict[str, dict[str, Any] | None] | None = None ) -> None: self.child_components: OrderedDict[str, Component] = OrderedDict() self.component_configs = components or {} + self._started = False def add_component( self, alias: str, type: str | type | None = None, **config: Any @@ -103,6 +104,8 @@ async def start(self) -> None: they have completed. """ + self._started = True + for alias in self.component_configs: if alias not in self.child_components: self.add_component(alias) diff --git a/src/asphalt/core/_runner.py b/src/asphalt/core/_runner.py index 646f4784..17737635 100644 --- a/src/asphalt/core/_runner.py +++ b/src/asphalt/core/_runner.py @@ -19,7 +19,7 @@ from anyio.abc import TaskStatus from exceptiongroup import catch -from ._component import Component, component_types +from ._component import Component, ContainerComponent, component_types from ._context import Context from ._exceptions import ApplicationExit @@ -53,6 +53,17 @@ def handle_application_exit(excgrp: ExceptionGroup) -> None: raise SystemExit(exit_exc.code).with_traceback(exc.__traceback__) from None +def check_started(component) -> ContainerComponent | None: + if isinstance(component, ContainerComponent): + for c in component.child_components: + unstarted_component = check_started(c) + if unstarted_component: + return unstarted_component + if not component._started: + return component + return None + + async def run_application( component: Component | dict[str, Any], *, @@ -136,6 +147,15 @@ async def run_application( logger.exception("Error during application startup") raise + unstarted_component = check_started(component) + if unstarted_component: + logger.error( + "Container component %s not started, " + "did you forget to await super().start() ?", + component, + ) + return + logger.info("Application started") await event.wait()