[core] Replace voluptuous with probatio for config validation

This commit is contained in:
Franck Nijhof
2026-06-29 11:14:40 +00:00
parent 8e23065b86
commit d1d01e5718
92 changed files with 381 additions and 438 deletions
+7 -7
View File
@@ -1460,7 +1460,7 @@ def test_only_with_framework_suggestion_without_docs_path() -> None:
def test_has_at_least_one_key_not_dict() -> None:
with pytest.raises(Invalid, match="expected dictionary"):
with pytest.raises(Invalid, match="expected a dictionary"):
cv.has_at_least_one_key("a", "b")([])
@@ -1475,17 +1475,17 @@ def test_has_at_least_one_key_ok() -> None:
def test_has_exactly_one_key_not_dict() -> None:
with pytest.raises(Invalid, match="expected dictionary"):
with pytest.raises(Invalid, match="expected a dictionary"):
cv.has_exactly_one_key("a", "b")("notdict")
def test_has_exactly_one_key_too_many() -> None:
with pytest.raises(Invalid, match="Cannot specify more than one"):
with pytest.raises(Invalid, match="exactly one of"):
cv.has_exactly_one_key("a", "b")({"a": 1, "b": 2})
def test_has_exactly_one_key_too_few() -> None:
with pytest.raises(Invalid, match="Must contain exactly one"):
with pytest.raises(Invalid, match="exactly one of"):
cv.has_exactly_one_key("a", "b")({"c": 1})
@@ -1495,12 +1495,12 @@ def test_has_exactly_one_key_ok() -> None:
def test_has_at_most_one_key_not_dict() -> None:
with pytest.raises(Invalid, match="expected dictionary"):
with pytest.raises(Invalid, match="expected a dictionary"):
cv.has_at_most_one_key("a", "b")(5)
def test_has_at_most_one_key_too_many() -> None:
with pytest.raises(vol.MultipleInvalid, match="Cannot specify more than one"):
with pytest.raises(vol.MultipleInvalid, match="at most one of"):
cv.has_at_most_one_key("a", "b")({"a": 1, "b": 2})
@@ -1510,7 +1510,7 @@ def test_has_at_most_one_key_ok() -> None:
def test_has_none_or_all_keys_not_dict() -> None:
with pytest.raises(Invalid, match="expected dictionary"):
with pytest.raises(Invalid, match="expected a dictionary"):
cv.has_none_or_all_keys("a", "b")(5)
+51
View File
@@ -0,0 +1,51 @@
"""The voluptuous compatibility shim for third-party (external) components.
External components import ``voluptuous`` directly. ESPHome installs probatio's
shim at package import (``esphome/__init__.py``), so those imports resolve to
probatio without the components needing any change. These tests pin that contract.
"""
import sys
import probatio
import esphome # noqa: F401 (importing installs the shim)
def test_voluptuous_resolves_to_probatio_shim() -> None:
"""``import voluptuous`` resolves to probatio's shim, not a real voluptuous."""
voluptuous = sys.modules.get("voluptuous")
assert voluptuous is not None
assert voluptuous.__name__ == "probatio._vol_shim"
# Submodules dependencies reach into are aliased too.
assert "voluptuous.schema_builder" in sys.modules
assert hasattr(sys.modules["voluptuous.schema_builder"], "_compile_scalar")
def test_external_component_style_schema_validates() -> None:
"""A schema built the way a third-party component would, through voluptuous."""
import voluptuous as vol # noqa: PLC0415 (mimics an external component)
# Markers imported from voluptuous are probatio markers via the shim.
assert issubclass(vol.Required, probatio.Marker)
schema = vol.Schema(
{
vol.Required("name"): str,
vol.Optional("count", default=1): int,
}
)
assert schema({"name": "x"}) == {"name": "x", "count": 1}
def test_shim_extra_key_is_probatio_error() -> None:
"""An extra key raises probatio's MultipleInvalid through the shim."""
import voluptuous as vol # noqa: PLC0415
schema = vol.Schema({vol.Required("name"): str})
try:
schema({"name": "x", "bogus": 1})
except vol.MultipleInvalid as err:
assert isinstance(err, probatio.MultipleInvalid)
else:
raise AssertionError("expected MultipleInvalid for an extra key")
+6 -3
View File
@@ -1,9 +1,9 @@
"""Tests for voluptuous_schema.py."""
import probatio as vol
import pytest
import voluptuous as vol
from esphome.voluptuous_schema import _Schema
from esphome.voluptuous_schema import ExtraKeysInvalid, _Schema
class TestIdKeyDropping:
@@ -38,8 +38,11 @@ class TestIdKeyDropping:
vol.Required("name"): str,
}
)
with pytest.raises(vol.MultipleInvalid, match="extra keys not allowed"):
with pytest.raises(vol.MultipleInvalid) as exc_info:
schema({"name": "test", "unknown_key": "value"})
# The extra key is reported as an ExtraKeysInvalid (probatio carries the
# close-match candidates on it); assert the type, not the exact wording.
assert any(isinstance(err, ExtraKeysInvalid) for err in exc_info.value.errors)
def test_id_key_not_dropped_when_in_schema(self):
"""When 'id' is declared in the schema, it should be validated normally."""