[core] Defer voluptuous and bundle imports out of the upload and logs fast path (#18093)

This commit is contained in:
J. Nick Koston
2026-08-05 20:39:19 +00:00
committed by GitHub
parent f2472563f4
commit a2d1f73bca
10 changed files with 150 additions and 61 deletions
+6 -4
View File
@@ -23,6 +23,7 @@ from esphome import const, platform_hooks
from esphome.const import (
ALLOWED_NAME_CHARS,
ARGUMENT_HELP_DEVICE,
BUNDLE_EXTENSION,
CONF_API,
CONF_AUTH,
CONF_BAUD_RATE,
@@ -1701,7 +1702,7 @@ def command_clean(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
from esphome.bundle import BUNDLE_EXTENSION, ConfigBundleCreator
from esphome.bundle import ConfigBundleCreator
creator = ConfigBundleCreator(config)
@@ -2551,10 +2552,11 @@ def run_esphome(argv):
return 0
# Bundle support: if the configuration is a .esphomebundle, extract it
# and rewrite conf_path to the extracted YAML config.
from esphome.bundle import is_bundle_path, prepare_bundle_for_compile
# and rewrite conf_path to the extracted YAML config. The suffix check
# stays inline so the ordinary run never imports esphome.bundle.
if conf_path.name.lower().endswith(BUNDLE_EXTENSION):
from esphome.bundle import prepare_bundle_for_compile
if is_bundle_path(conf_path):
_LOGGER.info("Extracting config bundle %s...", conf_path)
conf_path = prepare_bundle_for_compile(conf_path)
# Update the argument so downstream code sees the extracted path
+1 -6
View File
@@ -20,6 +20,7 @@ from typing import Any
from esphome import const, yaml_util
from esphome.const import (
BUNDLE_EXTENSION,
CONF_ESPHOME,
CONF_EXTERNAL_COMPONENTS,
CONF_INCLUDES,
@@ -35,7 +36,6 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = "bundle"
BUNDLE_EXTENSION = ".esphomebundle.tar.gz"
MANIFEST_FILENAME = "manifest.json"
CURRENT_MANIFEST_VERSION = 1
MAX_DECOMPRESSED_SIZE = 500 * 1024 * 1024 # 500 MB
@@ -755,11 +755,6 @@ def _validate_tar_members(tar: tarfile.TarFile, target_dir: Path) -> None:
)
def is_bundle_path(path: Path) -> bool:
"""Check if a path looks like a bundle file."""
return path.name.lower().endswith(BUNDLE_EXTENSION)
def _add_bytes_to_tar(tar: tarfile.TarFile, name: str, data: bytes) -> None:
"""Add in-memory bytes to a tar archive with deterministic metadata."""
info = tarfile.TarInfo(name=name)
+1
View File
@@ -121,6 +121,7 @@ PLATFORM_RP2040 = Platform.RP2040
PLATFORM_RTL87XX = Platform.RTL87XX
BUNDLE_EXTENSION = ".esphomebundle.tar.gz"
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
SECRETS_FILES = ("secrets.yaml", "secrets.yml")
+6 -1
View File
@@ -14,7 +14,6 @@ from pathlib import Path
from typing import Any
import uuid
from voluptuous import Invalid
import yaml
from yaml import SafeLoader as PurePythonLoader
import yaml.constructor
@@ -254,6 +253,8 @@ class IncludeFile:
if self._content is not _UNSET:
return self._content
if self.has_unresolved_expressions():
from voluptuous import Invalid
raise Invalid(
f"Cannot load include with unresolved substitutions: {self.file}"
)
@@ -339,6 +340,8 @@ def _load_include_candidates(
keepalive: list[Any],
) -> None:
"""Load every filesystem candidate for an unresolved ``IncludeFile``."""
from voluptuous import Invalid
log = _LOGGER.warning if warn_on_unresolved else _LOGGER.debug
candidates = _candidate_include_paths(include)
if not candidates:
@@ -409,6 +412,8 @@ def force_load_include_files(
run on a fresh re-parse where substitutions haven't been applied yet) to
demote it to a debug log.
"""
from voluptuous import Invalid
if _seen is None:
_seen = set()
if _expanded_paths is None: