Skip to content

Commit 93ba6bd

Browse files
committed
fix: only use manual SelectorEventLoop on Windows
The hand-constructed SelectorEventLoop path was incompatible with older uvicorn on Python 3.14 (lowest-direct CI matrix). On non-Windows, defer to uvicorn's own server.run() which handles loop setup portably. The selector-loop workaround is only needed on Windows where the default ProactorEventLoop leaves transports unclosed at thread teardown.
1 parent e1d5a6b commit 93ba6bd

File tree

1 file changed

+13
-8
lines changed

1 file changed

+13
-8
lines changed

tests/test_helpers.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@
1818

1919

2020
def _run_server_in_loop(server: uvicorn.Server) -> None:
21-
"""Thread target: run uvicorn on a fresh event loop, ensuring clean teardown.
21+
"""Thread target: run uvicorn with a clean-teardown event loop.
2222
2323
On Windows the default ProactorEventLoop can leave pipe transports alive
2424
when the loop is destroyed in a background thread — their ``__del__`` fires
2525
later and pytest's unraisable-exception hook attributes it to the next test.
26-
The selector loop shuts down cleanly, so we use it here instead of letting
27-
``server.run()`` call ``asyncio.run()`` with whatever the platform default is.
26+
The selector loop shuts down cleanly, so we use it there. On other platforms
27+
we defer to ``server.run()`` — uvicorn's own loop setup is more portable
28+
across versions than a hand-constructed loop.
2829
"""
29-
loop = asyncio.SelectorEventLoop()
30-
try:
31-
loop.run_until_complete(server.serve())
32-
finally:
33-
loop.close()
30+
if sys.platform == "win32": # pragma: no cover
31+
# Force selector loop without touching process-global policy
32+
loop = asyncio.SelectorEventLoop()
33+
try:
34+
loop.run_until_complete(server.serve())
35+
finally:
36+
loop.close()
37+
else:
38+
server.run()
3439

3540

3641
@contextmanager

0 commit comments

Comments
 (0)