_lazy_update_schema, container_schema, and their inner build() /
validator() closures all returned untyped functions. Annotate them so
callers see Callable[[Any], Any] for the validator factories and Schema
for the inner builds.
Addresses two stylistic notes on the PR:
- Move `from ..helpers import lazy_once` to module scope in
widgets/__init__.py. helpers.py only depends on esphome and
esphome.const, so there's no circular-import risk that would justify
the function-local import. Keep the `from ..schemas import
base_update_schema` deferred because schemas.py imports WidgetType
from this module.
- Document that lazy_once is single-threaded and retries build() on
exception (no negative-cache), so future callers know the contract
if the helper migrates to esphome/core/helpers.py.
Both container_schema and _lazy_update_schema implement the same
"build the voluptuous schema on first call and cache the result"
closure. Factor it out into lazy_once() in helpers.py so the pattern
only lives in one place.
No behavioral change; lvgl test configs still pass and import time is
unchanged from the previous commit (~215ms cold on M-series Mac).
Voluptuous schema construction inside container_schema() and the
register_action() chain in WidgetType.__init__ dominates cold-validate
time for any YAML that imports the lvgl component. cProfile of a cold
`import esphome.components.lvgl` shows ~225ms cumulative inside
container_schema (9 callers) and ~196ms cumulative inside
base_update_schema (30 callers, one per widget type), out of ~400ms
total.
Defer both: container_schema now returns a validator that builds its
underlying schema on first call, and WidgetType registers a lazy
validator with register_action so base_update_schema is only evaluated
when an `lvgl.<widget>.update` action is actually validated.
Measured on M-series Mac, fresh subprocess, 5 cold imports:
Before: 389, 385, 389, 384, 380 ms (avg 385ms)
After: 229, 217, 218, 215, 215 ms (avg 219ms)
A 43% reduction in `import esphome.components.lvgl`. Real
`esphome -q config` on tests/components/lvgl/test.host.yaml drops from
~770ms to ~550ms (warm-cache subprocess). Extrapolated to slower
hardware (Celeron N5105 dashboard hosts) this is ~1.5s saved per cold
LVGL save.
Schema output is unchanged: lvgl test configs validate identically and
`script/build_language_schema.py` produces a byte-identical lvgl
sub-tree before vs after.