Give the test loop factory a fallback for fixture-only runners

pytest-asyncio only parametrizes the loop factory for async tests, so a
synchronous test pulling in an async fixture built that fixture's runner
with a stock loop, losing the executor and the loop.time binding that the
policy used to supply everywhere.

Overriding _asyncio_loop_factory keeps the parametrized value when there
is one and falls back to create_event_loop otherwise. The added tests
cover both the synchronous and the async path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PRmnu5HiXQUaHcHnNCvBSU
This commit is contained in:
Claude
2026-08-22 21:07:24 +00:00
parent 4a3c17e4aa
commit 1508cc69fe
2 changed files with 30 additions and 0 deletions
+12
View File
@@ -38,6 +38,7 @@ import freezegun
import multidict
import pytest
import pytest_asyncio
from pytest_asyncio.plugin import LoopFactory
import pytest_socket
import requests_mock
import respx
@@ -158,6 +159,17 @@ def pytest_asyncio_loop_factories() -> dict[str, Callable[[], AbstractEventLoop]
return {"homeassistant": runner.create_event_loop}
@pytest.fixture(scope="session", autouse=True)
def _asyncio_loop_factory(request: pytest.FixtureRequest) -> LoopFactory:
"""Cover the runners pytest_asyncio_loop_factories does not reach.
pytest-asyncio only parametrizes the factory for async tests, so a
synchronous test pulling in an async fixture would otherwise build that
fixture's runner with a stock loop.
"""
return getattr(request, "param", None) or runner.create_event_loop
# Capture the real socket functions before any test patches them
_real_getaddrinfo = socket.getaddrinfo
+18
View File
@@ -434,3 +434,21 @@ def test_ensure_single_execution_sequential_runs(tmp_path: Path) -> None:
# Lock file should still exist after second run (not unlinked)
assert lock_file_path.exists()
def _assert_hass_event_loop(loop: asyncio.AbstractEventLoop) -> None:
"""Assert the loop carries the Home Assistant customizations."""
assert loop.time is runner.monotonic
assert isinstance(loop._default_executor, executor.InterruptibleThreadPoolExecutor)
def test_sync_test_gets_hass_event_loop(hass: HomeAssistant) -> None:
"""Test a synchronous test builds the async hass fixture on our loop."""
# pytest-asyncio only parametrizes the loop factory for async tests
_assert_hass_event_loop(hass.loop)
async def test_async_test_gets_hass_event_loop(hass: HomeAssistant) -> None:
"""Test an async test runs on our loop."""
_assert_hass_event_loop(asyncio.get_running_loop())
_assert_hass_event_loop(hass.loop)