mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[script] Recursively expand nested packages when merging component tests (#17557)
This commit is contained in:
@@ -263,22 +263,39 @@ def prepare_component_body(comp_data: dict, comp_name: str, comp_dir: Path) -> d
|
|||||||
else {}
|
else {}
|
||||||
)
|
)
|
||||||
|
|
||||||
packages_value = comp_data.get("packages")
|
# Expand component-specific package includes inline. A package include may
|
||||||
if isinstance(packages_value, dict):
|
# itself pull in further component-specific packages (e.g. web_server's test
|
||||||
common_bus_packages = get_common_bus_packages()
|
# includes common_v2, which includes common with the wifi/network config), so
|
||||||
for pkg_name, pkg_value in list(packages_value.items()):
|
# keep expanding until only common bus packages remain -- otherwise the nested
|
||||||
if pkg_name in common_bus_packages:
|
# includes are silently dropped when the packages key is removed below.
|
||||||
continue
|
common_bus_packages = get_common_bus_packages()
|
||||||
if isinstance(pkg_value, yaml_util.IncludeFile):
|
while True:
|
||||||
pkg_value = pkg_value.load()
|
packages_value = comp_data.get("packages")
|
||||||
if isinstance(pkg_value, dict):
|
expanded = False
|
||||||
comp_data = merge_config(comp_data, pkg_value)
|
if isinstance(packages_value, dict):
|
||||||
elif isinstance(packages_value, list):
|
for pkg_name, pkg_value in list(packages_value.items()):
|
||||||
for pkg_value in packages_value:
|
if pkg_name in common_bus_packages:
|
||||||
if isinstance(pkg_value, yaml_util.IncludeFile):
|
continue
|
||||||
pkg_value = pkg_value.load()
|
# Drop before merging so a nested packages dict introduced by the
|
||||||
if isinstance(pkg_value, dict):
|
# include does not re-add this same key on the next iteration.
|
||||||
comp_data = merge_config(comp_data, pkg_value)
|
del packages_value[pkg_name]
|
||||||
|
if isinstance(pkg_value, yaml_util.IncludeFile):
|
||||||
|
pkg_value = pkg_value.load()
|
||||||
|
if isinstance(pkg_value, dict):
|
||||||
|
comp_data = merge_config(comp_data, pkg_value)
|
||||||
|
expanded = True
|
||||||
|
elif isinstance(packages_value, list):
|
||||||
|
# List-style packages never contain common bus packages, so expand
|
||||||
|
# them all and drop the key entirely.
|
||||||
|
comp_data.pop("packages", None)
|
||||||
|
for pkg_value in packages_value:
|
||||||
|
if isinstance(pkg_value, yaml_util.IncludeFile):
|
||||||
|
pkg_value = pkg_value.load()
|
||||||
|
if isinstance(pkg_value, dict):
|
||||||
|
comp_data = merge_config(comp_data, pkg_value)
|
||||||
|
expanded = True
|
||||||
|
if not expanded:
|
||||||
|
break
|
||||||
# Common bus packages are re-added once by the caller; drop them here.
|
# Common bus packages are re-added once by the caller; drop them here.
|
||||||
comp_data.pop("packages", None)
|
comp_data.pop("packages", None)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ sys.path.insert(0, str((Path(__file__).parent / ".." / ".." / "script").resolve(
|
|||||||
|
|
||||||
import merge_component_configs # noqa: E402
|
import merge_component_configs # noqa: E402
|
||||||
|
|
||||||
|
from esphome import yaml_util # noqa: E402
|
||||||
|
|
||||||
deduplicate_by_id = merge_component_configs.deduplicate_by_id
|
deduplicate_by_id = merge_component_configs.deduplicate_by_id
|
||||||
|
prepare_component_body = merge_component_configs.prepare_component_body
|
||||||
|
|
||||||
|
|
||||||
def test_identical_duplicate_ids_collapse() -> None:
|
def test_identical_duplicate_ids_collapse() -> None:
|
||||||
@@ -99,3 +102,56 @@ def test_nested_lists_are_checked() -> None:
|
|||||||
}
|
}
|
||||||
with pytest.raises(ValueError, match="dup"):
|
with pytest.raises(ValueError, match="dup"):
|
||||||
deduplicate_by_id(data)
|
deduplicate_by_id(data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_nested_package_includes_are_fully_expanded(tmp_path: Path) -> None:
|
||||||
|
"""A package include that itself pulls in another package expands fully.
|
||||||
|
|
||||||
|
Mirrors web_server's tests, where test.yaml includes common_v2, which
|
||||||
|
includes common (holding the wifi/network config). Without recursive
|
||||||
|
expansion the nested include is dropped and network config is lost.
|
||||||
|
"""
|
||||||
|
(tmp_path / "common.yaml").write_text("wifi:\n ssid: MySSID\n")
|
||||||
|
(tmp_path / "common_v2.yaml").write_text(
|
||||||
|
"packages:\n device_base: !include common.yaml\nweb_server:\n port: 8080\n"
|
||||||
|
)
|
||||||
|
(tmp_path / "test.yaml").write_text(
|
||||||
|
"packages:\n web_server: !include common_v2.yaml\n"
|
||||||
|
"web_server:\n auth:\n username: admin\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
comp_data = yaml_util.load_yaml(tmp_path / "test.yaml")
|
||||||
|
result = prepare_component_body(comp_data, "web_server", tmp_path)
|
||||||
|
|
||||||
|
assert "packages" not in result
|
||||||
|
assert result["wifi"] == {"ssid": "MySSID"}
|
||||||
|
assert result["web_server"] == {"port": 8080, "auth": {"username": "admin"}}
|
||||||
|
|
||||||
|
|
||||||
|
def test_common_bus_package_is_left_for_caller(tmp_path: Path) -> None:
|
||||||
|
"""Common bus packages are not expanded inline; the caller re-adds them."""
|
||||||
|
comp_data = {
|
||||||
|
"packages": {
|
||||||
|
"i2c": {"sda": 21, "scl": 22},
|
||||||
|
"device_base": {"wifi": {"ssid": "MySSID"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result = prepare_component_body(comp_data, "mycomp", tmp_path)
|
||||||
|
|
||||||
|
# The bus package's body must not be merged in, and the packages key is
|
||||||
|
# dropped entirely for the caller to re-add the common bus package.
|
||||||
|
assert "packages" not in result
|
||||||
|
assert "sda" not in result
|
||||||
|
assert result["wifi"] == {"ssid": "MySSID"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_style_packages_are_expanded(tmp_path: Path) -> None:
|
||||||
|
"""List-style package includes are expanded and the key removed."""
|
||||||
|
(tmp_path / "common.yaml").write_text("wifi:\n ssid: MySSID\n")
|
||||||
|
(tmp_path / "test.yaml").write_text("packages:\n - !include common.yaml\n")
|
||||||
|
|
||||||
|
comp_data = yaml_util.load_yaml(tmp_path / "test.yaml")
|
||||||
|
result = prepare_component_body(comp_data, "mycomp", tmp_path)
|
||||||
|
|
||||||
|
assert "packages" not in result
|
||||||
|
assert result["wifi"] == {"ssid": "MySSID"}
|
||||||
|
|||||||
Reference in New Issue
Block a user