Pre-import more frontend deps to avoid importing when the event loop is running (#112031)

This commit is contained in:
J. Nick Koston 2024-03-02 13:44:06 -10:00 committed by GitHub
parent 9e428c6c5f
commit 08897137ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 3 deletions

View file

@ -22,16 +22,25 @@ import yarl
from . import config as conf_util, config_entries, core, loader, requirements
# Pre-import config and lovelace which have no requirements here to avoid
# Pre-import frontend deps which have no requirements here to avoid
# loading them at run time and blocking the event loop. We do this ahead
# of time so that we do not have to flag frontends deps with `import_executor`
# of time so that we do not have to flag frontend deps with `import_executor`
# as it would create a thundering heard of executor jobs trying to import
# frontend deps at the same time.
from .components import (
api as api_pre_import, # noqa: F401
auth as auth_pre_import, # noqa: F401
config as config_pre_import, # noqa: F401
device_automation as device_automation_pre_import, # noqa: F401
diagnostics as diagnostics_pre_import, # noqa: F401
file_upload as file_upload_pre_import, # noqa: F401
http,
lovelace as lovelace_pre_import, # noqa: F401
onboarding as onboarding_pre_import, # noqa: F401
repairs as repairs_pre_import, # noqa: F401
search as search_pre_import, # noqa: F401
system_log as system_log_pre_import, # noqa: F401
websocket_api as websocket_api_pre_import, # noqa: F401
)
from .const import (
FORMAT_DATETIME,

View file

@ -3,12 +3,13 @@ import asyncio
from collections.abc import Generator, Iterable
import glob
import os
import sys
from typing import Any
from unittest.mock import AsyncMock, Mock, patch
import pytest
from homeassistant import bootstrap, runner
from homeassistant import bootstrap, loader, runner
import homeassistant.config as config_util
from homeassistant.config_entries import HANDLERS, ConfigEntry
from homeassistant.const import SIGNAL_BOOTSTRAP_INTEGRATIONS
@ -1023,3 +1024,46 @@ async def test_bootstrap_dependencies(
f"Dependency {integration} will wait for dependencies dict_keys(['mqtt'])"
in caplog.text
)
async def test_frontend_deps_pre_import_no_requirements(hass: HomeAssistant) -> None:
"""Test frontend dependencies are pre-imported and do not have any requirements."""
pre_imports = [
name.removesuffix("_pre_import")
for name in dir(bootstrap)
if name.endswith("_pre_import")
]
# Make sure future refactoring does not
# accidentally remove the pre-imports
# or change the naming convention without
# updating this test.
assert len(pre_imports) > 3
for pre_import in pre_imports:
integration = await loader.async_get_integration(hass, pre_import)
assert not integration.requirements
async def test_bootstrap_does_not_preload_stage_1_integrations() -> None:
"""Test that the bootstrap does not preload stage 1 integrations.
If this test fails it means that stage1 integrations are being
loaded too soon and will not get their requirements updated
before they are loaded at runtime.
"""
process = await asyncio.create_subprocess_exec(
sys.executable,
"-c",
"import homeassistant.bootstrap; import sys; print(sys.modules)",
stdout=asyncio.subprocess.PIPE,
)
stdout, _ = await process.communicate()
assert process.returncode == 0
decoded_stdout = stdout.decode()
# Ensure no stage1 integrations have been imported
# as a side effect of importing the pre-imports
for integration in bootstrap.STAGE_1_INTEGRATIONS:
assert f"homeassistant.components.{integration}" not in decoded_stdout