Test for circular dependencies using manifests (#22908)

* Integration dependencies

* Lint

* Lint

* Fix one test

* Lint

* Fix load custom component integration

Fix async issue
Add circular dependency detection in manifest validation

* Fix test

* Address review comment

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>
This commit is contained in:
Paulus Schoutsen 2019-04-09 09:30:32 -07:00 committed by Jason Hu
parent 4110bd0acf
commit cac00f5b26
11 changed files with 314 additions and 89 deletions

View file

@ -128,16 +128,17 @@ async def async_from_config_dict(config: Dict[str, Any],
hass.config_entries = config_entries.ConfigEntries(hass, config)
await hass.config_entries.async_initialize()
components = _get_components(hass, config)
domains = _get_domains(hass, config)
# Resolve all dependencies of all components.
for component in list(components):
try:
components.update(loader.component_dependencies(hass, component))
except loader.LoaderError:
# Ignore it, or we'll break startup
# It will be properly handled during setup.
pass
for dep_domains in await asyncio.gather(*[
loader.async_component_dependencies(hass, domain)
for domain in domains
], return_exceptions=True):
# Result is either a set or an exception. We ignore exceptions
# It will be properly handled during setup of the domain.
if isinstance(dep_domains, set):
domains.update(dep_domains)
# setup components
res = await core_component.async_setup(hass, config)
@ -151,10 +152,10 @@ async def async_from_config_dict(config: Dict[str, Any],
_LOGGER.info("Home Assistant core initialized")
# stage 0, load logging components
for component in components:
if component in LOGGING_COMPONENT:
for domain in domains:
if domain in LOGGING_COMPONENT:
hass.async_create_task(
async_setup_component(hass, component, config))
async_setup_component(hass, domain, config))
await hass.async_block_till_done()
@ -165,18 +166,18 @@ async def async_from_config_dict(config: Dict[str, Any],
hass.helpers.area_registry.async_get_registry())
# stage 1
for component in components:
if component in FIRST_INIT_COMPONENT:
for domain in domains:
if domain in FIRST_INIT_COMPONENT:
hass.async_create_task(
async_setup_component(hass, component, config))
async_setup_component(hass, domain, config))
await hass.async_block_till_done()
# stage 2
for component in components:
if component in FIRST_INIT_COMPONENT or component in LOGGING_COMPONENT:
for domain in domains:
if domain in FIRST_INIT_COMPONENT or domain in LOGGING_COMPONENT:
continue
hass.async_create_task(async_setup_component(hass, component, config))
hass.async_create_task(async_setup_component(hass, domain, config))
await hass.async_block_till_done()
@ -398,18 +399,17 @@ async def async_mount_local_lib_path(config_dir: str) -> str:
@core.callback
def _get_components(hass: core.HomeAssistant,
config: Dict[str, Any]) -> Set[str]:
"""Get components to set up."""
def _get_domains(hass: core.HomeAssistant, config: Dict[str, Any]) -> Set[str]:
"""Get domains of components to set up."""
# Filter out the repeating and common config section [homeassistant]
components = set(key.split(' ')[0] for key in config.keys()
if key != core.DOMAIN)
domains = set(key.split(' ')[0] for key in config.keys()
if key != core.DOMAIN)
# Add config entry domains
components.update(hass.config_entries.async_domains()) # type: ignore
domains.update(hass.config_entries.async_domains()) # type: ignore
# Make sure the Hass.io component is loaded
if 'HASSIO' in os.environ:
components.add('hassio')
domains.add('hassio')
return components
return domains