[substitutions] [packages] Extract resolve_substitutions_block helper

Deduplicate the resolve+shape-check block between do_substitution_pass and
do_packages_pass, and drop the raw_substitutions/substitutions naming split.
This commit is contained in:
J. Nick Koston
2026-04-19 02:46:22 -05:00
parent e67b65b669
commit f579cf48a0
2 changed files with 33 additions and 29 deletions
+5 -15
View File
@@ -10,6 +10,7 @@ from esphome.components.substitutions import (
ContextVars,
push_context,
resolve_include,
resolve_substitutions_block,
substitute,
)
from esphome.components.substitutions.jinja import has_jinja
@@ -516,23 +517,12 @@ def do_packages_pass(
if CONF_PACKAGES not in config:
return config
raw_substitutions = config.pop(CONF_SUBSTITUTIONS, {})
with cv.prepend_path(CONF_SUBSTITUTIONS):
if isinstance(raw_substitutions, yaml_util.IncludeFile):
# Resolve `substitutions: !include file.yaml` before feeding into UserDict.
# Seed with command-line substitutions so `!include ${var}.yaml` can
# reference CLI-provided vars in the filename.
raw_substitutions, _ = resolve_include(
raw_substitutions,
[],
ContextVars(command_line_substitutions or {}),
strict_undefined=False,
substitutions = UserDict(
resolve_substitutions_block(
config.pop(CONF_SUBSTITUTIONS, {}), command_line_substitutions
)
if not isinstance(raw_substitutions, dict):
raise cv.Invalid(
f"Substitutions must be a key to value mapping, got {type(raw_substitutions)}"
)
substitutions = UserDict(raw_substitutions)
)
processor = _PackageProcessor(
substitutions, command_line_substitutions, skip_update
)
+28 -14
View File
@@ -414,6 +414,31 @@ def _warn_unresolved_variables(errors: ErrList) -> None:
)
def resolve_substitutions_block(
substitutions: Any,
command_line_substitutions: dict[str, Any] | None,
) -> dict[str, Any]:
"""Resolve a deferred ``substitutions: !include file.yaml`` and validate the shape.
The caller is responsible for wrapping the call in
``cv.prepend_path(CONF_SUBSTITUTIONS)`` for error reporting.
``command_line_substitutions`` seeds the filename context so
``substitutions: !include ${var}.yaml`` can reference CLI-provided vars.
"""
if isinstance(substitutions, IncludeFile):
substitutions, _ = resolve_include(
substitutions,
[],
ContextVars(command_line_substitutions or {}),
strict_undefined=False,
)
if not isinstance(substitutions, dict):
raise cv.Invalid(
f"Substitutions must be a key to value mapping, got {type(substitutions)}"
)
return substitutions
def do_substitution_pass(
config: OrderedDict, command_line_substitutions: dict[str, Any] | None = None
) -> OrderedDict:
@@ -429,20 +454,9 @@ def do_substitution_pass(
# Use merge_dicts_ordered to preserve OrderedDict type for move_to_end()
substitutions = config.pop(CONF_SUBSTITUTIONS, {})
with cv.prepend_path(CONF_SUBSTITUTIONS):
if isinstance(substitutions, IncludeFile):
# Resolve `substitutions: !include file.yaml` before validating the shape.
# Seed with command-line substitutions so `!include ${var}.yaml` can
# reference CLI-provided vars in the filename.
substitutions, _ = resolve_include(
substitutions,
[],
ContextVars(command_line_substitutions or {}),
strict_undefined=False,
)
if not isinstance(substitutions, dict):
raise cv.Invalid(
f"Substitutions must be a key to value mapping, got {type(substitutions)}"
)
substitutions = resolve_substitutions_block(
substitutions, command_line_substitutions
)
substitutions = merge_dicts_ordered(
substitutions, command_line_substitutions or {}
)