Skip to content

Commit 9459c5a

Browse files
committed
fix: surface subprocess errors when pytest XML is missing
When the test subprocess exits non-zero and produces no JUnit XML, log the return code and stdout/stderr at WARNING level so the root cause is visible in CI logs. Previously this was a generic "No test results found" message that made Windows CI flakes impossible to diagnose. Also fixes pre-existing mypy strict errors in parse_xml.py: - Add return type to _parse_func - Type CompletedProcess[str] (subprocess uses text=True) - Parameterize generic types (tuple, re.Match) - Remove dead .decode() branches (stdout is already str)
1 parent a563e70 commit 9459c5a

1 file changed

Lines changed: 16 additions & 18 deletions

File tree

codeflash/languages/python/parse_xml.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import os
1111
import re
12-
from typing import TYPE_CHECKING
12+
from typing import TYPE_CHECKING, Any
1313

1414
from junitparser.xunit2 import JUnitXml
1515

@@ -48,7 +48,7 @@
4848
)
4949

5050

51-
def _parse_func(file_path: Path):
51+
def _parse_func(file_path: Path) -> Any:
5252
from lxml.etree import XMLParser, parse
5353

5454
xml_parser = XMLParser(huge_tree=True)
@@ -59,13 +59,22 @@ def parse_python_test_xml(
5959
test_xml_file_path: Path,
6060
test_files: TestFiles,
6161
test_config: TestConfig,
62-
run_result: subprocess.CompletedProcess | None = None,
62+
run_result: subprocess.CompletedProcess[str] | None = None,
6363
) -> TestResults:
6464
from codeflash.verification.parse_test_output import resolve_test_file_from_class_path
6565

6666
test_results = TestResults()
6767
if not test_xml_file_path.exists():
68-
logger.warning(f"No test results for {test_xml_file_path} found.")
68+
if run_result is not None and run_result.returncode != 0:
69+
stderr_snippet = (run_result.stderr or "")[:500]
70+
stdout_snippet = (run_result.stdout or "")[:500]
71+
logger.warning(
72+
f"No test results for {test_xml_file_path} found. "
73+
f"Subprocess exited with code {run_result.returncode}.\n"
74+
f"stdout: {stdout_snippet}\nstderr: {stderr_snippet}"
75+
)
76+
else:
77+
logger.warning(f"No test results for {test_xml_file_path} found.")
6978
console.rule()
7079
return test_results
7180
try:
@@ -87,12 +96,7 @@ def parse_python_test_xml(
8796
):
8897
logger.info("Test failed to load, skipping it.")
8998
if run_result is not None:
90-
if isinstance(run_result.stdout, str) and isinstance(run_result.stderr, str):
91-
logger.info(f"Test log - STDOUT : {run_result.stdout} \n STDERR : {run_result.stderr}")
92-
else:
93-
logger.info(
94-
f"Test log - STDOUT : {run_result.stdout.decode()} \n STDERR : {run_result.stderr.decode()}"
95-
)
99+
logger.info(f"Test log - STDOUT : {run_result.stdout} \n STDERR : {run_result.stderr}")
96100
return test_results
97101

98102
test_class_path = testcase.classname
@@ -159,7 +163,7 @@ def parse_python_test_xml(
159163
sys_stdout = testcase.system_out or ""
160164

161165
begin_matches = list(matches_re_start.finditer(sys_stdout))
162-
end_matches: dict[tuple, re.Match] = {}
166+
end_matches: dict[tuple[str, ...], re.Match[str]] = {}
163167
for match in matches_re_end.finditer(sys_stdout):
164168
groups = match.groups()
165169
if len(groups[5].split(":")) > 1:
@@ -234,11 +238,5 @@ def parse_python_test_xml(
234238
f"Tests '{[test_file.original_file_path for test_file in test_files.test_files]}' failed to run, skipping"
235239
)
236240
if run_result is not None:
237-
stdout, stderr = "", ""
238-
try:
239-
stdout = run_result.stdout.decode()
240-
stderr = run_result.stderr.decode()
241-
except AttributeError:
242-
stdout = run_result.stderr
243-
logger.debug(f"Test log - STDOUT : {stdout} \n STDERR : {stderr}")
241+
logger.debug(f"Test log - STDOUT : {run_result.stdout} \n STDERR : {run_result.stderr}")
244242
return test_results

0 commit comments

Comments
 (0)