diff --git a/homeassistant/config.py b/homeassistant/config.py index f5870d683a0..6ff571f0d6b 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -697,6 +697,9 @@ async def async_process_component_config( except (vol.Invalid, HomeAssistantError) as ex: async_log_exception(ex, domain, config, hass, integration.documentation) return None + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unknown error calling %s config validator", domain) + return None # No custom config validator, proceed with schema validation if hasattr(component, "CONFIG_SCHEMA"): @@ -705,6 +708,9 @@ async def async_process_component_config( except vol.Invalid as ex: async_log_exception(ex, domain, config, hass, integration.documentation) return None + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unknown error calling %s CONFIG_SCHEMA", domain) + return None component_platform_schema = getattr( component, "PLATFORM_SCHEMA_BASE", getattr(component, "PLATFORM_SCHEMA", None) @@ -721,6 +727,13 @@ async def async_process_component_config( except vol.Invalid as ex: async_log_exception(ex, domain, p_config, hass, integration.documentation) continue + except Exception: # pylint: disable=broad-except + _LOGGER.exception( + "Unknown error validating %s platform config with %s component platform schema", + p_name, + domain, + ) + continue # Not all platform components follow same pattern for platforms # So if p_name is None we are not going to validate platform @@ -756,6 +769,13 @@ async def async_process_component_config( p_integration.documentation, ) continue + except Exception: # pylint: disable=broad-except + _LOGGER.exception( + "Unknown error validating config for %s platform for %s component with PLATFORM_SCHEMA", + p_name, + domain, + ) + continue platforms.append(p_validated) diff --git a/tests/test_config.py b/tests/test_config.py index 319ef841f46..fc5ec43093b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,9 +4,11 @@ import asyncio from collections import OrderedDict import copy import os -import unittest.mock as mock +from unittest import mock +from unittest.mock import Mock import asynctest +from asynctest import CoroutineMock, patch import pytest from voluptuous import Invalid, MultipleInvalid import yaml @@ -893,3 +895,97 @@ async def test_merge_split_component_definition(hass): assert len(config["light one"]) == 1 assert len(config["light two"]) == 1 assert len(config["light three"]) == 1 + + +async def test_component_config_exceptions(hass, caplog): + """Test unexpected exceptions validating component config.""" + # Config validator + assert ( + await config_util.async_process_component_config( + hass, + {}, + integration=Mock( + domain="test_domain", + get_platform=Mock( + return_value=Mock( + async_validate_config=CoroutineMock( + side_effect=ValueError("broken") + ) + ) + ), + ), + ) + is None + ) + assert "ValueError: broken" in caplog.text + assert "Unknown error calling test_domain config validator" in caplog.text + + # component.CONFIG_SCHEMA + caplog.clear() + assert ( + await config_util.async_process_component_config( + hass, + {}, + integration=Mock( + domain="test_domain", + get_platform=Mock(return_value=None), + get_component=Mock( + return_value=Mock( + CONFIG_SCHEMA=Mock(side_effect=ValueError("broken")) + ) + ), + ), + ) + is None + ) + assert "ValueError: broken" in caplog.text + assert "Unknown error calling test_domain CONFIG_SCHEMA" in caplog.text + + # component.PLATFORM_SCHEMA + caplog.clear() + assert await config_util.async_process_component_config( + hass, + {"test_domain": {"platform": "test_platform"}}, + integration=Mock( + domain="test_domain", + get_platform=Mock(return_value=None), + get_component=Mock( + return_value=Mock( + spec=["PLATFORM_SCHEMA_BASE"], + PLATFORM_SCHEMA_BASE=Mock(side_effect=ValueError("broken")), + ) + ), + ), + ) == {"test_domain": []} + assert "ValueError: broken" in caplog.text + assert ( + "Unknown error validating test_platform platform config with test_domain component platform schema" + in caplog.text + ) + + # platform.PLATFORM_SCHEMA + caplog.clear() + with patch( + "homeassistant.config.async_get_integration_with_requirements", + return_value=Mock( # integration that owns platform + get_platform=Mock( + return_value=Mock( # platform + PLATFORM_SCHEMA=Mock(side_effect=ValueError("broken")) + ) + ) + ), + ): + assert await config_util.async_process_component_config( + hass, + {"test_domain": {"platform": "test_platform"}}, + integration=Mock( + domain="test_domain", + get_platform=Mock(return_value=None), + get_component=Mock(return_value=Mock(spec=["PLATFORM_SCHEMA_BASE"])), + ), + ) == {"test_domain": []} + assert "ValueError: broken" in caplog.text + assert ( + "Unknown error validating config for test_platform platform for test_domain component with PLATFORM_SCHEMA" + in caplog.text + )