From c3ea6630d3853c1d5fbfe7e514a257bd748aec4e Mon Sep 17 00:00:00 2001 From: Tomasz Duda Date: Mon, 28 Oct 2024 19:29:10 +0100 Subject: [PATCH] is target platform --- esphome/config.py | 9 ++++---- esphome/core/config.py | 52 ++++++++++++++++++++++++++++++++++++++---- esphome/loader.py | 12 ++++++---- 3 files changed, 59 insertions(+), 14 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index 7d48569d2d..ddfe844c17 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -21,7 +21,6 @@ from esphome.const import ( CONF_PACKAGES, CONF_PLATFORM, CONF_SUBSTITUTIONS, - TARGET_PLATFORMS, ) from esphome.core import CORE, DocumentRange, EsphomeError import esphome.core.config as core_config @@ -832,7 +831,7 @@ def validate_config( result[CONF_ESPHOME] = config[CONF_ESPHOME] result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) try: - core_config.preload_core_config(config, result) + target_platform = core_config.preload_core_config(config, result) except vol.Invalid as err: result.add_error(err) return result @@ -840,9 +839,9 @@ def validate_config( result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME) # First run platform validation steps - for key in TARGET_PLATFORMS: - if key in config: - result.add_validation_step(LoadValidationStep(key, config[key])) + result.add_validation_step( + LoadValidationStep(target_platform, config[target_platform]) + ) result.run_validation_steps() if result.errors: diff --git a/esphome/core/config.py b/esphome/core/config.py index 8c130eb6db..923063c694 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -1,6 +1,7 @@ import logging import multiprocessing import os +from pathlib import Path import re from esphome import automation @@ -35,7 +36,6 @@ from esphome.const import ( CONF_VERSION, KEY_CORE, PLATFORM_ESP8266, - TARGET_PLATFORMS, __version__ as ESPHOME_VERSION, ) from esphome.core import CORE, coroutine_with_priority @@ -179,7 +179,7 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( # Compat options, these were moved to target-platform specific sections # but we'll keep these around for a long time because every config would # be impacted - cv.Optional(CONF_PLATFORM): cv.one_of(*TARGET_PLATFORMS, lower=True), + cv.Optional(CONF_PLATFORM): cv.string, cv.Optional(CONF_BOARD): cv.string_strict, cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, @@ -189,7 +189,38 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( ) -def preload_core_config(config, result): +def _is_target_platform(name): + try: + from esphome.loader import get_component + + # we cannot load some components without platform + component = get_component(name, True) + if component.is_target_platform: + return True + except KeyError: + pass + return False + + +def _supported_target_platforms(): + target_platforms = [] + # The root directory of the repo + root = Path(__file__).parent.parent + components_dir = root / "components" + + for path in components_dir.iterdir(): + if not path.is_dir(): + continue + if not (path / "__init__.py").is_file(): + continue + + name = path.name + if _is_target_platform(name): + target_platforms += [name] + return target_platforms + + +def preload_core_config(config, result) -> str: with cv.prepend_path(CONF_ESPHOME): conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) @@ -203,7 +234,12 @@ def preload_core_config(config, result): CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH]) has_oldstyle = CONF_PLATFORM in conf - newstyle_found = [key for key in TARGET_PLATFORMS if key in config] + newstyle_found = [] + + for domain, _ in config.items(): + if _is_target_platform(domain): + newstyle_found += [domain] + oldstyle_opts = [ CONF_ESP8266_RESTORE_FROM_FLASH, CONF_BOARD_FLASH_MODE, @@ -214,7 +250,7 @@ def preload_core_config(config, result): if not has_oldstyle and not newstyle_found: raise cv.Invalid( "Platform missing. You must include one of the available platform keys: " - + ", ".join(TARGET_PLATFORMS), + + ", ".join(_supported_target_platforms()), [CONF_ESPHOME], ) if has_oldstyle and newstyle_found: @@ -238,6 +274,11 @@ def preload_core_config(config, result): if has_oldstyle: plat = conf.pop(CONF_PLATFORM) + if not _is_target_platform(plat): + raise cv.Invalid( + "Platform missing. You must include one of the available platform keys: " + + ", ".join(_supported_target_platforms()) + ) plat_conf = {} if CONF_ESP8266_RESTORE_FROM_FLASH in conf: plat_conf["restore_from_flash"] = conf.pop(CONF_ESP8266_RESTORE_FROM_FLASH) @@ -259,6 +300,7 @@ def preload_core_config(config, result): # Insert generated target platform config to main config config[plat] = plat_conf config[CONF_ESPHOME] = conf + return newstyle_found[0] if newstyle_found else plat def include_file(path, basename): diff --git a/esphome/loader.py b/esphome/loader.py index 65939e66e0..0fb4187b04 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -173,13 +173,15 @@ def install_custom_components_meta_finder(): install_meta_finder(custom_components_dir) -def _lookup_module(domain): +def _lookup_module(domain, exception): if domain in _COMPONENT_CACHE: return _COMPONENT_CACHE[domain] try: module = importlib.import_module(f"esphome.components.{domain}") except ImportError as e: + if exception: + raise if "No module named" in str(e): _LOGGER.info( "Unable to import component %s: %s", domain, str(e), exc_info=False @@ -188,6 +190,8 @@ def _lookup_module(domain): _LOGGER.error("Unable to import component %s:", domain, exc_info=True) return None except Exception: # pylint: disable=broad-except + if exception: + raise _LOGGER.error("Unable to load component %s:", domain, exc_info=True) return None @@ -196,14 +200,14 @@ def _lookup_module(domain): return manif -def get_component(domain): +def get_component(domain, exception=False): assert "." not in domain - return _lookup_module(domain) + return _lookup_module(domain, exception) def get_platform(domain, platform): full = f"{platform}.{domain}" - return _lookup_module(full) + return _lookup_module(full, False) _COMPONENT_CACHE = {}