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
This commit is contained in:
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 typing import Any
from homeassistant.const import EVENT_COMPONENT_LOADED from homeassistant.const import EVENT_COMPONENT_LOADED
from homeassistant.core import Event, HomeAssistant from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.loader import async_get_integration, bind_hass from homeassistant.loader import Integration, async_get_integrations, bind_hass
from homeassistant.setup import ATTR_COMPONENT from homeassistant.setup import ATTR_COMPONENT
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -26,14 +26,24 @@ class IntegrationPlatform:
async def _async_process_single_integration_platform_component( 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: ) -> None:
"""Process a single integration platform.""" """Process a single integration platform."""
if component_name in integration_platform.seen_components: if component_name in integration_platform.seen_components:
return return
integration_platform.seen_components.add(component_name) 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 platform_name = integration_platform.platform_name
try: try:
@ -75,14 +85,22 @@ async def async_process_integration_platform_for_component(
integration_platforms: list[IntegrationPlatform] = hass.data[ integration_platforms: list[IntegrationPlatform] = hass.data[
DATA_INTEGRATION_PLATFORMS DATA_INTEGRATION_PLATFORMS
] ]
await asyncio.gather( integrations = await async_get_integrations(hass, (component_name,))
*[ tasks = [
asyncio.create_task(
_async_process_single_integration_platform_component( _async_process_single_integration_platform_component(
hass, component_name, integration_platform hass,
) component_name,
for integration_platform in integration_platforms 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 @bind_hass
@ -98,25 +116,39 @@ async def async_process_integration_platforms(
async def _async_component_loaded(event: Event) -> None: async def _async_component_loaded(event: Event) -> None:
"""Handle a new component loaded.""" """Handle a new component loaded."""
comp = event.data[ATTR_COMPONENT] await async_process_integration_platform_for_component(
if "." not in comp: hass, event.data[ATTR_COMPONENT]
await async_process_integration_platform_for_component(hass, comp) )
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[ integration_platforms: list[IntegrationPlatform] = hass.data[
DATA_INTEGRATION_PLATFORMS DATA_INTEGRATION_PLATFORMS
] ]
integration_platform = IntegrationPlatform(platform_name, process_platform, set()) integration_platform = IntegrationPlatform(platform_name, process_platform, set())
integration_platforms.append(integration_platform) integration_platforms.append(integration_platform)
if top_level_components := ( if top_level_components := [
comp for comp in hass.config.components if "." not in comp 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( _async_process_single_integration_platform_component(
hass, comp, integration_platform hass, comp, integrations[comp], integration_platform
) ),
for comp in top_level_components 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.""" """Test integration platform helpers."""
from unittest.mock import Mock from unittest.mock import Mock
import pytest
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.integration_platform import ( from homeassistant.helpers.integration_platform import (
async_process_integration_platform_for_component, 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 # Verify we can call async_process_integration_platform_for_component
# when there are none loaded and it does not throw # when there are none loaded and it does not throw
await async_process_integration_platform_for_component(hass, "any") 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