From 01ae6954395d7c088382cab99c33df973d4a498f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 7 Apr 2026 21:33:06 -1000 Subject: [PATCH] Fix load_yaml returning unresolved IncludeFile for top-level !include Move the IncludeFile resolution into _load_yaml_internal so all callers get resolved content, rather than patching individual call sites. --- esphome/yaml_util.py | 7 ++++++- script/analyze_component_buses.py | 3 --- script/merge_component_configs.py | 8 +------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 520379e51d2..59d851c02e7 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -599,9 +599,14 @@ def _load_yaml_internal(fname: Path) -> Any: listener(fname) try: with fname.open(encoding="utf-8") as f_handle: - return parse_yaml(fname, f_handle) + res = parse_yaml(fname, f_handle) except (UnicodeDecodeError, OSError) as err: raise EsphomeError(f"Error reading file {fname}: {err}") from err + # Top-level !include returns a deferred IncludeFile; resolve it so + # callers always receive the final content. + if isinstance(res, IncludeFile): + res = res.load() + return res def parse_yaml(file_name: Path, file_handle: TextIOWrapper, yaml_loader=None) -> Any: diff --git a/script/analyze_component_buses.py b/script/analyze_component_buses.py index e6c68847340..17af7af5771 100755 --- a/script/analyze_component_buses.py +++ b/script/analyze_component_buses.py @@ -221,9 +221,6 @@ def analyze_yaml_file(yaml_file: Path) -> dict[str, Any]: try: data = yaml_util.load_yaml(yaml_file) - # Top-level !include returns an IncludeFile that must be resolved - if isinstance(data, yaml_util.IncludeFile): - data = data.load() result["loaded"] = True except Exception: # pylint: disable=broad-exception-caught return result diff --git a/script/merge_component_configs.py b/script/merge_component_configs.py index 453d4109dda..41bbafcd021 100755 --- a/script/merge_component_configs.py +++ b/script/merge_component_configs.py @@ -27,7 +27,6 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) from esphome import yaml_util from esphome.config_helpers import merge_config -from esphome.yaml_util import IncludeFile from script.analyze_component_buses import PACKAGE_DEPENDENCIES, get_common_bus_packages # Prefix for dependency markers in package tracking @@ -47,12 +46,7 @@ def load_yaml_file(yaml_file: Path) -> dict: if not yaml_file.exists(): raise FileNotFoundError(f"YAML file not found: {yaml_file}") - data = yaml_util.load_yaml(yaml_file) - # Top-level !include (e.g., `!include common.yaml`) returns an IncludeFile - # that must be resolved before we can work with it as a dict. - if isinstance(data, IncludeFile): - data = data.load() - return data + return yaml_util.load_yaml(yaml_file) @lru_cache(maxsize=256)