mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 12:17:23 +00:00
[lvgl] Build automation_schema event validators lazily (#16633)
This commit is contained in:
@@ -22,6 +22,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import TimePeriod
|
||||
from esphome.core.config import StartupTrigger
|
||||
from esphome.schema_extractors import EnableSchemaExtraction
|
||||
|
||||
from . import defines as df, lv_validation as lvalid
|
||||
from .defines import (
|
||||
@@ -407,7 +408,34 @@ def part_schema(parts: tuple[str, ...] | list[str]) -> cv.Schema:
|
||||
return cv.Schema(part_dict(parts))
|
||||
|
||||
|
||||
def automation_schema(typ: LvType):
|
||||
def _lazy_validate_automation(extra_schema: dict) -> Callable[[Any], Any]:
|
||||
"""Return a validator that defers building the validate_automation schema.
|
||||
|
||||
validate_automation() runs AUTOMATION_SCHEMA.extend(extra_schema), which
|
||||
voluptuous compiles eagerly. automation_schema() builds ~60 of these per
|
||||
widget type, and the vast majority of slots are never invoked by a given
|
||||
user config. Deferring the build to first use removes that work from
|
||||
schema-construction time.
|
||||
|
||||
When EnableSchemaExtraction is set (build_language_schema.py), fall back
|
||||
to eager construction so the @schema_extractor("automation") decoration
|
||||
inside validate_automation is registered.
|
||||
"""
|
||||
if EnableSchemaExtraction:
|
||||
return validate_automation(extra_schema)
|
||||
|
||||
cached: Callable[[Any], Any] | None = None
|
||||
|
||||
def validator(value: Any) -> Any:
|
||||
nonlocal cached
|
||||
if cached is None:
|
||||
cached = validate_automation(extra_schema)
|
||||
return cached(value)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
def automation_schema(typ: LvType) -> dict[Any, Any]:
|
||||
events = df.LV_EVENT_TRIGGERS + df.SWIPE_TRIGGERS
|
||||
if typ.has_on_value:
|
||||
events = events + (CONF_ON_VALUE, CONF_ON_UPDATE)
|
||||
@@ -422,7 +450,7 @@ def automation_schema(typ: LvType):
|
||||
|
||||
return {
|
||||
**{
|
||||
cv.Optional(event): validate_automation(
|
||||
cv.Optional(event): _lazy_validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
Trigger.template(*get_trigger_args(event))
|
||||
@@ -431,7 +459,7 @@ def automation_schema(typ: LvType):
|
||||
)
|
||||
for event in events
|
||||
},
|
||||
cv.Optional(CONF_ON_BOOT): validate_automation(
|
||||
cv.Optional(CONF_ON_BOOT): _lazy_validate_automation(
|
||||
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger)}
|
||||
),
|
||||
}
|
||||
|
||||
71
tests/component_tests/lvgl/test_automation_schema_lazy.py
Normal file
71
tests/component_tests/lvgl/test_automation_schema_lazy.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""Tests for lvgl automation_schema lazy validate_automation build."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import esphome.components.lvgl # noqa: F401
|
||||
from esphome.components.lvgl import schemas as lvgl_schemas
|
||||
from esphome.components.lvgl.schemas import (
|
||||
WIDGET_TYPES,
|
||||
_lazy_validate_automation,
|
||||
automation_schema,
|
||||
)
|
||||
from esphome.components.lvgl.widgets import WidgetType
|
||||
from esphome.config_validation import GenerateID, declare_id
|
||||
from esphome.const import CONF_TRIGGER_ID
|
||||
from esphome.core.config import StartupTrigger
|
||||
|
||||
|
||||
def _widget_type(name: str = "obj") -> WidgetType:
|
||||
wt = WIDGET_TYPES.get(name)
|
||||
assert wt is not None, f"widget type {name!r} not registered"
|
||||
return wt
|
||||
|
||||
|
||||
def _trigger_extra_schema() -> dict:
|
||||
return {GenerateID(CONF_TRIGGER_ID): declare_id(StartupTrigger)}
|
||||
|
||||
|
||||
def test_lazy_validator_defers_build_until_first_call() -> None:
|
||||
with patch(
|
||||
"esphome.components.lvgl.schemas.validate_automation",
|
||||
wraps=lvgl_schemas.validate_automation,
|
||||
) as va_mock:
|
||||
validator = _lazy_validate_automation(_trigger_extra_schema())
|
||||
assert va_mock.call_count == 0
|
||||
validator({"then": []})
|
||||
assert va_mock.call_count == 1
|
||||
validator({"then": []})
|
||||
assert va_mock.call_count == 1
|
||||
|
||||
|
||||
def test_eager_build_when_schema_extraction_enabled() -> None:
|
||||
with (
|
||||
patch("esphome.components.lvgl.schemas.EnableSchemaExtraction", True),
|
||||
patch(
|
||||
"esphome.components.lvgl.schemas.validate_automation",
|
||||
wraps=lvgl_schemas.validate_automation,
|
||||
) as va_mock,
|
||||
):
|
||||
_lazy_validate_automation(_trigger_extra_schema())
|
||||
assert va_mock.call_count == 1
|
||||
|
||||
|
||||
def test_lazy_and_eager_produce_equivalent_validation() -> None:
|
||||
extra = _trigger_extra_schema()
|
||||
with patch("esphome.components.lvgl.schemas.EnableSchemaExtraction", True):
|
||||
eager = _lazy_validate_automation(extra)
|
||||
lazy = _lazy_validate_automation(_trigger_extra_schema())
|
||||
sample = {"then": []}
|
||||
assert lazy(sample) == eager(sample)
|
||||
|
||||
|
||||
def test_automation_schema_uses_lazy_validators() -> None:
|
||||
wt = _widget_type("obj")
|
||||
with patch(
|
||||
"esphome.components.lvgl.schemas.validate_automation",
|
||||
wraps=lvgl_schemas.validate_automation,
|
||||
) as va_mock:
|
||||
automation_schema(wt.w_type)
|
||||
assert va_mock.call_count == 0
|
||||
Reference in New Issue
Block a user