Coverage for lintro / utils / execution / exit_codes.py: 97%
32 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Exit code determination and result aggregation utilities.
3This module provides functions for determining exit codes and aggregating
4tool results from linting operations.
5"""
7from __future__ import annotations
9from lintro.enums.action import Action
10from lintro.models.core.tool_result import ToolResult
12# Constants
13DEFAULT_EXIT_CODE_SUCCESS: int = 0
14DEFAULT_EXIT_CODE_FAILURE: int = 1
15DEFAULT_REMAINING_COUNT: str = "?"
18def determine_exit_code(
19 action: Action,
20 all_results: list[ToolResult],
21 total_issues: int,
22 total_remaining: int,
23 main_phase_empty_due_to_filter: bool,
24) -> int:
25 """Determine final exit code based on results.
27 Args:
28 action: The action performed (check/fix/test).
29 all_results: List of all tool results.
30 total_issues: Total issues found.
31 total_remaining: Remaining issues after fix.
32 main_phase_empty_due_to_filter: Whether main phase was empty due to filtering.
34 Returns:
35 Exit code (0=success, 1=failure).
36 """
37 exit_code = DEFAULT_EXIT_CODE_SUCCESS
39 # Check for tool failures first (applies to all actions)
40 # Exclude skipped tools — they didn't fail, they just didn't run
41 if any(
42 not getattr(r, "success", True)
43 for r in all_results
44 if not getattr(r, "skipped", False)
45 ):
46 exit_code = DEFAULT_EXIT_CODE_FAILURE
48 # Then check for issues based on action
49 if action == Action.FIX:
50 if total_remaining > 0:
51 exit_code = DEFAULT_EXIT_CODE_FAILURE
52 else: # check
53 if total_issues > 0:
54 exit_code = DEFAULT_EXIT_CODE_FAILURE
56 # If all tools were filtered to post-checks but nothing ran, return failure
57 if main_phase_empty_due_to_filter and not all_results:
58 exit_code = DEFAULT_EXIT_CODE_FAILURE
60 return exit_code
63def aggregate_tool_results(
64 results: list[ToolResult],
65 action: Action,
66) -> tuple[int, int, int]:
67 """Aggregate results and compute totals.
69 Args:
70 results: List of tool results to aggregate.
71 action: The action performed (determines which counts to aggregate).
73 Returns:
74 Tuple of (total_issues, total_fixed, total_remaining).
75 """
76 total_issues = 0
77 total_fixed = 0
78 total_remaining = 0
80 for result in results:
81 # Exclude skipped tools from totals
82 if getattr(result, "skipped", False):
83 continue
84 total_issues += getattr(result, "issues_count", 0)
86 if action == Action.FIX:
87 fixed = getattr(result, "fixed_issues_count", None)
88 total_fixed += fixed if fixed is not None else 0
89 remaining = getattr(result, "remaining_issues_count", None)
90 total_remaining += remaining if remaining is not None else 0
92 return total_issues, total_fixed, total_remaining