mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Improve formatting of config validation errors (#103957)
* Improve formatting of config validation errors * Address review comments
This commit is contained in:
parent
85eac5a1b1
commit
94a2087ba0
@ -17,7 +17,7 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
from awesomeversion import AwesomeVersion
|
from awesomeversion import AwesomeVersion
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import MAX_VALIDATION_ERROR_ITEM_LENGTH
|
||||||
|
|
||||||
from . import auth
|
from . import auth
|
||||||
from .auth import mfa_modules as auth_mfa_modules, providers as auth_providers
|
from .auth import mfa_modules as auth_mfa_modules, providers as auth_providers
|
||||||
@ -578,6 +578,47 @@ def find_annotation(
|
|||||||
return find_annotation_rec(config, list(path), None)
|
return find_annotation_rec(config, list(path), None)
|
||||||
|
|
||||||
|
|
||||||
|
def stringify_invalid(ex: vol.Invalid) -> str:
|
||||||
|
"""Stringify voluptuous.Invalid.
|
||||||
|
|
||||||
|
Based on voluptuous.error.Invalid.__str__, the main modification
|
||||||
|
is to format the path delimited by -> instead of @data[].
|
||||||
|
"""
|
||||||
|
path = "->".join(str(m) for m in ex.path)
|
||||||
|
# This function is an alternative to the stringification done by
|
||||||
|
# vol.Invalid.__str__, so we need to call Exception.__str__ here
|
||||||
|
# instead of str(ex)
|
||||||
|
output = Exception.__str__(ex)
|
||||||
|
if error_type := ex.error_type:
|
||||||
|
output += " for " + error_type
|
||||||
|
return f"{output} '{path}'"
|
||||||
|
|
||||||
|
|
||||||
|
def humanize_error(
|
||||||
|
data: Any,
|
||||||
|
validation_error: vol.Invalid,
|
||||||
|
max_sub_error_length: int = MAX_VALIDATION_ERROR_ITEM_LENGTH,
|
||||||
|
) -> str:
|
||||||
|
"""Provide a more helpful + complete validation error message.
|
||||||
|
|
||||||
|
This is a modified version of voluptuous.error.Invalid.__str__,
|
||||||
|
the modifications make some minor changes to the formatting.
|
||||||
|
"""
|
||||||
|
if isinstance(validation_error, vol.MultipleInvalid):
|
||||||
|
return "\n".join(
|
||||||
|
sorted(
|
||||||
|
humanize_error(data, sub_error, max_sub_error_length)
|
||||||
|
for sub_error in validation_error.errors
|
||||||
|
)
|
||||||
|
)
|
||||||
|
offending_item_summary = repr(_get_by_path(data, validation_error.path))
|
||||||
|
if len(offending_item_summary) > max_sub_error_length:
|
||||||
|
offending_item_summary = (
|
||||||
|
f"{offending_item_summary[: max_sub_error_length - 3]}..."
|
||||||
|
)
|
||||||
|
return f"{stringify_invalid(validation_error)}, got {offending_item_summary}"
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _format_config_error(
|
def _format_config_error(
|
||||||
ex: Exception, domain: str, config: dict, link: str | None = None
|
ex: Exception, domain: str, config: dict, link: str | None = None
|
||||||
|
@ -204,7 +204,7 @@ async def test_optimistic_states(hass: HomeAssistant, start_ha) -> None:
|
|||||||
{
|
{
|
||||||
"alarm_control_panel": {"platform": "template"},
|
"alarm_control_panel": {"platform": "template"},
|
||||||
},
|
},
|
||||||
"required key not provided @ data['panels']",
|
"required key not provided 'panels'",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
|
@ -83,8 +83,7 @@ async def test_bad_core_config(hass: HomeAssistant) -> None:
|
|||||||
(
|
(
|
||||||
"Invalid config for [homeassistant] at "
|
"Invalid config for [homeassistant] at "
|
||||||
f"{hass.config.path(YAML_CONFIG_FILE)}, line 2: "
|
f"{hass.config.path(YAML_CONFIG_FILE)}, line 2: "
|
||||||
"not a valid value for dictionary value @ data['unit_system']. Got "
|
"not a valid value for dictionary value 'unit_system', got 'bad'."
|
||||||
"'bad'."
|
|
||||||
),
|
),
|
||||||
"homeassistant",
|
"homeassistant",
|
||||||
{"unit_system": "bad"},
|
{"unit_system": "bad"},
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
# name: test_component_config_validation_error[basic]
|
# name: test_component_config_validation_error[basic]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 6: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 6: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 9: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 9: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 12: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
||||||
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 19: required key not provided @ data['adr_0007_2']['host']. Got None.",
|
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 19: required key not provided 'adr_0007_2->host', got None.",
|
||||||
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 24: expected int for dictionary value @ data['adr_0007_3']['port']. Got 'foo'.",
|
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 24: expected int for dictionary value 'adr_0007_3->port', got 'foo'.",
|
||||||
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 29: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/basic/configuration.yaml, line 29: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
# name: test_component_config_validation_error[basic_include]
|
# name: test_component_config_validation_error[basic_include]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 5: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 5: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 8: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 8: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 11: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/iot_domain.yaml, line 11: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
||||||
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/configuration.yaml, line 3: required key not provided @ data['adr_0007_2']['host']. Got None.",
|
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/configuration.yaml, line 3: required key not provided 'adr_0007_2->host', got None.",
|
||||||
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/adr_0007_3.yaml, line 3: expected int for dictionary value @ data['adr_0007_3']['port']. Got 'foo'.",
|
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/basic_include/integrations/adr_0007_3.yaml, line 3: expected int for dictionary value 'adr_0007_3->port', got 'foo'.",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
# name: test_component_config_validation_error[include_dir_list]
|
# name: test_component_config_validation_error[include_dir_list]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_2.yaml, line 2: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_2.yaml, line 2: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_3.yaml, line 3: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_3.yaml, line 3: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_4.yaml, line 3: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_list/iot_domain/iot_domain_4.yaml, line 3: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
# name: test_component_config_validation_error[include_dir_merge_list]
|
# name: test_component_config_validation_error[include_dir_merge_list]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_1.yaml, line 5: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_1.yaml, line 5: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 3: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 3: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 6: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/include_dir_merge_list/iot_domain/iot_domain_2.yaml, line 6: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
# name: test_component_config_validation_error[packages]
|
# name: test_component_config_validation_error[packages]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 11: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 11: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 16: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 16: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 21: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 21: 'no_such_option' is an invalid option for [iot_domain.non_adr_0007], check: no_such_option",
|
||||||
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 28: required key not provided @ data['adr_0007_2']['host']. Got None.",
|
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 28: required key not provided 'adr_0007_2->host', got None.",
|
||||||
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 33: expected int for dictionary value @ data['adr_0007_3']['port']. Got 'foo'.",
|
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 33: expected int for dictionary value 'adr_0007_3->port', got 'foo'.",
|
||||||
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 38: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/packages/configuration.yaml, line 38: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
# name: test_component_config_validation_error[packages_include_dir_named]
|
# name: test_component_config_validation_error[packages_include_dir_named]
|
||||||
list([
|
list([
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 6: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 6: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 9: expected str for dictionary value @ data['option1']. Got 123.",
|
"Invalid config for [iot_domain.non_adr_0007] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 9: expected str for dictionary value 'option1', got 123.",
|
||||||
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 12: required key not provided @ data['platform']. Got None.",
|
"Invalid config for [iot_domain] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/iot_domain.yaml, line 12: required key not provided 'platform', got None.",
|
||||||
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_2.yaml, line 2: required key not provided @ data['adr_0007_2']['host']. Got None.",
|
"Invalid config for [adr_0007_2] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_2.yaml, line 2: required key not provided 'adr_0007_2->host', got None.",
|
||||||
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_3.yaml, line 4: expected int for dictionary value @ data['adr_0007_3']['port']. Got 'foo'.",
|
"Invalid config for [adr_0007_3] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_3.yaml, line 4: expected int for dictionary value 'adr_0007_3->port', got 'foo'.",
|
||||||
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_4.yaml, line 4: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
"Invalid config for [adr_0007_4] at <BASE_PATH>/fixtures/core/config/component_validation/packages_include_dir_named/integrations/adr_0007_4.yaml, line 4: 'no_such_option' is an invalid option for [adr_0007_4], check: adr_0007_4->no_such_option",
|
||||||
])
|
])
|
||||||
# ---
|
# ---
|
||||||
|
Loading…
x
Reference in New Issue
Block a user