From 9d73f0357ebbbd51677111bf4cb3ce1acbb39a9c Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 1 Oct 2026 16:53:57 +0200 Subject: [PATCH] Use wait_shared_future in requirements (#183892) Co-authored-by: Claude Opus 5.5 --- homeassistant/requirements.py | 3 ++- tests/test_requirements.py | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index 6ff5df7738a1..d02656874633 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -17,6 +17,7 @@ from .loader import ( async_suggest_report_issue, ) from .util import package as pkg_util +from .util.async_ import wait_shared_future # The default is too low when the internet connection is satellite or high latency PIP_TIMEOUT = 60 @@ -153,7 +154,7 @@ class RequirementsManager: if int_or_fut := cache.get(domain): if isinstance(int_or_fut, Integration): return int_or_fut - return await int_or_fut + return await wait_shared_future(int_or_fut) future = cache[domain] = self.hass.loop.create_future() try: diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 1a9a59c72e72..2a7edb5ebf1e 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -291,6 +291,45 @@ async def test_get_integration_with_requirements_concurrency( assert process_integration_calls == 1 +async def test_get_integration_with_requirements_concurrent_cancelled( + hass: HomeAssistant, +) -> None: + """Verify cancelling a waiting caller does not break the in-progress load.""" + hass.config.skip_pip = False + mock_integration( + hass, MockModule("test_component_dep", requirements=["test-comp-dep==1.0.0"]) + ) + process_event = asyncio.Event() + finish_event = asyncio.Event() + + async def _async_process_integration_blocked(*args: object) -> None: + process_event.set() + await finish_event.wait() + + manager = _async_get_manager(hass) + with patch.object( + manager, "_async_process_integration", _async_process_integration_blocked + ): + load_task1 = asyncio.create_task( + async_get_integration_with_requirements(hass, "test_component_dep") + ) + load_task2 = asyncio.create_task( + async_get_integration_with_requirements(hass, "test_component_dep") + ) + await process_event.wait() + load_task2.cancel() + with pytest.raises(asyncio.CancelledError): + await load_task2 + finish_event.set() + integration = await load_task1 + + assert integration.domain == "test_component_dep" + assert ( + await async_get_integration_with_requirements(hass, "test_component_dep") + is integration + ) + + async def test_get_integration_with_requirements_pip_install_fails_two_passes( hass: HomeAssistant, ) -> None: