mirror of
https://github.com/esphome/esphome.git
synced 2026-05-24 01:37:15 +08:00
[lvgl] Build widget update action schemas lazily (#16569)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
from collections.abc import Callable
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
from esphome import codegen as cg, config_validation as cv
|
||||
from esphome.automation import register_action
|
||||
@@ -15,6 +17,7 @@ from esphome.const import (
|
||||
from esphome.core import ID, EsphomeError, TimePeriod
|
||||
from esphome.coroutine import FakeAwaitable
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.schema_extractors import EnableSchemaExtraction
|
||||
from esphome.types import Expression
|
||||
|
||||
from ..defines import (
|
||||
@@ -73,6 +76,34 @@ from ..types import (
|
||||
EVENT_LAMB = "event_lamb__"
|
||||
|
||||
|
||||
def _build_update_schema(widget_type: "WidgetType") -> Schema:
|
||||
# Local import: ..schemas imports WidgetType from this module.
|
||||
from ..schemas import base_update_schema
|
||||
|
||||
return base_update_schema(widget_type, widget_type.parts).extend(
|
||||
widget_type.modify_schema
|
||||
)
|
||||
|
||||
|
||||
def _update_action_schema(
|
||||
widget_type: "WidgetType",
|
||||
) -> Schema | Callable[[Any], Any]:
|
||||
# Eager when extracting so build_language_schema.py sees the mapping;
|
||||
# lazy otherwise to skip ~200 ms of import-time voluptuous work.
|
||||
if EnableSchemaExtraction:
|
||||
return _build_update_schema(widget_type)
|
||||
|
||||
cached: Schema | None = None
|
||||
|
||||
def validator(value: Any) -> Any:
|
||||
nonlocal cached
|
||||
if cached is None:
|
||||
cached = _build_update_schema(widget_type)
|
||||
return cached(value)
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
class WidgetType:
|
||||
"""
|
||||
Describes a type of Widget, e.g. "bar" or "line"
|
||||
@@ -113,18 +144,17 @@ class WidgetType:
|
||||
|
||||
# Local import to avoid circular import
|
||||
from ..automation import update_to_code
|
||||
from ..schemas import WIDGET_TYPES, base_update_schema
|
||||
from ..schemas import WIDGET_TYPES
|
||||
|
||||
if not is_mock:
|
||||
if self.name in WIDGET_TYPES:
|
||||
raise EsphomeError(f"Duplicate definition of widget type '{self.name}'")
|
||||
WIDGET_TYPES[self.name] = self
|
||||
|
||||
# Register the update action automatically, adding widget-specific properties
|
||||
register_action(
|
||||
f"lvgl.{self.name}.update",
|
||||
ObjUpdateAction,
|
||||
base_update_schema(self, self.parts).extend(self.modify_schema),
|
||||
_update_action_schema(self),
|
||||
synchronous=True,
|
||||
)(update_to_code)
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Tests for lvgl.<widget>.update lazy schema build."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from esphome.automation import ACTION_REGISTRY
|
||||
import esphome.components.lvgl # noqa: F401
|
||||
from esphome.components.lvgl.schemas import WIDGET_TYPES
|
||||
from esphome.components.lvgl.widgets import _update_action_schema
|
||||
from esphome.config_validation import Schema
|
||||
|
||||
|
||||
def _widget_type(name: str = "obj"):
|
||||
wt = WIDGET_TYPES.get(name)
|
||||
assert wt is not None, f"widget type {name!r} not registered"
|
||||
return wt
|
||||
|
||||
|
||||
def test_registry_entry_uses_lazy_validator() -> None:
|
||||
entry = ACTION_REGISTRY["lvgl.label.update"]
|
||||
assert callable(entry.raw_schema)
|
||||
assert not isinstance(entry.raw_schema, Schema)
|
||||
|
||||
|
||||
def test_lazy_validator_defers_build_until_first_call() -> None:
|
||||
wt = _widget_type("label")
|
||||
with patch(
|
||||
"esphome.components.lvgl.widgets._build_update_schema",
|
||||
wraps=lambda w: Schema({}),
|
||||
) as build_mock:
|
||||
validator = _update_action_schema(wt)
|
||||
assert build_mock.call_count == 0
|
||||
validator({})
|
||||
assert build_mock.call_count == 1
|
||||
validator({})
|
||||
assert build_mock.call_count == 1
|
||||
|
||||
|
||||
def test_eager_build_when_schema_extraction_enabled() -> None:
|
||||
wt = _widget_type("label")
|
||||
with patch("esphome.components.lvgl.widgets.EnableSchemaExtraction", True):
|
||||
result = _update_action_schema(wt)
|
||||
assert isinstance(result, Schema)
|
||||
|
||||
|
||||
def test_lazy_and_eager_produce_equivalent_validation() -> None:
|
||||
wt = _widget_type("label")
|
||||
with patch("esphome.components.lvgl.widgets.EnableSchemaExtraction", True):
|
||||
eager = _update_action_schema(wt)
|
||||
lazy = _update_action_schema(wt)
|
||||
sample = {"id": "label_id"}
|
||||
assert lazy(sample) == eager(sample)
|
||||
Reference in New Issue
Block a user