Migrate integration_platform helper to use async_get_integrations (#89303)

* Migrate integration_platform helper to use async_get_integrations

We were fetching integrations inside the gather one
at a time. This is inefficent.

* cleanup

* cleanup

* add task name

* small tweaks

* gather only if we have tasks
pull/88942/head^2
J. Nick Koston 2023-03-08 11:01:47 -10:00 committed by GitHub
parent 4f11344bc3
commit e1d62b554a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 24 deletions

View File

@ -8,8 +8,8 @@ import logging
from typing import Any
from homeassistant.const import EVENT_COMPONENT_LOADED
from homeassistant.core import Event, HomeAssistant
from homeassistant.loader import async_get_integration, bind_hass
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.loader import Integration, async_get_integrations, bind_hass
from homeassistant.setup import ATTR_COMPONENT
_LOGGER = logging.getLogger(__name__)
@ -26,14 +26,24 @@ class IntegrationPlatform:
async def _async_process_single_integration_platform_component(
hass: HomeAssistant, component_name: str, integration_platform: IntegrationPlatform
hass: HomeAssistant,
component_name: str,
integration: Integration | Exception,
integration_platform: IntegrationPlatform,
) -> None:
"""Process a single integration platform."""
if component_name in integration_platform.seen_components:
return
integration_platform.seen_components.add(component_name)
integration = await async_get_integration(hass, component_name)
if isinstance(integration, Exception):
_LOGGER.exception(
"Error importing integration %s for %s",
component_name,
integration_platform.platform_name,
)
return
platform_name = integration_platform.platform_name
try:
@ -75,14 +85,22 @@ async def async_process_integration_platform_for_component(
integration_platforms: list[IntegrationPlatform] = hass.data[
DATA_INTEGRATION_PLATFORMS
]
await asyncio.gather(
*[
integrations = await async_get_integrations(hass, (component_name,))
tasks = [
asyncio.create_task(
_async_process_single_integration_platform_component(
hass, component_name, integration_platform
)
for integration_platform in integration_platforms
]
)
hass,
component_name,
integrations[component_name],
integration_platform,
),
name=f"process integration platform {integration_platform.platform_name} for {component_name}",
)
for integration_platform in integration_platforms
if component_name not in integration_platform.seen_components
]
if tasks:
await asyncio.gather(*tasks)
@bind_hass
@ -98,25 +116,39 @@ async def async_process_integration_platforms(
async def _async_component_loaded(event: Event) -> None:
"""Handle a new component loaded."""
comp = event.data[ATTR_COMPONENT]
if "." not in comp:
await async_process_integration_platform_for_component(hass, comp)
await async_process_integration_platform_for_component(
hass, event.data[ATTR_COMPONENT]
)
hass.bus.async_listen(EVENT_COMPONENT_LOADED, _async_component_loaded)
@callback
def _async_component_loaded_filter(event: Event) -> bool:
"""Handle integration platforms loaded."""
return "." not in event.data[ATTR_COMPONENT]
hass.bus.async_listen(
EVENT_COMPONENT_LOADED,
_async_component_loaded,
event_filter=_async_component_loaded_filter,
)
integration_platforms: list[IntegrationPlatform] = hass.data[
DATA_INTEGRATION_PLATFORMS
]
integration_platform = IntegrationPlatform(platform_name, process_platform, set())
integration_platforms.append(integration_platform)
if top_level_components := (
if top_level_components := [
comp for comp in hass.config.components if "." not in comp
):
await asyncio.gather(
*[
]:
integrations = await async_get_integrations(hass, top_level_components)
tasks = [
asyncio.create_task(
_async_process_single_integration_platform_component(
hass, comp, integration_platform
)
for comp in top_level_components
]
)
hass, comp, integrations[comp], integration_platform
),
name=f"process integration platform {platform_name} for {comp}",
)
for comp in top_level_components
if comp not in integration_platform.seen_components
]
if tasks:
await asyncio.gather(*tasks)

View File

@ -1,6 +1,8 @@
"""Test integration platform helpers."""
from unittest.mock import Mock
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers.integration_platform import (
async_process_integration_platform_for_component,
@ -51,3 +53,27 @@ async def test_process_integration_platforms_none_loaded(hass: HomeAssistant) ->
# Verify we can call async_process_integration_platform_for_component
# when there are none loaded and it does not throw
await async_process_integration_platform_for_component(hass, "any")
async def test_broken_integration(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test handling an integration with a broken or missing manifest."""
Mock()
hass.config.components.add("loaded")
event_platform = Mock()
mock_platform(hass, "event.platform_to_check", event_platform)
processed = []
async def _process_platform(hass, domain, platform):
"""Process platform."""
processed.append((domain, platform))
await async_process_integration_platforms(
hass, "platform_to_check", _process_platform
)
assert len(processed) == 0
assert "Error importing integration loaded for platform_to_check" in caplog.text