diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 3cfc9c4b15d..b153d160a72 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -139,7 +139,7 @@ def add_context(value: Any, context_vars: dict[str, Any] | None) -> Any: value.set_context({**value.vars, **(context_vars or {})}) return value - if context_vars and isinstance(value, (dict, list, str)): + if context_vars and isinstance(value, (dict, list, str, Lambda)): value = add_class_to_obj(value, ConfigContext) value.set_context(context_vars) return value diff --git a/tests/unit_tests/test_substitutions.py b/tests/unit_tests/test_substitutions.py index cf6d4adbf51..4783112578c 100644 --- a/tests/unit_tests/test_substitutions.py +++ b/tests/unit_tests/test_substitutions.py @@ -818,3 +818,23 @@ def test_resolve_include_error_no_expanded_from_for_literal_filename( substitutions.resolve_include(include, [], substitutions.ContextVars()) assert "expanded from" not in str(exc_info.value) + + +def test_include_vars_applied_to_lambda_value(tmp_path: Path) -> None: + """!include vars: must substitute into a top-level !lambda value in the included file. + + Regression test for the case where the included file's root is a Lambda; + add_context() previously only tagged dict/list/str, so the include's vars + never reached the substitution pass for Lambda content. + """ + included = tmp_path / "lambda.yaml" + included.write_text('!lambda |-\n return "${foo}";\n') + + include = yaml_util.IncludeFile( + tmp_path / "main.yaml", "lambda.yaml", {"foo": "bar"}, yaml_util.load_yaml + ) + config = OrderedDict({"value": include.load()}) + result = substitutions.do_substitution_pass(config) + + assert isinstance(result["value"], Lambda) + assert result["value"].value == 'return "bar";'