Fix check config (#43663)

This commit is contained in:
Paulus Schoutsen 2020-11-26 22:25:21 +01:00 committed by GitHub
parent edf70e9f06
commit e1de36fda8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 21 deletions

View file

@ -212,7 +212,13 @@ class DomainBlueprints:
"""Load a blueprint."""
try:
blueprint_data = yaml.load_yaml(self.blueprint_folder / blueprint_path)
except (HomeAssistantError, FileNotFoundError) as err:
except FileNotFoundError as err:
raise FailedToLoad(
self.domain,
blueprint_path,
FileNotFoundError(f"Unable to find {blueprint_path}"),
) from err
except HomeAssistantError as err:
raise FailedToLoad(self.domain, blueprint_path, err) from err
return Blueprint(
@ -251,13 +257,25 @@ class DomainBlueprints:
async def async_get_blueprint(self, blueprint_path: str) -> Blueprint:
"""Get a blueprint."""
def load_from_cache():
"""Load blueprint from cache."""
blueprint = self._blueprints[blueprint_path]
if blueprint is None:
raise FailedToLoad(
self.domain,
blueprint_path,
FileNotFoundError(f"Unable to find {blueprint_path}"),
)
return blueprint
if blueprint_path in self._blueprints:
return self._blueprints[blueprint_path]
return load_from_cache()
async with self._load_lock:
# Check it again
if blueprint_path in self._blueprints:
return self._blueprints[blueprint_path]
return load_from_cache()
try:
blueprint = await self.hass.async_add_executor_job(

View file

@ -1,9 +1,9 @@
"""Helper to check the configuration file."""
from collections import OrderedDict
import logging
import os
from typing import List, NamedTuple, Optional
import attr
import voluptuous as vol
from homeassistant import loader
@ -36,11 +36,13 @@ class CheckConfigError(NamedTuple):
config: Optional[ConfigType]
@attr.s
class HomeAssistantConfig(OrderedDict):
"""Configuration result with errors attribute."""
errors: List[CheckConfigError] = attr.ib(factory=list)
def __init__(self) -> None:
"""Initialize HA config."""
super().__init__()
self.errors: List[CheckConfigError] = []
def add_error(
self,
@ -139,14 +141,24 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> HomeAssistantConfig
config_validator, "async_validate_config"
):
try:
return await config_validator.async_validate_config( # type: ignore
hass, config
)
result[domain] = (
await config_validator.async_validate_config( # type: ignore
hass, config
)
)[domain]
continue
except (vol.Invalid, HomeAssistantError) as ex:
_comp_error(ex, domain, config)
continue
except Exception: # pylint: disable=broad-except
result.add_error("Unknown error calling %s config validator", domain)
except Exception as err: # pylint: disable=broad-except
logging.getLogger(__name__).exception(
"Unexpected error validating config"
)
result.add_error(
f"Unexpected error calling config validator: {err}",
domain,
config.get(domain),
)
continue
config_schema = getattr(component, "CONFIG_SCHEMA", None)

View file

@ -203,8 +203,8 @@ async def test_domain_blueprints_get_blueprint_errors(hass, domain_bps):
with patch(
"homeassistant.util.yaml.load_yaml", return_value={"blueprint": "invalid"}
):
assert await domain_bps.async_get_blueprint("non-existing-path") is None
), pytest.raises(errors.FailedToLoad):
await domain_bps.async_get_blueprint("non-existing-path")
async def test_domain_blueprints_caching(domain_bps):
@ -258,3 +258,11 @@ async def test_domain_blueprints_add_blueprint(domain_bps, blueprint_1):
with patch.object(domain_bps, "_load_blueprint") as mock_load:
assert await domain_bps.async_get_blueprint("something.yaml") == blueprint_1
assert not mock_load.mock_calls
async def test_inputs_from_config_nonexisting_blueprint(domain_bps):
"""Test referring non-existing blueprint."""
with pytest.raises(errors.FailedToLoad):
await domain_bps.async_inputs_from_config(
{"use_blueprint": {"path": "non-existing.yaml"}}
)

View file

@ -7,8 +7,8 @@ from homeassistant.helpers.check_config import (
async_check_ha_config_file,
)
from tests.async_mock import patch
from tests.common import patch_yaml_files
from tests.async_mock import Mock, patch
from tests.common import mock_platform, patch_yaml_files
_LOGGER = logging.getLogger(__name__)
@ -37,7 +37,7 @@ def log_ha_config(conf):
_LOGGER.debug("error[%s] = %s", cnt, err)
async def test_bad_core_config(hass, loop):
async def test_bad_core_config(hass):
"""Test a bad core config setup."""
files = {YAML_CONFIG_FILE: BAD_CORE_CONFIG}
with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
@ -53,7 +53,7 @@ async def test_bad_core_config(hass, loop):
assert not res.errors
async def test_config_platform_valid(hass, loop):
async def test_config_platform_valid(hass):
"""Test a valid platform setup."""
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: demo"}
with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
@ -65,7 +65,7 @@ async def test_config_platform_valid(hass, loop):
assert not res.errors
async def test_component_platform_not_found(hass, loop):
async def test_component_platform_not_found(hass):
"""Test errors if component or platform not found."""
# Make sure they don't exist
files = {YAML_CONFIG_FILE: BASE_CONFIG + "beer:"}
@ -83,7 +83,7 @@ async def test_component_platform_not_found(hass, loop):
assert not res.errors
async def test_component_platform_not_found_2(hass, loop):
async def test_component_platform_not_found_2(hass):
"""Test errors if component or platform not found."""
# Make sure they don't exist
files = {YAML_CONFIG_FILE: BASE_CONFIG + "light:\n platform: beer"}
@ -103,7 +103,7 @@ async def test_component_platform_not_found_2(hass, loop):
assert not res.errors
async def test_package_invalid(hass, loop):
async def test_package_invalid(hass):
"""Test a valid platform setup."""
files = {
YAML_CONFIG_FILE: BASE_CONFIG + (" packages:\n p1:\n" ' group: ["a"]')
@ -121,7 +121,7 @@ async def test_package_invalid(hass, loop):
assert res.keys() == {"homeassistant"}
async def test_bootstrap_error(hass, loop):
async def test_bootstrap_error(hass):
"""Test a valid platform setup."""
files = {YAML_CONFIG_FILE: BASE_CONFIG + "automation: !include no.yaml"}
with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
@ -146,6 +146,7 @@ automation:
input:
trigger_event: blueprint_event
service_to_call: test.automation
input_datetime:
""",
hass.config.path(
"blueprints/automation/test_event_service.yaml"
@ -166,3 +167,28 @@ action:
with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
res = await async_check_ha_config_file(hass)
assert len(res.get("automation", [])) == 1
assert len(res.errors) == 0
assert "input_datetime" in res
async def test_config_platform_raise(hass):
"""Test bad config validation platform."""
mock_platform(
hass,
"bla.config",
Mock(async_validate_config=Mock(side_effect=Exception("Broken"))),
)
files = {
YAML_CONFIG_FILE: BASE_CONFIG
+ """
bla:
value: 1
""",
}
with patch("os.path.isfile", return_value=True), patch_yaml_files(files):
res = await async_check_ha_config_file(hass)
assert len(res.errors) == 1
err = res.errors[0]
assert err.domain == "bla"
assert err.message == "Unexpected error calling config validator: Broken"
assert err.config == {"value": 1}