1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 09:38:58 +01:00

Avoid multiple executor jobs with concurrent calls to async_get_component (#112155)

This commit is contained in:
J. Nick Koston
2024-03-03 20:22:31 -10:00
committed by GitHub
parent 99414d8b85
commit 943996b60b
2 changed files with 94 additions and 8 deletions
+61 -2
View File
@@ -2,6 +2,7 @@
import asyncio
import os
import sys
import threading
from typing import Any
from unittest.mock import MagicMock, Mock, patch
@@ -1086,8 +1087,8 @@ async def test_async_get_component_loads_loop_if_already_in_sys_modules(
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
assert "test_package_loaded_executor" not in hass.config.components
assert "test_package_loaded_executor.config_flow" not in hass.config.components
config_flow_module_name = f"{integration.pkg_path}.config_flow"
module_mock = MagicMock()
@@ -1133,6 +1134,64 @@ async def test_async_get_component_loads_loop_if_already_in_sys_modules(
assert module is module_mock
async def test_async_get_component_concurrent_loads(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
enable_custom_integrations: None,
) -> None:
"""Verify async_get_component waits if the first load if called again when still in progress."""
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 "test_package_loaded_executor" not in hass.config.components
assert "test_package_loaded_executor.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()
imports = []
start_event = threading.Event()
import_event = asyncio.Event()
def import_module(name: str) -> Any:
hass.loop.call_soon_threadsafe(import_event.set)
imports.append(name)
start_event.wait()
if name == integration.pkg_path:
return module_mock
if name == config_flow_module_name:
return config_flow_module_mock
raise ImportError
modules_without_integration = {
k: v
for k, v in sys.modules.items()
if k != config_flow_module_name and k != integration.pkg_path
}
with patch.dict(
"sys.modules",
{**modules_without_integration},
clear=True,
), patch("homeassistant.loader.importlib.import_module", import_module):
load_task1 = asyncio.create_task(integration.async_get_component())
load_task2 = asyncio.create_task(integration.async_get_component())
await import_event.wait() # make sure the import is started
assert not integration._component_future.done()
start_event.set()
comp1 = await load_task1
comp2 = await load_task2
assert integration._component_future is None
assert comp1 is module_mock
assert comp2 is module_mock
assert imports == [integration.pkg_path, config_flow_module_name]
async def test_async_get_component_deadlock_fallback(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None: