Load translations at setup time if they were not loaded at bootstrap (#110921)

This commit is contained in:
J. Nick Koston 2024-02-24 11:31:25 -10:00 committed by GitHub
parent 076ae22fdd
commit dd80157dc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 40 additions and 49 deletions

View File

@ -5,10 +5,9 @@ import asyncio
from collections.abc import Iterable, Mapping
import logging
import string
from typing import TYPE_CHECKING, Any
from typing import Any
from homeassistant.const import (
EVENT_COMPONENT_LOADED,
EVENT_CORE_CONFIG_UPDATE,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
@ -498,35 +497,6 @@ def async_setup(hass: HomeAssistant) -> None:
_LOGGER.debug("Loading translations for language: %s", new_language)
await cache.async_load(new_language, hass.config.components)
@callback
def _async_load_translations_for_component_filter(event: Event) -> bool:
"""Filter out unwanted events."""
component: str | None = event.data.get("component")
# Platforms don't have their own translations, skip them
return bool(
component
and "." not in component
and not cache.async_is_loaded(hass.config.language, {component})
)
async def _async_load_translations_for_component(event: Event) -> None:
"""Load translations for a component."""
component: str | None = event.data.get("component")
if TYPE_CHECKING:
assert component is not None
language = hass.config.language
_LOGGER.debug(
"Loading translations for language: %s and component: %s",
language,
component,
)
await cache.async_load(language, {component})
hass.bus.async_listen(
EVENT_COMPONENT_LOADED,
_async_load_translations_for_component,
event_filter=_async_load_translations_for_component_filter,
)
hass.bus.async_listen(
EVENT_CORE_CONFIG_UPDATE,
_async_load_translations,
@ -541,6 +511,14 @@ async def async_load_integrations(hass: HomeAssistant, integrations: set[str]) -
)
@callback
def async_translations_loaded(hass: HomeAssistant, components: set[str]) -> bool:
"""Return if the given components are loaded for the language."""
return _async_get_translations_cache(hass).async_is_loaded(
hass.config.language, components
)
@callback
def async_translate_state(
hass: HomeAssistant,

View File

@ -25,6 +25,7 @@ from .core import (
callback,
)
from .exceptions import DependencyError, HomeAssistantError
from .helpers import translation
from .helpers.issue_registry import IssueSeverity, async_create_issue
from .helpers.typing import ConfigType, EventType
from .util import ensure_unique_string
@ -244,7 +245,7 @@ async def _async_process_dependencies(
return failed
async def _async_setup_component(
async def _async_setup_component( # noqa: C901
hass: core.HomeAssistant, domain: str, config: ConfigType
) -> bool:
"""Set up a component for Home Assistant.
@ -343,7 +344,19 @@ async def _async_setup_component(
start = timer()
_LOGGER.info("Setting up %s", domain)
with async_start_setup(hass, [domain]):
integration_set = {domain}
load_translations_task: asyncio.Task[None] | None = None
if not translation.async_translations_loaded(hass, integration_set):
# For most cases we expect the translations are already
# loaded since we try to load them in bootstrap ahead of time.
# If for some reason the background task in bootstrap was too slow
# or the integration was added after bootstrap, we will load them here.
load_translations_task = asyncio.create_task(
translation.async_load_integrations(hass, integration_set)
)
with async_start_setup(hass, integration_set):
if hasattr(component, "PLATFORM_SCHEMA"):
# Entity components have their own warning
warn_task = None
@ -409,6 +422,8 @@ async def _async_setup_component(
await asyncio.sleep(0)
await hass.config_entries.flow.async_wait_import_flow_initialized(domain)
if load_translations_task:
await load_translations_task
# Add to components before the entry.async_setup
# call to avoid a deadlock when forwarding platforms
hass.config.components.add(domain)

View File

@ -8,7 +8,7 @@ from unittest.mock import Mock, call, patch
import pytest
from homeassistant import loader
from homeassistant.const import EVENT_COMPONENT_LOADED, EVENT_CORE_CONFIG_UPDATE
from homeassistant.const import EVENT_CORE_CONFIG_UPDATE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import translation
from homeassistant.loader import async_get_integration
@ -605,20 +605,6 @@ async def test_setup(hass: HomeAssistant):
"""Test the setup load listeners helper."""
translation.async_setup(hass)
with patch(
"homeassistant.helpers.translation._TranslationCache.async_load",
) as mock:
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "loaded_component"})
await hass.async_block_till_done()
mock.assert_called_once_with(hass.config.language, {"loaded_component"})
with patch(
"homeassistant.helpers.translation._TranslationCache.async_load",
) as mock:
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "config.component"})
await hass.async_block_till_done()
mock.assert_not_called()
# Should not be called if the language is the current language
with patch(
"homeassistant.helpers.translation._TranslationCache.async_load",

View File

@ -11,7 +11,7 @@ from homeassistant import config_entries, setup
from homeassistant.const import EVENT_COMPONENT_LOADED, EVENT_HOMEASSISTANT_START
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import discovery
from homeassistant.helpers import discovery, translation
from homeassistant.helpers.config_validation import (
PLATFORM_SCHEMA,
PLATFORM_SCHEMA_BASE,
@ -801,3 +801,15 @@ async def test_setup_config_entry_from_yaml(
caplog.clear()
hass.data.pop(setup.DATA_SETUP)
hass.config.components.remove("test_integration_only_entry")
async def test_loading_component_loads_translations(hass: HomeAssistant) -> None:
"""Test that loading a component loads translations."""
assert translation.async_translations_loaded(hass, {"comp"}) is False
mock_setup = Mock(return_value=True)
mock_integration(hass, MockModule("comp", setup=mock_setup))
assert await setup.async_setup_component(hass, "comp", {})
assert mock_setup.called
assert translation.async_translations_loaded(hass, {"comp"}) is True