Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions homeassistant/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
9 changes: 7 additions & 2 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
63 changes: 63 additions & 0 deletions tests/test_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down