Load integrations only once (#23132)

This commit is contained in:
Paulus Schoutsen 2019-04-15 20:38:24 -07:00 committed by GitHub
parent f48eb913b3
commit 8b86bf7dd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 12 deletions

View file

@ -4,6 +4,7 @@ The methods for loading Home Assistant integrations.
This module has quite some complex parts. I have tried to add as much
documentation as possible to keep it understandable.
"""
import asyncio
import functools as ft
import importlib
import json
@ -19,7 +20,7 @@ from typing import (
Any,
TypeVar,
List,
Dict
Dict,
)
# Typing imports that create a circular dependency
@ -116,6 +117,7 @@ class Integration:
self.domain = manifest['domain'] # type: str
self.dependencies = manifest['dependencies'] # type: List[str]
self.requirements = manifest['requirements'] # type: List[str]
_LOGGER.info("Loaded %s from %s", self.domain, pkg_path)
def get_component(self) -> ModuleType:
"""Return the component."""
@ -148,14 +150,20 @@ async def async_get_integration(hass: 'HomeAssistant', domain: str)\
raise IntegrationNotFound(domain)
cache = hass.data[DATA_INTEGRATIONS] = {}
integration = cache.get(domain, _UNDEF) # type: Optional[Integration]
int_or_evt = cache.get(domain, _UNDEF) # type: Optional[Integration]
if integration is _UNDEF:
if isinstance(int_or_evt, asyncio.Event):
await int_or_evt.wait()
int_or_evt = cache.get(domain, _UNDEF)
if int_or_evt is _UNDEF:
pass
elif integration is None:
elif int_or_evt is None:
raise IntegrationNotFound(domain)
else:
return integration
return int_or_evt
event = cache[domain] = asyncio.Event()
try:
import custom_components
@ -165,6 +173,7 @@ async def async_get_integration(hass: 'HomeAssistant', domain: str)\
if integration is not None:
_LOGGER.warning(CUSTOM_WARNING, domain)
cache[domain] = integration
event.set()
return integration
except ImportError:
@ -179,12 +188,12 @@ async def async_get_integration(hass: 'HomeAssistant', domain: str)\
if integration is not None:
cache[domain] = integration
event.set()
return integration
integration = await hass.async_add_executor_job(
Integration.resolve_legacy, hass, domain
)
integration = Integration.resolve_legacy(hass, domain)
cache[domain] = integration
event.set()
if not integration:
raise IntegrationNotFound(domain)
@ -253,8 +262,6 @@ def _load_file(hass, # type: HomeAssistant
if getattr(module, '__file__', None) is None:
continue
_LOGGER.info("Loaded %s from %s", comp_or_platform, path)
cache[comp_or_platform] = module
if module.__name__.startswith(PACKAGE_CUSTOM_COMPONENTS):
@ -318,8 +325,9 @@ class Components:
# Test integration cache
integration = self._hass.data.get(DATA_INTEGRATIONS, {}).get(comp_name)
if integration:
component = integration.get_component()
if isinstance(integration, Integration):
component = integration.get_component(
) # type: Optional[ModuleType]
else:
# Fallback to importing old-school
component = _load_file(self._hass, comp_name, LOOKUP_PATHS)

View file

@ -152,3 +152,13 @@ def test_integration_properties(hass):
assert integration.domain == 'hue'
assert integration.dependencies == ['test-dep']
assert integration.requirements == ['test-req==1.0.0']
async def test_integrations_only_once(hass):
"""Test that we load integrations only once."""
int_1 = hass.async_create_task(
loader.async_get_integration(hass, 'hue'))
int_2 = hass.async_create_task(
loader.async_get_integration(hass, 'hue'))
assert await int_1 is await int_2