Catch all exceptions on import component/platform (#64930)
parent
a24f79434f
commit
24546dfdf9
|
@ -539,18 +539,44 @@ class Integration:
|
||||||
|
|
||||||
def get_component(self) -> ModuleType:
|
def get_component(self) -> ModuleType:
|
||||||
"""Return the component."""
|
"""Return the component."""
|
||||||
cache = self.hass.data.setdefault(DATA_COMPONENTS, {})
|
cache: dict[str, ModuleType] = self.hass.data.setdefault(DATA_COMPONENTS, {})
|
||||||
if self.domain not in cache:
|
if self.domain in cache:
|
||||||
|
return cache[self.domain]
|
||||||
|
|
||||||
|
try:
|
||||||
cache[self.domain] = importlib.import_module(self.pkg_path)
|
cache[self.domain] = importlib.import_module(self.pkg_path)
|
||||||
return cache[self.domain] # type: ignore
|
except ImportError:
|
||||||
|
raise
|
||||||
|
except Exception as err:
|
||||||
|
_LOGGER.exception(
|
||||||
|
"Unexpected exception importing component %s", self.pkg_path
|
||||||
|
)
|
||||||
|
raise ImportError(f"Exception importing {self.pkg_path}") from err
|
||||||
|
|
||||||
|
return cache[self.domain]
|
||||||
|
|
||||||
def get_platform(self, platform_name: str) -> ModuleType:
|
def get_platform(self, platform_name: str) -> ModuleType:
|
||||||
"""Return a platform for an integration."""
|
"""Return a platform for an integration."""
|
||||||
cache = self.hass.data.setdefault(DATA_COMPONENTS, {})
|
cache: dict[str, ModuleType] = self.hass.data.setdefault(DATA_COMPONENTS, {})
|
||||||
full_name = f"{self.domain}.{platform_name}"
|
full_name = f"{self.domain}.{platform_name}"
|
||||||
if full_name not in cache:
|
if full_name in cache:
|
||||||
|
return cache[full_name]
|
||||||
|
|
||||||
|
try:
|
||||||
cache[full_name] = self._import_platform(platform_name)
|
cache[full_name] = self._import_platform(platform_name)
|
||||||
return cache[full_name] # type: ignore
|
except ImportError:
|
||||||
|
raise
|
||||||
|
except Exception as err:
|
||||||
|
_LOGGER.exception(
|
||||||
|
"Unexpected exception importing platform %s.%s",
|
||||||
|
self.pkg_path,
|
||||||
|
platform_name,
|
||||||
|
)
|
||||||
|
raise ImportError(
|
||||||
|
f"Exception importing {self.pkg_path}.{platform_name}"
|
||||||
|
) from err
|
||||||
|
|
||||||
|
return cache[full_name]
|
||||||
|
|
||||||
def _import_platform(self, platform_name: str) -> ModuleType:
|
def _import_platform(self, platform_name: str) -> ModuleType:
|
||||||
"""Import the platform."""
|
"""Import the platform."""
|
||||||
|
|
|
@ -181,9 +181,6 @@ async def _async_setup_component(
|
||||||
except ImportError as err:
|
except ImportError as err:
|
||||||
log_error(f"Unable to import component: {err}", integration.documentation)
|
log_error(f"Unable to import component: {err}", integration.documentation)
|
||||||
return False
|
return False
|
||||||
except Exception: # pylint: disable=broad-except
|
|
||||||
_LOGGER.exception("Setup failed for %s: unknown error", domain)
|
|
||||||
return False
|
|
||||||
|
|
||||||
processed_config = await conf_util.async_process_component_config(
|
processed_config = await conf_util.async_process_component_config(
|
||||||
hass, config, integration
|
hass, config, integration
|
||||||
|
|
|
@ -157,6 +157,21 @@ async def test_get_integration(hass):
|
||||||
assert hue_light == integration.get_platform("light")
|
assert hue_light == integration.get_platform("light")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_integration_exceptions(hass):
|
||||||
|
"""Test resolving integration."""
|
||||||
|
integration = await loader.async_get_integration(hass, "hue")
|
||||||
|
|
||||||
|
with pytest.raises(ImportError), patch(
|
||||||
|
"homeassistant.loader.importlib.import_module", side_effect=ValueError("Boom")
|
||||||
|
):
|
||||||
|
assert hue == integration.get_component()
|
||||||
|
|
||||||
|
with pytest.raises(ImportError), patch(
|
||||||
|
"homeassistant.loader.importlib.import_module", side_effect=ValueError("Boom")
|
||||||
|
):
|
||||||
|
assert hue_light == integration.get_platform("light")
|
||||||
|
|
||||||
|
|
||||||
async def test_get_integration_legacy(hass, enable_custom_integrations):
|
async def test_get_integration_legacy(hass, enable_custom_integrations):
|
||||||
"""Test resolving integration."""
|
"""Test resolving integration."""
|
||||||
integration = await loader.async_get_integration(hass, "test_embedded")
|
integration = await loader.async_get_integration(hass, "test_embedded")
|
||||||
|
|
|
@ -577,7 +577,7 @@ async def test_async_when_setup_or_start_already_loaded(hass):
|
||||||
async def test_setup_import_blows_up(hass):
|
async def test_setup_import_blows_up(hass):
|
||||||
"""Test that we handle it correctly when importing integration blows up."""
|
"""Test that we handle it correctly when importing integration blows up."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.loader.Integration.get_component", side_effect=ValueError
|
"homeassistant.loader.Integration.get_component", side_effect=ImportError
|
||||||
):
|
):
|
||||||
assert not await setup.async_setup_component(hass, "sun", {})
|
assert not await setup.async_setup_component(hass, "sun", {})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue