diff --git a/homeassistant/config.py b/homeassistant/config.py index 949774d33612..fc2feb480654 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -14,7 +14,7 @@ from pathlib import Path import re import shutil from types import ModuleType -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, TypeVar from urllib.parse import urlparse from awesomeversion import AwesomeVersion @@ -67,6 +67,7 @@ from .requirements import RequirementsNotFound, async_get_integration_with_requi from .util.package import is_docker_env from .util.unit_system import get_unit_system, validate_unit_system from .util.yaml import SECRET_YAML, Secrets, YamlTypeError, load_yaml_dict +from .util.yaml.objects import NodeStrClass _LOGGER = logging.getLogger(__name__) @@ -146,6 +147,9 @@ class ConfigExceptionInfo: integration_link: str | None +_T = TypeVar("_T") + + @dataclass class IntegrationConfigInfo: """Configuration for an integration and exception information.""" @@ -1221,9 +1225,45 @@ async def async_process_component_and_handle_errors( integration_config_info = await async_process_component_config( hass, config, integration ) - return async_handle_component_errors( + async_handle_component_errors( hass, integration_config_info, integration, raise_on_failure ) + return async_drop_config_annotations(integration_config_info, integration) + + +@callback +def async_drop_config_annotations( + integration_config_info: IntegrationConfigInfo, + integration: Integration, +) -> ConfigType | None: + """Remove file and line annotations from str items in component configuration.""" + if (config := integration_config_info.config) is None: + return None + + def drop_config_annotations_rec(node: Any) -> Any: + if isinstance(node, dict): + # Some integrations store metadata in custom dict classes, preserve those + tmp = dict(node) + node.clear() + node.update( + (drop_config_annotations_rec(k), drop_config_annotations_rec(v)) + for k, v in tmp.items() + ) + return node + + if isinstance(node, list): + return [drop_config_annotations_rec(v) for v in node] + + if isinstance(node, NodeStrClass): + return str(node) + + return node + + # Don't drop annotations from the homeassistant integration because it may + # have configuration for other integrations as packages. + if integration.domain in config and integration.domain != CONF_CORE: + drop_config_annotations_rec(config[integration.domain]) + return config @callback @@ -1232,18 +1272,16 @@ def async_handle_component_errors( integration_config_info: IntegrationConfigInfo, integration: Integration, raise_on_failure: bool = False, -) -> ConfigType | None: +) -> None: """Handle component configuration errors from async_process_component_config. In case of errors: - Print the error messages to the log. - Raise a ConfigValidationError if raise_on_failure is set. - - Returns the integration config or `None`. """ if not (config_exception_info := integration_config_info.exception_info_list): - return integration_config_info.config + return platform_exception: ConfigExceptionInfo domain = integration.domain @@ -1261,7 +1299,7 @@ def async_handle_component_errors( ) if not raise_on_failure: - return integration_config_info.config + return if len(config_exception_info) == 1: translation_key = platform_exception.translation_key diff --git a/homeassistant/setup.py b/homeassistant/setup.py index 7a7f4323be60..5408da20a702 100644 --- a/homeassistant/setup.py +++ b/homeassistant/setup.py @@ -256,8 +256,9 @@ async def _async_setup_component( integration_config_info = await conf_util.async_process_component_config( hass, config, integration ) - processed_config = conf_util.async_handle_component_errors( - hass, integration_config_info, integration + conf_util.async_handle_component_errors(hass, integration_config_info, integration) + processed_config = conf_util.async_drop_config_annotations( + integration_config_info, integration ) for platform_exception in integration_config_info.exception_info_list: if platform_exception.translation_key not in NOTIFY_FOR_TRANSLATION_KEYS: