1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-25 21:23:53 +01:00

[substitutions] Fix AttributeError when using packages with substitutions (#11274)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
J. Nick Koston
2025-10-15 17:23:20 -10:00
committed by GitHub
parent 6943b1d985
commit f2e0a412db
4 changed files with 236 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
import logging
from esphome import core
from esphome.config_helpers import Extend, Remove, merge_config
from esphome.config_helpers import Extend, Remove, merge_config, merge_dicts_ordered
import esphome.config_validation as cv
from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS
from esphome.yaml_util import ESPHomeDataBase, ESPLiteralValue, make_data_base
@@ -170,10 +170,10 @@ def do_substitution_pass(config, command_line_substitutions, ignore_missing=Fals
return
# Merge substitutions in config, overriding with substitutions coming from command line:
substitutions = {
**config.get(CONF_SUBSTITUTIONS, {}),
**(command_line_substitutions or {}),
}
# Use merge_dicts_ordered to preserve OrderedDict type for move_to_end()
substitutions = merge_dicts_ordered(
config.get(CONF_SUBSTITUTIONS, {}), command_line_substitutions or {}
)
with cv.prepend_path("substitutions"):
if not isinstance(substitutions, dict):
raise cv.Invalid(

View File

@@ -12,7 +12,7 @@ from typing import Any
import voluptuous as vol
from esphome import core, loader, pins, yaml_util
from esphome.config_helpers import Extend, Remove
from esphome.config_helpers import Extend, Remove, merge_dicts_ordered
import esphome.config_validation as cv
from esphome.const import (
CONF_ESPHOME,
@@ -922,10 +922,9 @@ def validate_config(
if CONF_SUBSTITUTIONS in config or command_line_substitutions:
from esphome.components import substitutions
result[CONF_SUBSTITUTIONS] = {
**(config.get(CONF_SUBSTITUTIONS) or {}),
**command_line_substitutions,
}
result[CONF_SUBSTITUTIONS] = merge_dicts_ordered(
config.get(CONF_SUBSTITUTIONS) or {}, command_line_substitutions
)
result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS)
try:
substitutions.do_substitution_pass(config, command_line_substitutions)

View File

@@ -10,6 +10,7 @@ from esphome.const import (
PlatformFramework,
)
from esphome.core import CORE
from esphome.util import OrderedDict
# Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum
_PLATFORM_FRAMEWORK_LOOKUP = {
@@ -17,6 +18,25 @@ _PLATFORM_FRAMEWORK_LOOKUP = {
}
def merge_dicts_ordered(*dicts: dict) -> OrderedDict:
"""Merge multiple dicts into an OrderedDict, preserving key order.
This is a helper to ensure that dictionary merging preserves OrderedDict type,
which is important for operations like move_to_end().
Args:
*dicts: Variable number of dictionaries to merge (later dicts override earlier ones)
Returns:
OrderedDict with merged contents
"""
result = OrderedDict()
for d in dicts:
if d:
result.update(d)
return result
class Extend:
def __init__(self, value):
self.value = value
@@ -60,7 +80,11 @@ def merge_config(full_old, full_new):
if isinstance(new, dict):
if not isinstance(old, dict):
return new
res = old.copy()
# Preserve OrderedDict type by copying to OrderedDict if either input is OrderedDict
if isinstance(old, OrderedDict) or isinstance(new, OrderedDict):
res = OrderedDict(old)
else:
res = old.copy()
for k, v in new.items():
if isinstance(v, Remove) and k in old:
del res[k]