Fix idf_tools_check parser for ESP-IDF v5.x multi-line output
The v5.x check command uses multi-line blocks per tool instead of single-line "tool version: found" format. Parser now tracks "Checking tool <name>" headings and "version installed in tools directory:" lines. Old single-line format still supported.
This commit is contained in:
parent
877fb273d0
commit
edc1be4cc3
@ -133,15 +133,53 @@ def _parse_tools_check(output: str) -> dict[str, Any]:
|
|||||||
"""Parse ``idf_tools.py check`` output.
|
"""Parse ``idf_tools.py check`` output.
|
||||||
|
|
||||||
Returns dict with ``installed`` and ``missing`` lists.
|
Returns dict with ``installed`` and ``missing`` lists.
|
||||||
|
|
||||||
|
ESP-IDF v5.x uses a multi-line format per tool::
|
||||||
|
|
||||||
|
Checking tool xtensa-esp-elf
|
||||||
|
no version found in PATH
|
||||||
|
version installed in tools directory: esp-13.2.0_20240530
|
||||||
|
|
||||||
|
A tool counts as *installed* when at least one ``version installed``
|
||||||
|
line appears under its heading. It counts as *missing* when there is
|
||||||
|
no such line (or only "no version found" lines).
|
||||||
|
|
||||||
|
The parser also handles the older single-line format
|
||||||
|
``tool version: found`` / ``tool version: not found``.
|
||||||
"""
|
"""
|
||||||
installed: list[str] = []
|
installed: list[str] = []
|
||||||
missing: list[str] = []
|
missing: list[str] = []
|
||||||
|
|
||||||
|
current_tool: str | None = None
|
||||||
|
has_installed_version = False
|
||||||
|
|
||||||
for line in output.splitlines():
|
for line in output.splitlines():
|
||||||
stripped = line.strip()
|
stripped = line.strip()
|
||||||
if not stripped:
|
if not stripped:
|
||||||
continue
|
continue
|
||||||
# Lines look like: "xtensa-esp-elf 14.2.0_20241119: found" or "... not found"
|
|
||||||
|
# Multi-line format: "Checking tool <name>"
|
||||||
|
if stripped.startswith("Checking tool "):
|
||||||
|
# Flush previous tool
|
||||||
|
if current_tool is not None:
|
||||||
|
(installed if has_installed_version else missing).append(current_tool)
|
||||||
|
current_tool = stripped[len("Checking tool "):]
|
||||||
|
has_installed_version = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Multi-line format: indented status lines under a tool heading
|
||||||
|
if current_tool is not None and line[0] in (" ", "\t"):
|
||||||
|
if "version installed in tools directory:" in stripped:
|
||||||
|
version = stripped.split(":", 1)[1].strip()
|
||||||
|
has_installed_version = True
|
||||||
|
# Enrich the tool name with the installed version
|
||||||
|
current_tool_with_ver = f"{current_tool} {version}"
|
||||||
|
# Replace plain name if this is the first version found
|
||||||
|
if has_installed_version and " " not in current_tool:
|
||||||
|
current_tool = current_tool_with_ver
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Single-line format: "xtensa-esp-elf 14.2.0: found" (older IDF)
|
||||||
if ": found" in stripped:
|
if ": found" in stripped:
|
||||||
tool_name = stripped.split(":")[0].strip()
|
tool_name = stripped.split(":")[0].strip()
|
||||||
installed.append(tool_name)
|
installed.append(tool_name)
|
||||||
@ -149,6 +187,10 @@ def _parse_tools_check(output: str) -> dict[str, Any]:
|
|||||||
tool_name = stripped.split(":")[0].strip()
|
tool_name = stripped.split(":")[0].strip()
|
||||||
missing.append(tool_name)
|
missing.append(tool_name)
|
||||||
|
|
||||||
|
# Flush final tool from multi-line parsing
|
||||||
|
if current_tool is not None:
|
||||||
|
(installed if has_installed_version else missing).append(current_tool)
|
||||||
|
|
||||||
return {"installed": installed, "missing": missing}
|
return {"installed": installed, "missing": missing}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -170,7 +170,8 @@ class TestParseToolsList:
|
|||||||
class TestParseToolsCheck:
|
class TestParseToolsCheck:
|
||||||
"""Tests for _parse_tools_check parser."""
|
"""Tests for _parse_tools_check parser."""
|
||||||
|
|
||||||
SAMPLE_OUTPUT = textwrap.dedent("""\
|
# Old single-line format (kept for backwards compat)
|
||||||
|
SAMPLE_OUTPUT_LEGACY = textwrap.dedent("""\
|
||||||
xtensa-esp-elf 14.2.0_20241119: found
|
xtensa-esp-elf 14.2.0_20241119: found
|
||||||
riscv32-esp-elf 14.2.0_20241119: found
|
riscv32-esp-elf 14.2.0_20241119: found
|
||||||
xtensa-esp-elf-gdb 14.2_20240403: found
|
xtensa-esp-elf-gdb 14.2_20240403: found
|
||||||
@ -179,22 +180,42 @@ class TestParseToolsCheck:
|
|||||||
ninja 1.11.1: not found
|
ninja 1.11.1: not found
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Real ESP-IDF v5.3 multi-line format
|
||||||
|
SAMPLE_OUTPUT_V5 = textwrap.dedent("""\
|
||||||
|
Checking for installed tools...
|
||||||
|
Checking tool xtensa-esp-elf-gdb
|
||||||
|
no version found in PATH
|
||||||
|
version installed in tools directory: 14.2_20240403
|
||||||
|
Checking tool riscv32-esp-elf-gdb
|
||||||
|
no version found in PATH
|
||||||
|
version installed in tools directory: 14.2_20240403
|
||||||
|
Checking tool xtensa-esp-elf
|
||||||
|
no version found in PATH
|
||||||
|
version installed in tools directory: esp-13.2.0_20240530
|
||||||
|
Checking tool cmake
|
||||||
|
version found in PATH: 4.2.2
|
||||||
|
version installed in tools directory: 3.24.0
|
||||||
|
Checking tool qemu-riscv32
|
||||||
|
no version found in PATH
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Legacy single-line tests
|
||||||
def test_installed_tools(self):
|
def test_installed_tools(self):
|
||||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||||
assert "xtensa-esp-elf 14.2.0_20241119" in result["installed"]
|
assert "xtensa-esp-elf 14.2.0_20241119" in result["installed"]
|
||||||
assert "riscv32-esp-elf 14.2.0_20241119" in result["installed"]
|
assert "riscv32-esp-elf 14.2.0_20241119" in result["installed"]
|
||||||
|
|
||||||
def test_missing_tools(self):
|
def test_missing_tools(self):
|
||||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||||
assert "cmake 3.24.0" in result["missing"]
|
assert "cmake 3.24.0" in result["missing"]
|
||||||
assert "ninja 1.11.1" in result["missing"]
|
assert "ninja 1.11.1" in result["missing"]
|
||||||
|
|
||||||
def test_installed_count(self):
|
def test_installed_count(self):
|
||||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||||
assert len(result["installed"]) == 4
|
assert len(result["installed"]) == 4
|
||||||
|
|
||||||
def test_missing_count(self):
|
def test_missing_count(self):
|
||||||
result = _parse_tools_check(self.SAMPLE_OUTPUT)
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_LEGACY)
|
||||||
assert len(result["missing"]) == 2
|
assert len(result["missing"]) == 2
|
||||||
|
|
||||||
def test_empty_output(self):
|
def test_empty_output(self):
|
||||||
@ -213,6 +234,34 @@ class TestParseToolsCheck:
|
|||||||
assert len(result["installed"]) == 0
|
assert len(result["installed"]) == 0
|
||||||
assert len(result["missing"]) == 2
|
assert len(result["missing"]) == 2
|
||||||
|
|
||||||
|
# Multi-line format tests (ESP-IDF v5.x)
|
||||||
|
def test_v5_installed_tools(self):
|
||||||
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||||
|
names = [t.split()[0] for t in result["installed"]]
|
||||||
|
assert "xtensa-esp-elf-gdb" in names
|
||||||
|
assert "riscv32-esp-elf-gdb" in names
|
||||||
|
assert "xtensa-esp-elf" in names
|
||||||
|
assert "cmake" in names
|
||||||
|
|
||||||
|
def test_v5_missing_tools(self):
|
||||||
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||||
|
assert "qemu-riscv32" in result["missing"]
|
||||||
|
|
||||||
|
def test_v5_installed_count(self):
|
||||||
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||||
|
assert len(result["installed"]) == 4
|
||||||
|
|
||||||
|
def test_v5_missing_count(self):
|
||||||
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||||
|
assert len(result["missing"]) == 1
|
||||||
|
|
||||||
|
def test_v5_version_included_in_name(self):
|
||||||
|
result = _parse_tools_check(self.SAMPLE_OUTPUT_V5)
|
||||||
|
# Installed tools should include version from "version installed" line
|
||||||
|
installed_str = " ".join(result["installed"])
|
||||||
|
assert "14.2_20240403" in installed_str
|
||||||
|
assert "esp-13.2.0_20240530" in installed_str
|
||||||
|
|
||||||
|
|
||||||
class TestParseExportVars:
|
class TestParseExportVars:
|
||||||
"""Tests for _parse_export_vars parser."""
|
"""Tests for _parse_export_vars parser."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user