From 6baffdeb54213e4f683c852ef42684b09debc1f5 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Tue, 9 Dec 2025 13:43:38 +0100 Subject: [PATCH] fix: resolve test hang with websockets 14+ The test suite hangs after upgrading websockets from 13.x to 14.x/15.x because websockets 14.0 switched to a new asyncio implementation by default. Changes: 1. In TestServer.serve(): Replace deprecated asyncio.get_event_loop() pattern with asyncio.create_task() which is the modern way to create tasks when already inside an async context. 2. In serve_in_thread(): Instead of relying on server.run() which uses asyncio.run(), explicitly create a new event loop in the background thread with asyncio.new_event_loop() and asyncio.set_event_loop(). 3. Remove loop="asyncio" from Config() since we now manage the event loop manually. This approach is compatible with uvicorn 0.36+ (which removed the deprecated setup_event_loop() method) and works with websockets 14+. Fixes #3708 --- tests/conftest.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 858bca1397..54d62af60d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -235,10 +235,9 @@ def install_signal_handlers(self) -> None: async def serve(self, sockets=None): self.restart_requested = asyncio.Event() - loop = asyncio.get_event_loop() tasks = { - loop.create_task(super().serve(sockets=sockets)), - loop.create_task(self.watch_restarts()), + asyncio.create_task(super().serve(sockets=sockets)), + asyncio.create_task(self.watch_restarts()), } await asyncio.wait(tasks) @@ -269,7 +268,15 @@ async def watch_restarts(self) -> None: # pragma: no cover def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: - thread = threading.Thread(target=server.run) + def run_server(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + loop.run_until_complete(server.serve()) + finally: + loop.close() + + thread = threading.Thread(target=run_server) thread.start() try: while not server.started: @@ -282,6 +289,6 @@ def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: @pytest.fixture(scope="session") def server() -> typing.Iterator[TestServer]: - config = Config(app=app, lifespan="off", loop="asyncio") + config = Config(app=app, lifespan="off") server = TestServer(config=config) yield from serve_in_thread(server)