Share one event loop per test module to stop Windows socketpair churn (#3070)
This commit is contained in:
parent
d287c9868f
commit
867bba6263
1 changed files with 60 additions and 3 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -18,11 +18,35 @@ from logfire.testing import CaptureLogfire # noqa: E402
|
|||
import mcp.shared._otel # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anyio_backend():
|
||||
@pytest.fixture(scope="session")
|
||||
def anyio_backend() -> str:
|
||||
return "asyncio"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
async def _module_runner_lease(anyio_backend: str) -> AsyncIterator[None]:
|
||||
"""Share one event loop across each module's tests instead of one per test.
|
||||
|
||||
anyio's pytest plugin tears its runner down whenever the last lease is
|
||||
released, so with only function-scoped async fixtures every async test
|
||||
creates and destroys its own event loop. On Windows each loop's self-pipe
|
||||
is an emulated loopback-TCP socketpair, and churning thousands of those per
|
||||
run can transiently exhaust kernel socket buffers — surfacing in CI as
|
||||
`OSError: [WinError 10055]` raised from `asyncio.new_event_loop()` before
|
||||
an arbitrary test's body even starts. Holding a module-scoped lease caps
|
||||
the churn at one loop per module per xdist worker.
|
||||
|
||||
Modules that parametrize `anyio_backend` or call `trio.run(...)` directly
|
||||
must shadow this fixture with a sync no-op: a module-scoped lease cannot
|
||||
depend on the function-scoped parameter (pytest raises ScopeMismatch at
|
||||
setup), and the lease's live asyncio loop lingers over direct trio runs,
|
||||
whose signal handling collides with the loop's wakeup fd on Windows. The
|
||||
lease also makes sniffio report asyncio to the module's sync tests, so a
|
||||
sync test must not call `anyio.run()` itself.
|
||||
"""
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="capfire")
|
||||
def _capfire_isolated(capfire: CaptureLogfire) -> Iterator[CaptureLogfire]:
|
||||
"""Override of logfire's `capfire` that scopes the MCP tracer to the test.
|
||||
|
|
|
|||
Loading…
Reference in a new issue