diff --git a/homeassistant/config.py b/homeassistant/config.py index 896c9be3653..3fd76c275c2 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -1458,6 +1458,9 @@ async def async_process_component_config( # noqa: C901 # Check if the integration has a custom config validator config_validator = None + # A successful call to async_get_component will prime + # the cache for platform_exists to ensure it does no + # blocking I/O if integration.platform_exists("config") is not False: # If the config platform cannot possibly exist, don't try to load it. try: diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 7873fbd4c74..af15a7acabf 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -900,6 +900,14 @@ class Integration: ) raise ImportError(f"Exception importing {self.pkg_path}") from err + if self.platform_exists("config"): + # Setting up a component always checks if the config + # platform exists. Since we may be running in the executor + # we will use this opportunity to cache the config platform + # as well. + with suppress(ImportError): + self.get_platform("config") + return cache[self.domain] async def async_get_platform(self, platform_name: str) -> ModuleType: diff --git a/tests/test_loader.py b/tests/test_loader.py index 34104987de4..9552849ac4a 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -1038,6 +1038,37 @@ async def test_hass_components_use_reported( ) in caplog.text +async def test_async_get_component_preloads_config( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Verify async_get_component will try to preload the config platform.""" + executor_import_integration = _get_test_integration( + hass, "executor_import", True, import_executor=True + ) + assert executor_import_integration.import_executor is True + + assert "homeassistant.components.executor_import" not in sys.modules + assert "custom_components.executor_import" not in sys.modules + + with patch( + "homeassistant.loader.importlib.import_module" + ) as mock_import, patch.object( + executor_import_integration, "platform_exists", return_value=True + ) as mock_platform_exists: + await executor_import_integration.async_get_component() + + assert mock_platform_exists.call_count == 1 + assert mock_import.call_count == 2 + assert ( + mock_import.call_args_list[0][0][0] + == "homeassistant.components.executor_import" + ) + assert ( + mock_import.call_args_list[1][0][0] + == "homeassistant.components.executor_import.config" + ) + + async def test_async_get_component_deadlock_fallback( hass: HomeAssistant, caplog: pytest.LogCaptureFixture ) -> None: