[packages] Allow list instead of dict for packages (#8688)

This commit is contained in:
Clyde Stubbs 2025-05-05 10:26:59 +10:00 committed by GitHub
parent 3ed03edfec
commit a31d8ec309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 63 additions and 33 deletions

View File

@ -24,22 +24,13 @@ DOMAIN = CONF_PACKAGES
def validate_git_package(config: dict):
if CONF_URL not in config:
return config
config = BASE_SCHEMA(config)
new_config = config
for key, conf in config.items():
if CONF_URL in conf:
try:
conf = BASE_SCHEMA(conf)
if CONF_FILE in conf:
new_config[key][CONF_FILES] = [conf[CONF_FILE]]
del new_config[key][CONF_FILE]
except cv.MultipleInvalid as e:
with cv.prepend_path([key]):
raise e
except cv.Invalid as e:
raise cv.Invalid(
"Extra keys not allowed in git based package",
path=[key] + e.path,
) from e
if CONF_FILE in config:
new_config[CONF_FILES] = [config[CONF_FILE]]
del new_config[CONF_FILE]
return new_config
@ -74,8 +65,8 @@ BASE_SCHEMA = cv.All(
cv.Required(CONF_URL): cv.url,
cv.Optional(CONF_USERNAME): cv.string,
cv.Optional(CONF_PASSWORD): cv.string,
cv.Exclusive(CONF_FILE, "files"): validate_yaml_filename,
cv.Exclusive(CONF_FILES, "files"): cv.All(
cv.Exclusive(CONF_FILE, CONF_FILES): validate_yaml_filename,
cv.Exclusive(CONF_FILES, CONF_FILES): cv.All(
cv.ensure_list(
cv.Any(
validate_yaml_filename,
@ -100,14 +91,17 @@ BASE_SCHEMA = cv.All(
cv.has_at_least_one_key(CONF_FILE, CONF_FILES),
)
PACKAGE_SCHEMA = cv.All(
cv.Any(validate_source_shorthand, BASE_SCHEMA, dict), validate_git_package
)
CONFIG_SCHEMA = cv.All(
CONFIG_SCHEMA = cv.Any(
cv.Schema(
{
str: cv.Any(validate_source_shorthand, BASE_SCHEMA, dict),
str: PACKAGE_SCHEMA,
}
),
validate_git_package,
cv.ensure_list(PACKAGE_SCHEMA),
)
@ -183,25 +177,33 @@ def _process_base_package(config: dict) -> dict:
return {"packages": packages}
def _process_package(package_config, config):
recursive_package = package_config
if CONF_URL in package_config:
package_config = _process_base_package(package_config)
if isinstance(package_config, dict):
recursive_package = do_packages_pass(package_config)
config = merge_config(recursive_package, config)
return config
def do_packages_pass(config: dict):
if CONF_PACKAGES not in config:
return config
packages = config[CONF_PACKAGES]
with cv.prepend_path(CONF_PACKAGES):
packages = CONFIG_SCHEMA(packages)
if not isinstance(packages, dict):
if isinstance(packages, dict):
for package_name, package_config in reversed(packages.items()):
with cv.prepend_path(package_name):
config = _process_package(package_config, config)
elif isinstance(packages, list):
for package_config in reversed(packages):
config = _process_package(package_config, config)
else:
raise cv.Invalid(
f"Packages must be a key to value mapping, got {type(packages)} instead"
f"Packages must be a key to value mapping or list, got {type(packages)} instead"
)
for package_name, package_config in reversed(packages.items()):
with cv.prepend_path(package_name):
recursive_package = package_config
if CONF_URL in package_config:
package_config = _process_base_package(package_config)
if isinstance(package_config, dict):
recursive_package = do_packages_pass(package_config)
config = merge_config(recursive_package, config)
del config[CONF_PACKAGES]
return config

View File

@ -76,10 +76,11 @@ def test_package_unused(basic_esphome, basic_wifi):
def test_package_invalid_dict(basic_esphome, basic_wifi):
"""
Ensures an error is raised if packages is not valid.
If a url: key is present, it's expected to be well-formed remote package spec. Ensure an error is raised if not.
Any other simple dict passed as a package will be merged as usual but may fail later validation.
"""
config = {CONF_ESPHOME: basic_esphome, CONF_PACKAGES: basic_wifi}
config = {CONF_ESPHOME: basic_esphome, CONF_PACKAGES: basic_wifi | {CONF_URL: ""}}
with pytest.raises(cv.Invalid):
do_packages_pass(config)

View File

@ -0,0 +1,3 @@
sensor:
- platform: template
id: package_sensor

View File

@ -0,0 +1,11 @@
packages:
- sensor:
- platform: template
id: inline_sensor
- !include package.yaml
- github://esphome/esphome/tests/components/template/common.yaml@dev
- url: https://github.com/esphome/esphome
file: tests/components/binary_sensor_map/common.yaml
ref: dev
refresh: 1d

View File

@ -0,0 +1,13 @@
packages:
sensor:
sensor:
- platform: template
id: inline_sensor
local: !include package.yaml
shorthand: github://esphome/esphome/tests/components/template/common.yaml@dev
github:
url: https://github.com/esphome/esphome
file: tests/components/binary_sensor_map/common.yaml
ref: dev
refresh: 1d