From 65e2d866625d8834258d49d947db4700150f3c29 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 24 Feb 2026 15:22:07 +0200 Subject: [PATCH 1/2] Catch errors from TestServer (instead of hanging if `server.started` never becomes true) --- tests/conftest.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 858bca1397..d076c3ab5d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -269,11 +269,28 @@ async def watch_restarts(self) -> None: # pragma: no cover def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: - thread = threading.Thread(target=server.run) + server_exception = None + server_caught_exception = threading.Event() + + def _run_server() -> None: + nonlocal server_exception + try: + server.run() + except BaseException as exc: # pragma: nocover + # BaseException as we need to catch SystemExit too; + # `uvicorn` calls `sys.exit(1)` at failure. + server_exception = exc + server_caught_exception.set() + + thread = threading.Thread(target=_run_server) thread.start() + try: while not server.started: - time.sleep(1e-3) + if server_caught_exception.wait(1e-3): + raise RuntimeError( + f"Server failed to start: {server_exception!r}", + ) from server_exception yield server finally: server.should_exit = True From 8a959d830e8631f84401aeb5fd2e0ffa9bbfe3d7 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 24 Feb 2026 15:22:59 +0200 Subject: [PATCH 2/2] Do not wait forever for test server to start or stop --- tests/conftest.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index d076c3ab5d..132d1a15ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -268,7 +268,11 @@ async def watch_restarts(self) -> None: # pragma: no cover await self.startup() -def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: +def serve_in_thread( + server: TestServer, + *, + timeout: float = 10.0, +) -> typing.Iterator[TestServer]: server_exception = None server_caught_exception = threading.Event() @@ -286,15 +290,21 @@ def _run_server() -> None: thread.start() try: - while not server.started: - if server_caught_exception.wait(1e-3): + start_time = time.time() + while True: + if server.started: + break + if server_caught_exception.wait(1e-3): # pragma: nocover raise RuntimeError( f"Server failed to start: {server_exception!r}", ) from server_exception + if time.time() - start_time > timeout: # pragma: nocover + raise TimeoutError("Server did not start in time") + time.sleep(1e-3) yield server finally: server.should_exit = True - thread.join() + thread.join(timeout=timeout) @pytest.fixture(scope="session")