From d33a0971529b1335dbe8bd3050fe90e35d908c61 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 20 Aug 2026 17:29:47 -0500 Subject: [PATCH] Name every skipped size line, best-effort script compile, unusable idedata returns None --- esphome/build_helpers/idedata.py | 16 +++++++--------- esphome/espidf/size_summary.py | 10 ++++++++-- esphome/platformio/extra_script.py | 6 +++++- tests/unit_tests/build_helpers/test_idedata.py | 4 ++-- tests/unit_tests/test_platformio_extra_script.py | 14 ++++++++++++++ tests/unit_tests/test_platformio_library.py | 4 +--- tests/unit_tests/test_size_summary.py | 15 +++++++++++++++ 7 files changed, 52 insertions(+), 17 deletions(-) diff --git a/esphome/build_helpers/idedata.py b/esphome/build_helpers/idedata.py index e36c95de30..4bd8f41e10 100644 --- a/esphome/build_helpers/idedata.py +++ b/esphome/build_helpers/idedata.py @@ -279,18 +279,16 @@ def load_or_build_idedata( data = idedata_from_build(compile_commands, launcher) data["prog_path"] = str(elf_path) if _is_launcher(data["cxx_path"]): - # Serve the data for this run but never persist a launcher as the - # compiler path (the cache would outlive the timestamp check and - # hide the fault) + # Known-unusable: consumers must not run a launcher as the compiler, + # and a cached copy would outlive the timestamp check _LOGGER.warning( - "compile_commands names the launcher %s as the compiler; " - "not caching idedata", + "compile_commands names the launcher %s as the compiler; no usable idedata", data["cxx_path"], ) - else: - cache.parent.mkdir(parents=True, exist_ok=True) - # Atomic so a crash mid-write cannot leave a truncated cache - write_file(cache, json.dumps(data, indent=2) + "\n") + return None + cache.parent.mkdir(parents=True, exist_ok=True) + # Atomic so a crash mid-write cannot leave a truncated cache + write_file(cache, json.dumps(data, indent=2) + "\n") return data diff --git a/esphome/espidf/size_summary.py b/esphome/espidf/size_summary.py index 572e2b0eff..83791ce797 100644 --- a/esphome/espidf/size_summary.py +++ b/esphome/espidf/size_summary.py @@ -92,10 +92,16 @@ def print_summary(size_json: Path, partitions_csv: Path | None) -> None: if ram_total and ram_used is not None: print_size_line("RAM", ram_used, ram_total) else: - _LOGGER.warning("Skipping RAM summary: no DRAM/DIRAM region in %s", size_json) + _LOGGER.warning( + "Skipping RAM summary: no usable DRAM/DIRAM region in %s", size_json + ) image_size = data.get("image_size") - if image_size is None or partitions_csv is None: + if image_size is None: + _LOGGER.warning("Skipping Flash summary: no image_size in %s", size_json) + return + if partitions_csv is None: + _LOGGER.warning("Skipping Flash summary: no partition table given") return try: app_size = _find_app_partition_size(partitions_csv) diff --git a/esphome/platformio/extra_script.py b/esphome/platformio/extra_script.py index 6b21100031..67f143d2e7 100644 --- a/esphome/platformio/extra_script.py +++ b/esphome/platformio/extra_script.py @@ -182,9 +182,13 @@ def run_extra_script( pio_env=f"esphome_{board_mcu}", pio_platform=pio_platform, ) - code = compile(script_path.read_text(encoding="utf-8"), str(script_path), "exec") old_cwd = Path.cwd() try: + # Inside the try: a SyntaxError or bad encoding in a vendored script + # is just as best-effort as a runtime failure + code = compile( + script_path.read_text(encoding="utf-8"), str(script_path), "exec" + ) os.chdir(library_dir) exec( # noqa: S102 pylint: disable=exec-used code, diff --git a/tests/unit_tests/build_helpers/test_idedata.py b/tests/unit_tests/build_helpers/test_idedata.py index 26fc1d1ee7..c34475724e 100644 --- a/tests/unit_tests/build_helpers/test_idedata.py +++ b/tests/unit_tests/build_helpers/test_idedata.py @@ -449,9 +449,9 @@ def test_load_or_build_idedata_never_caches_a_launcher( data = idedata.load_or_build_idedata( compile_commands, tmp_path / "f.elf", cache ) - assert data["cxx_path"] == "/opt/homebrew/bin/ccache" + assert data is None assert not cache.exists() - assert "not caching idedata" in caplog.text + assert "no usable idedata" in caplog.text def test_load_or_build_idedata_cache_hit_skips_rebuild(tmp_path: Path) -> None: diff --git a/tests/unit_tests/test_platformio_extra_script.py b/tests/unit_tests/test_platformio_extra_script.py index ec62b67f08..a5f5382500 100644 --- a/tests/unit_tests/test_platformio_extra_script.py +++ b/tests/unit_tests/test_platformio_extra_script.py @@ -263,3 +263,17 @@ def test_run_extra_script_keeps_partial_capture(tmp_path, caplog) -> None: ) assert result.libs == ["algobsec"] assert "keeping the partial capture" in caplog.text + + +def test_run_extra_script_syntax_error_is_best_effort(tmp_path, caplog) -> None: + """A vendored script that does not even compile warns and skips instead + of aborting the build.""" + from esphome.platformio.extra_script import run_extra_script + + script = tmp_path / "extra.py" + script.write_text("def broken(:\n") + result = run_extra_script( + script, library_dir=tmp_path, board_mcu="esp32", pio_platform="espressif32" + ) + assert result.libs == [] + assert "keeping the partial capture" in caplog.text diff --git a/tests/unit_tests/test_platformio_library.py b/tests/unit_tests/test_platformio_library.py index 971abcc3f4..4088cac1f8 100644 --- a/tests/unit_tests/test_platformio_library.py +++ b/tests/unit_tests/test_platformio_library.py @@ -555,11 +555,9 @@ def test_join_flag_args_trailing_bare_flag_warns( assert "Ignoring trailing '-l'" in caplog.text -def test_split_flag_entry_non_string_is_clean( # type: ignore[no-untyped-def] -) -> None: +def test_split_flag_entry_non_string_is_clean() -> None: """A dict or number from a third-party manifest fails naming the entry, not with an opaque shlex traceback.""" - from esphome.core import EsphomeError from esphome.platformio.library import split_flag_entry with pytest.raises(EsphomeError, match="Malformed build flag"): diff --git a/tests/unit_tests/test_size_summary.py b/tests/unit_tests/test_size_summary.py index dc937147ad..7a536a6838 100644 --- a/tests/unit_tests/test_size_summary.py +++ b/tests/unit_tests/test_size_summary.py @@ -178,3 +178,18 @@ def test_print_summary_corrupt_json_warns( size_json.write_text("{not json") print_summary(size_json, partitions_csv=None) assert "Skipping size summary" in caplog.text + + +def test_print_summary_missing_flash_inputs_warn( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """Both absent-input paths for the Flash line name their cause.""" + size_json = _write_size_json(tmp_path, _esp32_size_data()) + print_summary(size_json, partitions_csv=None) + assert "no partition table given" in caplog.text + caplog.clear() + data = _esp32_size_data() + data.pop("image_size", None) + size_json = _write_size_json(tmp_path, data) + print_summary(size_json, partitions_csv=tmp_path / "partitions.cssv") + assert "no image_size" in caplog.text