From 9bc130c13151261cabe189ff2a99eafe8355c1af Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 17 Feb 2024 20:01:36 -0600 Subject: [PATCH] Ensure translations for other integrations can be loaded if one integration fails (#110748) * load failure * merge --- homeassistant/helpers/translation.py | 18 +++++++++------- tests/helpers/test_translation.py | 31 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 3334dc9a62f..d448ea0d4ce 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -114,7 +114,7 @@ def _merge_resources( # We are going to merge the translations for the custom device classes into # the translations of sensor. - new_value = translation_strings[component].get(category) + new_value = translation_strings.get(component, {}).get(category) if new_value is None: continue @@ -135,17 +135,17 @@ def _merge_resources( def build_resources( - translation_strings: dict[str, dict[str, Any]], + translation_strings: dict[str, dict[str, dict[str, Any] | str]], components: set[str], category: str, ) -> dict[str, dict[str, Any] | str]: """Build the resources response for the given components.""" # Build response return { - component: translation_strings[component][category] + component: category_strings for component in components - if category in translation_strings[component] - and translation_strings[component][category] is not None + if (component_strings := translation_strings.get(component)) + and (category_strings := component_strings.get(category)) } @@ -161,7 +161,8 @@ async def _async_get_component_strings( files_to_load = {} for loaded in components: domain = loaded.partition(".")[0] - integration = integrations[domain] + if not (integration := integrations.get(domain)): + continue path = component_translation_path(loaded, language, integration) # No translation available @@ -273,7 +274,10 @@ class _TranslationCache: ints_or_excs = await async_get_integrations(self.hass, domains) for domain, int_or_exc in ints_or_excs.items(): if isinstance(int_or_exc, Exception): - raise int_or_exc + _LOGGER.warning( + "Failed to load integration for translation: %s", int_or_exc + ) + continue integrations[domain] = int_or_exc for translation_strings in await asyncio.gather( diff --git a/tests/helpers/test_translation.py b/tests/helpers/test_translation.py index 22bb23f7130..961d0d91b7f 100644 --- a/tests/helpers/test_translation.py +++ b/tests/helpers/test_translation.py @@ -435,6 +435,37 @@ async def test_translation_merging_loaded_together( assert translations == hue_translations | homekit_translations +async def test_ensure_translations_still_load_if_one_integration_fails( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test that if one integration fails to load we can still get translations.""" + hass.config.components.add("sensor") + hass.config.components.add("broken") + + sensor_integration = await loader.async_get_integration(hass, "sensor") + + with patch( + "homeassistant.helpers.translation.async_get_integrations", + return_value={ + "sensor": sensor_integration, + "broken": Exception("unhandled failure"), + }, + ): + translations = await translation.async_get_translations( + hass, "en", "entity_component", integrations={"sensor", "broken"} + ) + assert "Failed to load integration for translation" in caplog.text + assert "broken" in caplog.text + + assert translations + + sensor_translations = await translation.async_get_translations( + hass, "en", "entity_component", integrations={"sensor"} + ) + + assert translations == sensor_translations + + async def test_caching(hass: HomeAssistant) -> None: """Test we cache data.""" hass.config.components.add("sensor")