diff --git a/homeassistant/requirements.py b/homeassistant/requirements.py index f073fd13df8..aaad5c1f251 100644 --- a/homeassistant/requirements.py +++ b/homeassistant/requirements.py @@ -97,12 +97,21 @@ async def async_get_integration_with_requirements( deps_to_check.append(check_domain) if deps_to_check: - await asyncio.gather( + results = await asyncio.gather( *[ async_get_integration_with_requirements(hass, dep, done) for dep in deps_to_check - ] + ], + return_exceptions=True, ) + for result in results: + if not isinstance(result, BaseException): + continue + if not isinstance(result, IntegrationNotFound) or not ( + not integration.is_built_in + and result.domain in integration.after_dependencies + ): + raise result cache[domain] = integration event.set() diff --git a/tests/common.py b/tests/common.py index 32d2742f4d8..cc971ca4f13 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1046,10 +1046,15 @@ async def get_system_health_info(hass, domain): return await hass.data["system_health"][domain].info_callback(hass) -def mock_integration(hass, module): +def mock_integration(hass, module, built_in=True): """Mock an integration.""" integration = loader.Integration( - hass, f"homeassistant.components.{module.DOMAIN}", None, module.mock_manifest() + hass, + f"{loader.PACKAGE_BUILTIN}.{module.DOMAIN}" + if built_in + else f"{loader.PACKAGE_CUSTOM_COMPONENTS}.{module.DOMAIN}", + None, + module.mock_manifest(), ) def mock_import_platform(platform_name): diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 2c5b529467d..acc83afeec2 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -139,6 +139,69 @@ async def test_get_integration_with_requirements(hass): ] +async def test_get_integration_with_missing_dependencies(hass): + """Check getting an integration with missing dependencies.""" + hass.config.skip_pip = False + mock_integration( + hass, + MockModule("test_component_after_dep"), + ) + mock_integration( + hass, + MockModule( + "test_component", + dependencies=["test_component_dep"], + partial_manifest={"after_dependencies": ["test_component_after_dep"]}, + ), + ) + mock_integration( + hass, + MockModule( + "test_custom_component", + dependencies=["test_component_dep"], + partial_manifest={"after_dependencies": ["test_component_after_dep"]}, + ), + built_in=False, + ) + with pytest.raises(loader.IntegrationNotFound): + await async_get_integration_with_requirements(hass, "test_component") + with pytest.raises(loader.IntegrationNotFound): + await async_get_integration_with_requirements(hass, "test_custom_component") + + +async def test_get_built_in_integration_with_missing_after_dependencies(hass): + """Check getting a built_in integration with missing after_dependencies results in exception.""" + hass.config.skip_pip = False + mock_integration( + hass, + MockModule( + "test_component", + partial_manifest={"after_dependencies": ["test_component_after_dep"]}, + ), + built_in=True, + ) + with pytest.raises(loader.IntegrationNotFound): + await async_get_integration_with_requirements(hass, "test_component") + + +async def test_get_custom_integration_with_missing_after_dependencies(hass): + """Check getting a custom integration with missing after_dependencies.""" + hass.config.skip_pip = False + mock_integration( + hass, + MockModule( + "test_custom_component", + partial_manifest={"after_dependencies": ["test_component_after_dep"]}, + ), + built_in=False, + ) + integration = await async_get_integration_with_requirements( + hass, "test_custom_component" + ) + assert integration + assert integration.domain == "test_custom_component" + + async def test_install_with_wheels_index(hass): """Test an install attempt with wheels index URL.""" hass.config.skip_pip = False