Merge branch 'esp8266-native-library-backend' into esp8266-native-build-spec

This commit is contained in:
J. Nick Koston
2026-08-22 21:28:28 -05:00
7 changed files with 63 additions and 4 deletions
+4
View File
@@ -16,7 +16,11 @@ def tools_cache_path(env_var: str, subdir: str) -> Path:
from esphome.helpers import get_str_env
if prefix := get_str_env(env_var, "").strip():
# resolve(): symlinked prefixes otherwise trip idf.py's
# venv-mismatch warning on every build
return Path(prefix).expanduser().resolve()
# appauthor=False keeps the Windows path short (no vendor segment);
# deep IDF trees run into MAX_PATH otherwise
return (
Path(platformdirs.user_cache_dir("esphome", appauthor=False)) / subdir
).resolve()
+3 -2
View File
@@ -31,8 +31,9 @@ SockAddr = IPv4SockAddr | IPv6SockAddr
_LOGGER = logging.getLogger(__name__)
# cv.boolean's closed spelling tables; shared so env-knob parsers cannot
# drift from what configs accept
# cv.boolean's closed spelling tables, shared with the strict env-knob
# parser (build_helpers.ccache.parse_enable_env). The legacy get_bool_env
# below keeps its own laxer table for backward compatibility.
TRUTHY_BOOL_STRINGS = frozenset({"true", "yes", "on", "enable"})
FALSY_BOOL_STRINGS = frozenset({"false", "no", "off", "disable"})
+5
View File
@@ -110,6 +110,11 @@ class _FakeSConsEnv:
def get(self, key: str, default: str | None = None) -> str | None:
return self._vars.get(key, default)
def __getitem__(self, key: str) -> str:
# Scripts also read env["BOARD_MCU"]; without this the broad
# handler would discard every flag the script captured
return self._vars[key]
def Append(self, **kwargs) -> None: # noqa: N802 (SCons API name)
for key, value in kwargs.items():
if key not in _CAPTURED_KEYS:
+8 -2
View File
@@ -211,8 +211,14 @@ def prefetch_packages(
[(entry[0], _fetch(entry)) for entry in pending],
)
for name, err in failures:
# install_package retries this one itself, with a visible bar
_LOGGER.debug("Prefetch of %s failed: %s", name, err)
if isinstance(err, (EsphomeError, OSError)):
# Expected download failures: install_package retries this one
# itself, with a visible bar
_LOGGER.debug("Prefetch of %s failed: %s", name, err)
else:
# Anything else is a programming error that would otherwise
# become a permanent silent no-op
_LOGGER.warning("Prefetch of %s failed: %r", name, err)
def install_package(
@@ -169,6 +169,22 @@ def test_apply_extra_script_callable_target_and_str_flags(tmp_path) -> None:
assert c.data["build"]["flags"] == ["-DBASE=1", "-lesp8266"]
def test_apply_extra_script_subscript_env_read(tmp_path) -> None:
"""Scripts also read env["BOARD_MCU"]; the subscript form must work or
the broad handler discards every flag the script captured."""
(tmp_path / "src").mkdir()
script = tmp_path / "extra.py"
script.write_text("env.Append(LIBS=[env['BOARD_MCU']])\n")
c = IDFComponent("owner/name", "1.0", source=URLSource("http://dummy"))
c.path = tmp_path
c.data = {"build": {"extraScript": "extra.py"}}
apply_extra_script(c, board_mcu=lambda: "esp8266", pio_platform="espressif8266")
assert c.data["build"]["flags"] == ["-lesp8266"]
def test_apply_extra_script_no_script_and_no_flags(tmp_path) -> None:
# No extraScript declared: nothing happens, the target is never resolved
@@ -166,9 +166,13 @@ def caplog_at_info():
handler.emit = records.append
logger = logging.getLogger("esphome.platformio.library")
logger.addHandler(handler)
# The level must actually admit INFO or the no-INFO assertions are vacuous
old_level = logger.level
logger.setLevel(logging.INFO)
try:
yield records
finally:
logger.setLevel(old_level)
logger.removeHandler(handler)
@@ -625,3 +625,26 @@ def test_prefetch_packages_download_failure_is_debug(
assert mock_download.call_count == 2
assert "Prefetch of a failed" in caplog.text
assert "Prefetch of b failed" in caplog.text
def test_prefetch_packages_unexpected_failure_warns(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
"""A programming error (not a download failure) surfaces at WARNING
instead of becoming a permanent silent no-op."""
with (
patch.object(
registry, "download_with_resume", side_effect=TypeError("bad call")
),
patch.object(
registry, "registry_download", side_effect=_resolve_for({"a": 10, "b": 20})
),
):
registry.prefetch_packages(
[
("a", "1.0", tmp_path / "a", []),
("b", "2.0", tmp_path / "b", []),
],
tmp_path / "dl",
)
assert "TypeError" in caplog.text