Coverage for lintro / utils / output / parser_registration.py: 96%

46 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Register all parsers at module load time. 

2 

3This module registers all tool parsers with the ParserRegistry, 

4providing O(1) lookup for parser dispatch and fixability predicates. 

5""" 

6 

7# mypy: ignore-errors 

8# Note: mypy errors are suppressed because lintro runs mypy from file's directory, 

9# breaking package resolution. When run properly (mypy lintro/...), this file passes. 

10 

11from __future__ import annotations 

12 

13import json 

14from typing import Any 

15 

16from loguru import logger 

17 

18from lintro.enums.tool_name import ToolName 

19from lintro.parsers.actionlint.actionlint_parser import parse_actionlint_output 

20from lintro.parsers.astro_check.astro_check_parser import parse_astro_check_output 

21from lintro.parsers.bandit.bandit_parser import parse_bandit_output 

22from lintro.parsers.black.black_issue import BlackIssue 

23from lintro.parsers.black.black_parser import parse_black_output 

24from lintro.parsers.clippy.clippy_parser import parse_clippy_output 

25from lintro.parsers.hadolint.hadolint_parser import parse_hadolint_output 

26from lintro.parsers.markdownlint.markdownlint_parser import parse_markdownlint_output 

27from lintro.parsers.mypy.mypy_parser import parse_mypy_output 

28from lintro.parsers.ruff.ruff_format_issue import RuffFormatIssue 

29from lintro.parsers.ruff.ruff_issue import RuffIssue 

30from lintro.parsers.ruff.ruff_parser import parse_ruff_output 

31from lintro.parsers.svelte_check.svelte_check_parser import parse_svelte_check_output 

32from lintro.parsers.vue_tsc.vue_tsc_parser import parse_vue_tsc_output 

33from lintro.parsers.yamllint.yamllint_parser import parse_yamllint_output 

34from lintro.utils.output.parser_registry import ParserRegistry 

35 

36# ----------------------------------------------------------------------------- 

37# Fixability Predicates 

38# ----------------------------------------------------------------------------- 

39 

40 

41def _ruff_is_fixable(issue: object) -> bool: 

42 """Check if a Ruff issue is fixable. 

43 

44 Args: 

45 issue: The issue object to check. 

46 

47 Returns: 

48 True if the issue is fixable (format issue or has fixable attribute). 

49 """ 

50 return isinstance(issue, RuffFormatIssue) or ( 

51 isinstance(issue, RuffIssue) and getattr(issue, "fixable", False) 

52 ) 

53 

54 

55def _black_is_fixable(issue: object) -> bool: 

56 """Check if a Black issue is fixable. 

57 

58 Args: 

59 issue: The issue object to check. 

60 

61 Returns: 

62 True if the issue is a BlackIssue (all Black issues are fixable). 

63 """ 

64 return isinstance(issue, BlackIssue) and getattr(issue, "fixable", True) 

65 

66 

67# ----------------------------------------------------------------------------- 

68# Special Parsers 

69# ----------------------------------------------------------------------------- 

70 

71 

72class ParserError(Exception): 

73 """Exception raised when parsing tool output fails. 

74 

75 This exception is raised instead of silently returning empty results, 

76 allowing callers to distinguish between "no issues found" and "parsing failed". 

77 """ 

78 

79 

80def _parse_bandit_output(output: str) -> list[Any]: 

81 """Parse Bandit output, handling JSON format. 

82 

83 Args: 

84 output: Raw Bandit output (expected to be JSON). 

85 

86 Returns: 

87 List of parsed Bandit issues. 

88 

89 Raises: 

90 ParserError: If the output cannot be parsed as valid JSON. 

91 """ 

92 try: 

93 return parse_bandit_output(bandit_data=json.loads(output)) 

94 except (json.JSONDecodeError, KeyError, TypeError, ValueError) as e: 

95 logger.error(f"Failed to parse Bandit output: {e}") 

96 raise ParserError(f"Failed to parse Bandit output: {e}") from e 

97 

98 

99# ----------------------------------------------------------------------------- 

100# Registration 

101# ----------------------------------------------------------------------------- 

102 

103 

104def register_all_parsers() -> None: 

105 """Register all tool parsers with the registry. 

106 

107 This function should be called once during application initialization 

108 to populate the ParserRegistry with all available tool parsers. 

109 """ 

110 # Ruff - Python linter/formatter 

111 ParserRegistry.register( 

112 ToolName.RUFF.value, 

113 parse_ruff_output, 

114 is_fixable=_ruff_is_fixable, 

115 ) 

116 

117 # Black - Python formatter 

118 ParserRegistry.register( 

119 ToolName.BLACK.value, 

120 parse_black_output, 

121 is_fixable=_black_is_fixable, 

122 ) 

123 

124 # Mypy - Python type checker 

125 ParserRegistry.register( 

126 ToolName.MYPY.value, 

127 parse_mypy_output, 

128 ) 

129 

130 # Actionlint - GitHub Actions linter 

131 ParserRegistry.register( 

132 ToolName.ACTIONLINT.value, 

133 parse_actionlint_output, 

134 ) 

135 

136 # Hadolint - Dockerfile linter 

137 ParserRegistry.register( 

138 ToolName.HADOLINT.value, 

139 parse_hadolint_output, 

140 ) 

141 

142 # Yamllint - YAML linter 

143 ParserRegistry.register( 

144 ToolName.YAMLLINT.value, 

145 parse_yamllint_output, 

146 ) 

147 

148 # Markdownlint - Markdown linter 

149 ParserRegistry.register( 

150 ToolName.MARKDOWNLINT.value, 

151 parse_markdownlint_output, 

152 ) 

153 

154 # Bandit - Python security linter (special JSON handling) 

155 ParserRegistry.register( 

156 ToolName.BANDIT.value, 

157 _parse_bandit_output, 

158 ) 

159 

160 # Clippy - Rust linter 

161 ParserRegistry.register( 

162 ToolName.CLIPPY.value, 

163 parse_clippy_output, 

164 ) 

165 

166 # Astro check - Astro type checker 

167 ParserRegistry.register( 

168 ToolName.ASTRO_CHECK.value, 

169 parse_astro_check_output, 

170 ) 

171 

172 # Svelte-check - Svelte type checker 

173 ParserRegistry.register( 

174 ToolName.SVELTE_CHECK.value, 

175 parse_svelte_check_output, 

176 ) 

177 

178 # Vue-tsc - Vue TypeScript type checker 

179 ParserRegistry.register( 

180 ToolName.VUE_TSC.value, 

181 parse_vue_tsc_output, 

182 ) 

183 

184 

185# Auto-register parsers when module is imported 

186register_all_parsers()