-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
WIP: Split bootstrap into validation, requirements and setup #2912
8000New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
import logging.handlers | ||
import os | ||
import sys | ||
from collections import defaultdict | ||
from collections import defaultdict, OrderedDict | ||
from threading import RLock | ||
|
||
from types import ModuleType | ||
|
@@ -73,6 +73,63 @@ def _handle_requirements(hass: core.HomeAssistant, component, | |
return True | ||
|
||
|
||
def get_validated_component_config(hass: core.HomeAssistant, domain: str, | ||
config) -> bool: | ||
"""Validate config for a domain, including all platforms.""" | ||
component = loader.get_component(domain) | ||
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) | ||
if dep not in hass.config.components] | ||
|
||
if missing_deps: | ||
_LOGGER.error( | ||
'Not initializing %s because not all dependencies loaded: %s', | ||
domain, ", ".join(missing_deps)) | ||
return False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please return None instead of False. |
||
|
||
if hasattr(component, 'CONFIG_SCHEMA'): | ||
try: | ||
config = component.CONFIG_SCHEMA(config) | ||
except vol.MultipleInvalid as ex: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably not your code but this should be |
||
_log_exception(ex, domain, config) | ||
return False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same
<
10000
/div>
|
||
|
||
platforms = [] | ||
for p_name, p_config in config_per_platform(config, domain): | ||
# Validate component specific platform schema | ||
if hasattr(component, 'PLATFORM_SCHEMA'): | ||
try: | ||
p_validated = component.PLATFORM_SCHEMA(p_config) | ||
except vol.MultipleInvalid as ex: | ||
_log_exception(ex, domain, p_config) | ||
continue | ||
|
||
# Not all platform components follow same pattern for platforms | ||
# So if p_name is None we are not going to validate platform | ||
# (the automation component is one of them) | ||
if p_name is None: | ||
platforms.append(p_validated) | ||
continue | ||
|
||
# ensure the platform is loaded | ||
platform = prepare_setup_platform(hass, config, domain, p_name) | ||
|
||
if platform is None: | ||
continue | ||
|
||
# Validate platform specific schema | ||
if hasattr(platform, 'PLATFORM_SCHEMA'): | ||
try: | ||
p_validated = platform.PLATFORM_SCHEMA(p_validated) | ||
except vol.MultipleInvalid as ex: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vol.Invalid |
||
_log_exception(ex, '{}.{}'.format(domain, p_name), | ||
p_validated) | ||
return False | ||
|
||
platforms.append(p_validated) | ||
|
||
return platforms | ||
|
||
|
||
def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: | ||
"""Setup a component for Home Assistant.""" | ||
# pylint: disable=too-many-return-statements,too-many-branches | ||
|
@@ -90,66 +147,19 @@ def _setup_component(hass: core.HomeAssistant, domain: str, config) -> bool: | |
domain, domain) | ||
return False | ||
|
||
component = loader.get_component(domain) | ||
missing_deps = [dep for dep in getattr(component, 'DEPENDENCIES', []) | ||
if dep not in hass.config.components] | ||
|
||
if missing_deps: | ||
_LOGGER.error( | ||
'Not initializing %s because not all dependencies loaded: %s', | ||
domain, ", ".join(missing_deps)) | ||
return False | ||
|
||
if hasattr(component, 'CONFIG_SCHEMA'): | ||
try: | ||
config = component.CONFIG_SCHEMA(config) | ||
except vol.MultipleInvalid as ex: | ||
_log_exception(ex, domain, config) | ||
return False | ||
|
||
elif hasattr(component, 'PLATFORM_SCHEMA'): | ||
platforms = [] | ||
for p_name, p_config in config_per_platform(config, domain): | ||
# Validate component specific platform schema | ||
try: | ||
p_validated = component.PLATFORM_SCHEMA(p_config) | ||
except vol.MultipleInvalid as ex: | ||
_log_exception(ex, domain, p_config) | ||
return False | ||
|
||
# Not all platform components follow same pattern for platforms | ||
# So if p_name is None we are not going to validate platform | ||
# (the automation component is one of them) | ||
if p_name is None: | ||
platforms.append(p_validated) | ||
continue | ||
platforms = get_validated_component_config(hass, domain, config) | ||
|
||
platform = prepare_setup_platform(hass, config, domain, | ||
p_name) | ||
component = loader.get_component(domain) # already cached | ||
|
||
if platform is None: | ||
return False | ||
# Create a copy of the configuration with all config for current | ||
# component removed and add validated config back in. | ||
filter_keys = extract_domain_configs(config, domain) | ||
config = {key: value for key, value in config.items() | ||
if key not in filter_keys} | ||
config[domain] = platforms | ||
|
||
# Validate platform specific schema | ||
if hasattr(platform, 'PLATFORM_SCHEMA'): | ||
try: | ||
p_validated = platform.PLATFORM_SCHEMA(p_validated) | ||
except vol.MultipleInvalid as ex: | ||
_log_exception(ex, '{}.{}'.format(domain, p_name), | ||
p_validated) | ||
return False | ||
|
||
platforms.append(p_validated) | ||
|
||
# Create a copy of the configuration with all config for current | ||
# component removed and add validated config back in. | ||
filter_keys = extract_domain_configs(config, domain) | ||
config = {key: value for key, value in config.items() | ||
if key not in filter_keys} | ||
config[domain] = platforms | ||
|
||
if not _handle_requirements(hass, component, domain): | ||
return False | ||
# if not _handle_requirements(hass, component, domain): | ||
# return False | ||
|
||
_CURRENT_SETUP.append(domain) | ||
|
||
|
@@ -278,7 +288,39 @@ def from_config_dict(config: Dict[str, Any], | |
event_decorators.HASS = hass | ||
service.HASS = hass | ||
|
||
# Setup the components | ||
# For all component & platform setups follow these three steps: | ||
# (this might even be registered as service) | ||
# | ||
# 1 - validate config | ||
# - Load the components | ||
# - Run Schemas | ||
# - DEPENDENCIES rough check hass.components & config tree | ||
# | ||
# 2 - REQUIREMENTS (& remove config items if this fails) | ||
# | ||
# 3 - Finally perform setup | ||
# - can assume all config is validated | ||
# - DEPENDENCIES final check | ||
# | ||
|
||
# Step 1: Validate | ||
validated_config = OrderedDict({}) | ||
for domain in loader.load_order_components(components): | ||
validated_config[domain] = get_validated_component_config(hass, domain, | ||
config) | ||
|
||
# Step 2: Handle REQUIREMENTS | ||
if hass.config.skip_pip: | ||
for domain, platforms in validated_config.items(): | ||
if not _handle_requirements(hass, domain, domain): | ||
pass # remove it TBD | ||
for platform in platforms: | ||
if not _handle_requirements(hass, | ||
'{}.{}'.format(domain, platform), | ||
'{}.{}'.format(domain, platform)): | ||
pass # remove_it TBD | ||
|
||
# Step 3: Setup the components | ||
for domain in loader.load_order_components(components): | ||
_setup_component(hass, domain, config) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not help in being here for your scripts, as validate script will not be able to do this.