diff --git a/src/textual/app.py b/src/textual/app.py index 4f3ac64a59..deb932ec1c 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -1461,15 +1461,19 @@ async def run_app(app: App) -> None: Args: app: App to run. """ - if message_hook is not None: - message_hook_context_var.set(message_hook) - app._loop = asyncio.get_running_loop() - app._thread_id = threading.get_ident() - await app._process_messages( - ready_callback=on_app_ready, - headless=headless, - terminal_size=size, - ) + + try: + if message_hook is not None: + message_hook_context_var.set(message_hook) + app._loop = asyncio.get_running_loop() + app._thread_id = threading.get_ident() + await app._process_messages( + ready_callback=on_app_ready, + headless=headless, + terminal_size=size, + ) + finally: + app_ready_event.set() # Launch the app in the "background" active_message_pump.set(app) @@ -2383,7 +2387,7 @@ def _handle_exception(self, error: Exception) -> None: self._return_code = 1 # If we're running via pilot and this is the first exception encountered, # take note of it so that we can re-raise for test frameworks later. - if self.is_headless and self._exception is None: + if self._exception is None: self._exception = error self._exception_event.set() diff --git a/src/textual/pilot.py b/src/textual/pilot.py index 8226eb97ee..739a66591e 100644 --- a/src/textual/pilot.py +++ b/src/textual/pilot.py @@ -366,7 +366,11 @@ async def _wait_for_screen(self, timeout: float = 30.0) -> bool: Raises: WaitForScreenTimeout: If the screen and its children didn't finish processing within the timeout. """ - children = [self.app, *self.app.screen.walk_children(with_self=True)] + try: + screen = self.app.screen + except Exception: + return False + children = [self.app, *screen.walk_children(with_self=True)] count = 0 count_zero_event = asyncio.Event() diff --git a/tests/test_pilot.py b/tests/test_pilot.py index 9451237fab..52a759416f 100644 --- a/tests/test_pilot.py +++ b/tests/test_pilot.py @@ -6,6 +6,7 @@ from textual.app import App, ComposeResult from textual.binding import Binding from textual.containers import Center, Middle +from textual.css.errors import StylesheetError from textual.pilot import OutOfBounds from textual.screen import Screen from textual.widgets import Button, Label @@ -395,3 +396,14 @@ async def test_pilot_resize_terminal(): await pilot.pause() assert app.size == (27, 15) assert app.screen.size == (27, 15) + + +async def test_fail_early(): + # https://github.com/Textualize/textual/issues/3282 + class MyApp(App): + CSS_PATH = "foo.tcss" + + app = MyApp() + with pytest.raises(StylesheetError): + async with app.run_test() as pilot: + await pilot.press("enter")