From bc6b9973508bbc065b2164927440076d32d3a29c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 20 Aug 2026 18:17:56 -0500 Subject: [PATCH] Restore the lazy shlex import, pin the probe convention, assert the shim diagnostics --- esphome/espidf/component.py | 2 +- esphome/platformio/library.py | 6 +++++- esphome/platformio/toolchain.py | 4 ++-- tests/unit_tests/test_platformio_extra_script.py | 10 ++++++++-- tests/unit_tests/test_platformio_toolchain.py | 7 +++++++ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/esphome/espidf/component.py b/esphome/espidf/component.py index 7cefa315e9..07c3dc9dde 100644 --- a/esphome/espidf/component.py +++ b/esphome/espidf/component.py @@ -94,7 +94,7 @@ def generate_cmakelists_txt(component: IDFComponent) -> str: ) # PlatformIO shell-lexes each build.flags entry, so one entry can carry a # flag and its argument (e.g. "-include cp_custom_alloc.h"); bare - # -I/-L/-l tokens re-glue to their argument ("-I foo" -> "-Ifoo") so the + # -I/-L/-l/-D tokens re-glue to their argument ("-I foo" -> "-Ifoo") so # prefix classifiers below still route them. # Joined per entry, as SCons's ParseFlags lexes each string # independently: a dangling -I ending one entry must warn, not absorb diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index 60c1ddda60..e93d9c499f 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -23,7 +23,6 @@ import logging import os from pathlib import Path, PurePosixPath import re -import shlex import tempfile from typing import Any from urllib.parse import urlsplit, urlunsplit @@ -562,6 +561,11 @@ def _resolve_registry_version( def split_flag_entry(entry: Any, owner: str) -> list[str]: """``shlex.split`` with a clean error naming the offending flags entry.""" + # Late import: this module loads with the esp32 platform on every + # validate/compile; shlex (and its linecache pull-in) is only needed + # when actually lexing flags + import shlex + try: return shlex.split(entry) except (ValueError, AttributeError, TypeError) as err: diff --git a/esphome/platformio/toolchain.py b/esphome/platformio/toolchain.py index e8778c19a0..a98ef3e9fe 100644 --- a/esphome/platformio/toolchain.py +++ b/esphome/platformio/toolchain.py @@ -254,8 +254,8 @@ def _ccache_runs(ccache: str) -> bool: stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=15, - # Repo-wide convention (posix_spawn fast path); pinned by - # tests/script/test_helpers.py + # Repo-wide convention (posix_spawn fast path); see the + # close_fds=False call sites across esphome/ and script/helpers.py close_fds=False, ) except (OSError, subprocess.SubprocessError): diff --git a/tests/unit_tests/test_platformio_extra_script.py b/tests/unit_tests/test_platformio_extra_script.py index e1217a4006..3de9ced3d8 100644 --- a/tests/unit_tests/test_platformio_extra_script.py +++ b/tests/unit_tests/test_platformio_extra_script.py @@ -198,10 +198,14 @@ def test_apply_extra_script_no_script_and_no_flags(tmp_path) -> None: assert "flags" not in c.data["build"] -def test_apply_extra_script_ignores_uncaptured_env_calls(tmp_path) -> None: - """Un-captured env vars and unsupported env methods are silent no-ops.""" +def test_apply_extra_script_ignores_uncaptured_env_calls(tmp_path, caplog) -> None: + """Un-captured env vars and unsupported env methods are skipped but + diagnosable from the build log.""" + import logging + from esphome.platformio.extra_script import apply_extra_script + caplog.set_level(logging.DEBUG) script = tmp_path / "extra.py" script.write_text( "env.Replace(CC='clang')\nenv.Append(UNCAPTURED=['x'], LIBS='single')\n" @@ -211,6 +215,8 @@ def test_apply_extra_script_ignores_uncaptured_env_calls(tmp_path) -> None: c.data = {"build": {"extraScript": "extra.py"}} apply_extra_script(c, board_mcu=lambda: "esp8266", pio_platform="espressif8266") assert c.data["build"]["flags"] == ["-lsingle"] + assert "env.Append(UNCAPTURED=...) is not captured" in caplog.text + assert "env.Replace(...) is a no-op here" in caplog.text def test_apply_extra_script_swallows_script_errors(tmp_path, caplog) -> None: diff --git a/tests/unit_tests/test_platformio_toolchain.py b/tests/unit_tests/test_platformio_toolchain.py index 172b288c25..28304270a4 100644 --- a/tests/unit_tests/test_platformio_toolchain.py +++ b/tests/unit_tests/test_platformio_toolchain.py @@ -1977,3 +1977,10 @@ def test_run_platformio_cli_invokes_heal( with patch.object(toolchain, "heal_platformio_python_env") as mock_heal: toolchain.run_platformio_cli("test") mock_heal.assert_called_once() + + +def test_ccache_probe_spawns_with_close_fds_false() -> None: + """The probe follows the repo-wide posix_spawn convention.""" + with patch("subprocess.run") as mock_run: + assert toolchain._ccache_runs("/usr/bin/ccache") is True + assert mock_run.call_args.kwargs["close_fds"] is False