Polish pre-extraction failure reporting

This commit is contained in:
J. Nick Koston
2026-08-27 20:02:20 -05:00
parent 5da2cca3e7
commit 1fe3475f18
4 changed files with 23 additions and 9 deletions
+10 -3
View File
@@ -28,6 +28,7 @@ from esphome.framework_helpers import (
failure_reason,
get_python_env_executable_path,
get_system_python_path,
is_expected_fetch_error,
resume_fetch_job,
rmdir,
run_batch_downloads,
@@ -297,8 +298,9 @@ def _run_idf_tools_script(
) -> tuple[bool, str | None, str | None]:
"""Run one of the sibling idf_tools-backed helper scripts.
The script is executed with the framework's ``tools`` directory on
PYTHONPATH so it imports the framework's own ``idf_tools`` module.
PYTHONPATH carries this directory (sibling imports like
``_tool_resolution``), the esphome package root (``esphome.helpers``),
and the framework's ``tools`` dir (its own ``idf_tools`` module).
"""
cmd = [
get_system_python_path(),
@@ -829,7 +831,12 @@ def _preinstall_idf_tool_archives(
# surviving torn dir prints its own guidance there
_LOGGER.warning("ESP-IDF tool pre-extraction failed; see above")
except Exception as e: # noqa: BLE001 # pylint: disable=broad-exception-caught
_LOGGER.warning("ESP-IDF tool pre-extraction failed: %s", failure_reason(e))
# A programming error keeps its traceback at WARNING
_LOGGER.warning(
"ESP-IDF tool pre-extraction failed: %s",
failure_reason(e),
exc_info=None if is_expected_fetch_error(e) else e,
)
_LOGGER.debug("Pre-extraction failure detail", exc_info=True)
+3 -2
View File
@@ -1,8 +1,9 @@
"""Print JSON download info for the ESP-IDF tools an install would fetch.
Run via ``python <this file> <idf_framework_root> <targets-csv> <tool-spec>...``.
PYTHONPATH must include ``<idf_framework_root>/tools`` so ``idf_tools`` is
importable, and IDF_TOOLS_PATH must be set. Prints a JSON list of
PYTHONPATH must include this directory (for ``_tool_resolution``) and
``<idf_framework_root>/tools`` (for ``idf_tools``), and IDF_TOOLS_PATH must
be set. Prints a JSON list of
``{name, url, size, sha256, dest}`` for every tool version that is not yet
installed, where ``dest`` is the archive filename ``idf_tools.py install``
expects to find in ``<IDF_TOOLS_PATH>/dist``. Tools with no download for the
+6 -2
View File
@@ -36,7 +36,8 @@ def collect_pending(
for tool, name, version, download in iter_tool_downloads(
targets_csv, tool_specs, on_broken
):
# An archive at its final name was sha256-verified by the prefetch
# Trusted as-is: the prefetch verifies archives at their final name,
# and the installer redoes anything this pass fails on
if (name, version) in pending or not (
dist_path / archive_name(download)
).is_file():
@@ -52,8 +53,11 @@ def install_one(tool: object, name: str, version: str) -> bool | None:
tool.install(version)
# check_binary_valid exits via SystemExit; the installer redoes failures
except (Exception, SystemExit) as e: # noqa: BLE001 # pylint: disable=broad-exception-caught
# Name the type: idf_tools' fatal() raises SystemExit(1), which
# would render as a bare "1"
print(
f"pre-extracting {name}@{version} failed, leaving it to the installer: {e}",
f"pre-extracting {name}@{version} failed, leaving it to the "
f"installer: {type(e).__name__}: {e}",
file=sys.stderr,
)
# A torn dest dir must not look installed to the installer
+4 -2
View File
@@ -2086,7 +2086,8 @@ def test_preinstall_script_failure_only_warns(
def test_preinstall_exception_only_warns(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
"""An unexpected error must not become a new way for the install to fail."""
"""An unexpected error must not become a new way for the install to fail,
and keeps its traceback at WARNING."""
with (
patch(
"esphome.espidf.framework._run_idf_tools_script",
@@ -2095,7 +2096,8 @@ def test_preinstall_exception_only_warns(
patch("esphome.espidf.framework.get_usable_cpu_count", return_value=1),
):
_preinstall_idf_tool_archives(tmp_path, "esp32", ["required"], None)
assert "pre-extraction failed" in caplog.text
record = next(r for r in caplog.records if "pre-extraction failed" in r.message)
assert record.exc_info is not None
# ---------------------------------------------------------------------------