diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index aaac984a99..8627422edd 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -1,3 +1,4 @@ +import functools import re from esphome import automation @@ -112,6 +113,7 @@ HARDWARE_UART_TO_SERIAL = { is_log_level = cv.one_of(*LOG_LEVELS, upper=True) +@functools.cache def _uart_selection_esp32() -> dict[str, list[str]]: from esphome.components.esp32 import ( VARIANT_ESP32, @@ -146,6 +148,7 @@ def _uart_selection_esp32() -> dict[str, list[str]]: } +@functools.cache def _uart_selection_libretiny() -> dict[str, list[str]]: from esphome.components.libretiny.const import ( COMPONENT_BK72XX, @@ -160,16 +163,24 @@ def _uart_selection_libretiny() -> dict[str, list[str]]: } -def __getattr__(name: str): - # These tables are introspected by external tooling (esphome/device-builder); - # built on access so the platform packages stay out of module import. - if name == "UART_SELECTION_ESP32": - return _uart_selection_esp32() - if name == "UART_SELECTION_LIBRETINY": - return _uart_selection_libretiny() +# These tables are introspected by external tooling (esphome/device-builder); +# built on access so the platform packages stay out of module import. +_LAZY_UART_SELECTION_TABLES = { + "UART_SELECTION_ESP32": _uart_selection_esp32, + "UART_SELECTION_LIBRETINY": _uart_selection_libretiny, +} + + +def __getattr__(name: str) -> dict[str, list[str]]: + if (table := _LAZY_UART_SELECTION_TABLES.get(name)) is not None: + return table() raise AttributeError(f"module {__name__!r} has no attribute {name!r}") +def __dir__() -> list[str]: + return [*globals(), *_LAZY_UART_SELECTION_TABLES] + + def uart_selection(value): if CORE.is_esp32: from esphome.components.esp32 import get_esp32_variant diff --git a/tests/unit_tests/components/test_logger.py b/tests/unit_tests/components/test_logger.py new file mode 100644 index 0000000000..a59d57e13e --- /dev/null +++ b/tests/unit_tests/components/test_logger.py @@ -0,0 +1,24 @@ +"""Tests for the logger component's lazy platform tables.""" + +import sys + + +def test_uart_selection_tables_exposed_for_external_tooling() -> None: + """device-builder introspects these names off the module; keep them + accessible (and discoverable) without importing the platform packages.""" + for mod in ("esphome.components.esp32", "esphome.components.libretiny"): + sys.modules.pop(mod, None) + + from esphome.components import logger + + assert "UART_SELECTION_ESP32" in dir(logger) + assert "UART_SELECTION_LIBRETINY" in dir(logger) + assert logger.UART_SELECTION_ESP32["ESP32C3"] == [ + "UART0", + "UART1", + "USB_CDC", + "USB_SERIAL_JTAG", + ] + assert "bk72xx" in logger.UART_SELECTION_LIBRETINY + # Repeated access returns the same cached object + assert logger.UART_SELECTION_ESP32 is logger.UART_SELECTION_ESP32