Fix async_get_component loading in the executor when the module is already loaded (#112153)

This commit is contained in:
J. Nick Koston 2024-03-03 17:56:50 -10:00 committed by GitHub
parent bef8376f83
commit f4b2c9b569
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 2 deletions

View File

@ -847,8 +847,9 @@ class Integration:
domain = self.domain
# Some integrations fail on import because they call functions incorrectly.
# So we do it before validating config to catch these errors.
load_executor = (
self.import_executor and f"{self.pkg_path}.{domain}" not in sys.modules
load_executor = self.import_executor and (
self.pkg_path not in sys.modules
or (self.config_flow and f"{self.pkg_path}.config_flow" not in sys.modules)
)
if load_executor:
try:

View File

@ -1073,6 +1073,66 @@ async def test_async_get_component_preloads_config_and_config_flow(
)
async def test_async_get_component_loads_loop_if_already_in_sys_modules(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
enable_custom_integrations: None,
) -> None:
"""Verify async_get_component does not create an executor job if the module is already in sys.modules."""
integration = await loader.async_get_integration(
hass, "test_package_loaded_executor"
)
assert integration.pkg_path == "custom_components.test_package_loaded_executor"
assert integration.import_executor is True
assert integration.config_flow is True
assert "executor_import" not in hass.config.components
assert "executor_import.config_flow" not in hass.config.components
config_flow_module_name = f"{integration.pkg_path}.config_flow"
module_mock = MagicMock()
config_flow_module_mock = MagicMock()
def import_module(name: str) -> Any:
if name == integration.pkg_path:
return module_mock
if name == config_flow_module_name:
return config_flow_module_mock
raise ImportError
modules_without_config_flow = {
k: v for k, v in sys.modules.items() if k != config_flow_module_name
}
with patch.dict(
"sys.modules",
{**modules_without_config_flow, integration.pkg_path: module_mock},
clear=True,
), patch("homeassistant.loader.importlib.import_module", import_module):
module = await integration.async_get_component()
# The config flow is missing so we should load
# in the executor
assert "loaded_executor=True" in caplog.text
assert "loaded_executor=False" not in caplog.text
assert module is module_mock
caplog.clear()
with patch.dict(
"sys.modules",
{
integration.pkg_path: module_mock,
config_flow_module_name: config_flow_module_mock,
},
), patch("homeassistant.loader.importlib.import_module", import_module):
module = await integration.async_get_component()
# Everything is there so we should load in the event loop
# since it will all be cached
assert "loaded_executor=False" in caplog.text
assert "loaded_executor=True" not in caplog.text
assert module is module_mock
async def test_async_get_component_deadlock_fallback(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None: