Coverage for lintro / cli_utils / commands / check.py: 84%

73 statements  

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

1"""Check command implementation for lintro CLI. 

2 

3This module provides the core logic for the 'check' command. 

4 

5Functions: 

6 check_command: CLI command for checking files with various tools. 

7 check: Programmatic function for backward compatibility. 

8""" 

9 

10import sys 

11 

12import click 

13from click.testing import CliRunner 

14 

15from lintro.utils.tool_executor import run_lint_tools_simple 

16 

17# Constants 

18DEFAULT_PATHS: list[str] = ["."] 

19DEFAULT_EXIT_CODE: int = 0 

20DEFAULT_ACTION: str = "check" 

21 

22 

23@click.command("check") 

24@click.argument("paths", nargs=-1, type=click.Path(exists=True)) 

25@click.option( 

26 "--tools", 

27 type=str, 

28 help='Comma-separated list of tools to run. Use "all" to run all available tools.', 

29) 

30@click.option( 

31 "--tool-options", 

32 type=str, 

33 help="Tool-specific options in the format tool:option=value,tool:option=value", 

34) 

35@click.option( 

36 "--exclude", 

37 type=str, 

38 help="Comma-separated list of patterns to exclude from processing", 

39) 

40@click.option( 

41 "--include-venv", 

42 is_flag=True, 

43 help="Include virtual environment directories in processing", 

44) 

45@click.option( 

46 "--output", 

47 type=click.Path(), 

48 help="Output file path for writing results", 

49) 

50@click.option( 

51 "--output-format", 

52 type=click.Choice( 

53 ["plain", "grid", "markdown", "html", "json", "csv", "github", "sarif"], 

54 ), 

55 default="grid", 

56 help="Output format for displaying results", 

57) 

58@click.option( 

59 "--group-by", 

60 type=click.Choice(["file", "code", "none", "auto"]), 

61 default="file", 

62 help="How to group issues in the output", 

63) 

64@click.option( 

65 "--ignore-conflicts", 

66 is_flag=True, 

67 help="Ignore potential conflicts between tools", 

68) 

69@click.option( 

70 "--verbose", 

71 "-v", 

72 is_flag=True, 

73 help="Show verbose output", 

74) 

75@click.option( 

76 "--no-log", 

77 is_flag=True, 

78 hidden=True, 

79 help="Disable logging to file (not yet implemented)", 

80) 

81@click.option( 

82 "--raw-output", 

83 is_flag=True, 

84 help="Show raw tool output instead of formatted output", 

85) 

86@click.option( 

87 "--incremental", 

88 is_flag=True, 

89 help="Only check files that have changed since the last run", 

90) 

91@click.option( 

92 "--no-cache", 

93 is_flag=True, 

94 help="Clear incremental cache before running (forces full check)", 

95) 

96@click.option( 

97 "--stream/--no-stream", 

98 default=False, 

99 hidden=True, 

100 help="Stream tool output in real-time (not yet implemented)", 

101) 

102@click.option( 

103 "--debug", 

104 is_flag=True, 

105 help="Enable debug output on console", 

106) 

107@click.option( 

108 "--auto-install", 

109 is_flag=True, 

110 help="Auto-install Node.js dependencies if node_modules is missing", 

111) 

112@click.option( 

113 "--yes", 

114 "-y", 

115 is_flag=True, 

116 help="Skip confirmation prompt and proceed immediately", 

117) 

118@click.option( 

119 "--fix", 

120 "ai_fix", 

121 is_flag=True, 

122 help="Generate AI fix suggestions (safe fixes auto-apply in CI)", 

123) 

124def check_command( 

125 paths: tuple[str, ...], 

126 tools: str | None, 

127 tool_options: str | None, 

128 exclude: str | None, 

129 include_venv: bool, 

130 output: str | None, 

131 output_format: str, 

132 group_by: str, 

133 ignore_conflicts: bool, 

134 verbose: bool, 

135 no_log: bool, 

136 raw_output: bool, 

137 incremental: bool, 

138 no_cache: bool, 

139 stream: bool, 

140 debug: bool, 

141 auto_install: bool, 

142 yes: bool, 

143 ai_fix: bool, 

144) -> None: 

145 """Check files for issues using the specified tools. 

146 

147 Args: 

148 paths: tuple: List of file/directory paths to check. 

149 tools: str | None: Comma-separated list of tool names to run. 

150 tool_options: str | None: Tool-specific configuration options. 

151 exclude: str | None: Comma-separated patterns of files/dirs to exclude. 

152 include_venv: bool: Whether to include virtual environment directories. 

153 output: str | None: Path to output file for results. 

154 output_format: str: Format for displaying results (table, json, etc). 

155 group_by: str: How to group issues in output (tool, file, etc). 

156 ignore_conflicts: bool: Whether to ignore tool configuration conflicts. 

157 verbose: bool: Whether to show verbose output during execution. 

158 no_log: bool: Whether to disable logging to file. 

159 raw_output: bool: Whether to show raw tool output instead of formatted output. 

160 incremental: bool: Whether to only check files changed since last run. 

161 no_cache: bool: Whether to clear the incremental cache before running. 

162 stream: bool: Whether to stream tool output in real-time. 

163 debug: bool: Whether to enable debug output on console. 

164 auto_install: bool: Whether to auto-install Node.js deps if missing. 

165 yes: bool: Skip confirmation prompt and proceed immediately. 

166 ai_fix: bool: Generate AI fix suggestions with interactive review. 

167 

168 Raises: 

169 SystemExit: Process exit with the aggregated exit code from tools. 

170 """ 

171 # Handle cache clearing 

172 if no_cache: 

173 from lintro.utils.file_cache import clear_all_caches 

174 

175 clear_all_caches() 

176 

177 # Add default paths if none provided 

178 path_list: list[str] = list(paths) if paths else list(DEFAULT_PATHS) 

179 

180 # Build tool-specific options string 

181 tool_option_parts: list[str] = [] 

182 if tool_options: 

183 tool_option_parts.append(tool_options) 

184 

185 combined_tool_options: str | None = ( 

186 ",".join(tool_option_parts) if tool_option_parts else None 

187 ) 

188 

189 # Run with simplified approach 

190 exit_code: int = run_lint_tools_simple( 

191 action=DEFAULT_ACTION, 

192 paths=path_list, 

193 tools=tools, 

194 tool_options=combined_tool_options, 

195 exclude=exclude, 

196 include_venv=include_venv, 

197 group_by=group_by, 

198 output_format=output_format, 

199 verbose=verbose, 

200 raw_output=raw_output, 

201 output_file=output, 

202 incremental=incremental, 

203 debug=debug, 

204 stream=stream, 

205 no_log=no_log, 

206 auto_install=auto_install, 

207 yes=yes, 

208 ai_fix=ai_fix, 

209 ignore_conflicts=ignore_conflicts, 

210 ) 

211 

212 # Exit with code only; CLI uses this as process exit code and avoids any 

213 # additional trailing output after the logger's ASCII art. 

214 raise SystemExit(exit_code) 

215 

216 

217def check( 

218 paths: tuple[str, ...], 

219 tools: str | None, 

220 tool_options: str | None, 

221 exclude: str | None, 

222 include_venv: bool, 

223 output: str | None, 

224 output_format: str, 

225 group_by: str, 

226 ignore_conflicts: bool, 

227 verbose: bool, 

228 no_log: bool, 

229 auto_install: bool = False, 

230 yes: bool = False, 

231 ai_fix: bool = False, 

232) -> None: 

233 """Programmatic check function for backward compatibility. 

234 

235 Args: 

236 paths: tuple: List of file/directory paths to check. 

237 tools: str | None: Comma-separated list of tool names to run. 

238 tool_options: str | None: Tool-specific configuration options. 

239 exclude: str | None: Comma-separated patterns of files/dirs to exclude. 

240 include_venv: bool: Whether to include virtual environment directories. 

241 output: str | None: Path to output file for results. 

242 output_format: str: Format for displaying results (table, json, etc). 

243 group_by: str: How to group issues in output (tool, file, etc). 

244 ignore_conflicts: bool: Whether to ignore tool configuration conflicts. 

245 verbose: bool: Whether to show verbose output during execution. 

246 no_log: bool: Whether to disable logging to file. 

247 auto_install: bool: Whether to auto-install Node.js deps if missing. 

248 yes: bool: Skip confirmation prompt and proceed immediately. 

249 ai_fix: bool: Generate AI fix suggestions with interactive review. 

250 

251 Returns: 

252 None: This function does not return a value. 

253 """ 

254 # Build arguments for the click command 

255 args: list[str] = [] 

256 if paths: 

257 args.extend(list(paths)) 

258 if tools: 

259 args.extend(["--tools", tools]) 

260 if tool_options: 

261 args.extend(["--tool-options", tool_options]) 

262 if exclude: 

263 args.extend(["--exclude", exclude]) 

264 if include_venv: 

265 args.append("--include-venv") 

266 if output: 

267 args.extend(["--output", output]) 

268 if output_format: 

269 args.extend(["--output-format", output_format]) 

270 if group_by: 

271 args.extend(["--group-by", group_by]) 

272 if ignore_conflicts: 

273 args.append("--ignore-conflicts") 

274 if verbose: 

275 args.append("--verbose") 

276 if no_log: 

277 args.append("--no-log") 

278 if auto_install: 

279 args.append("--auto-install") 

280 if yes: 

281 args.append("--yes") 

282 if ai_fix: 

283 args.append("--fix") 

284 

285 runner = CliRunner() 

286 result = runner.invoke(check_command, args) 

287 

288 if result.exit_code != DEFAULT_EXIT_CODE: 

289 sys.exit(result.exit_code) 

290 return None