thailint 0.13.0__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- src/cli/linters/__init__.py +6 -0
- src/cli/linters/code_patterns.py +75 -333
- src/cli/linters/code_smells.py +47 -168
- src/cli/linters/documentation.py +21 -98
- src/cli/linters/performance.py +274 -0
- src/cli/linters/shared.py +232 -6
- src/cli/linters/structure.py +23 -21
- src/cli/linters/structure_quality.py +25 -21
- src/core/linter_utils.py +91 -6
- src/linters/file_header/atemporal_detector.py +54 -40
- src/linters/file_header/config.py +14 -0
- src/linters/lazy_ignores/python_analyzer.py +5 -1
- src/linters/lazy_ignores/types.py +2 -0
- src/linters/method_property/config.py +0 -1
- src/linters/method_property/linter.py +0 -6
- src/linters/nesting/linter.py +11 -6
- src/linters/nesting/violation_builder.py +1 -0
- src/linters/performance/__init__.py +91 -0
- src/linters/performance/config.py +43 -0
- src/linters/performance/constants.py +49 -0
- src/linters/performance/linter.py +149 -0
- src/linters/performance/python_analyzer.py +365 -0
- src/linters/performance/regex_analyzer.py +312 -0
- src/linters/performance/regex_linter.py +139 -0
- src/linters/performance/typescript_analyzer.py +236 -0
- src/linters/performance/violation_builder.py +160 -0
- src/templates/thailint_config_template.yaml +30 -0
- {thailint-0.13.0.dist-info → thailint-0.15.0.dist-info}/METADATA +3 -2
- {thailint-0.13.0.dist-info → thailint-0.15.0.dist-info}/RECORD +32 -22
- {thailint-0.13.0.dist-info → thailint-0.15.0.dist-info}/WHEEL +0 -0
- {thailint-0.13.0.dist-info → thailint-0.15.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.13.0.dist-info → thailint-0.15.0.dist-info}/licenses/LICENSE +0 -0
src/cli/linters/__init__.py
CHANGED
|
@@ -27,6 +27,7 @@ from src.cli.linters import ( # noqa: F401
|
|
|
27
27
|
code_patterns,
|
|
28
28
|
code_smells,
|
|
29
29
|
documentation,
|
|
30
|
+
performance,
|
|
30
31
|
structure,
|
|
31
32
|
structure_quality,
|
|
32
33
|
)
|
|
@@ -39,6 +40,7 @@ from src.cli.linters.code_patterns import (
|
|
|
39
40
|
)
|
|
40
41
|
from src.cli.linters.code_smells import dry, magic_numbers
|
|
41
42
|
from src.cli.linters.documentation import file_header
|
|
43
|
+
from src.cli.linters.performance import perf, regex_in_loop, string_concat_loop
|
|
42
44
|
from src.cli.linters.structure import file_placement, pipeline
|
|
43
45
|
from src.cli.linters.structure_quality import nesting, srp
|
|
44
46
|
|
|
@@ -58,4 +60,8 @@ __all__ = [
|
|
|
58
60
|
"pipeline",
|
|
59
61
|
# Documentation commands
|
|
60
62
|
"file_header",
|
|
63
|
+
# Performance commands
|
|
64
|
+
"perf",
|
|
65
|
+
"string_concat_loop",
|
|
66
|
+
"regex_in_loop",
|
|
61
67
|
]
|
src/cli/linters/code_patterns.py
CHANGED
|
@@ -16,31 +16,15 @@ Exports: print_statements command, method_property command, stateless_class comm
|
|
|
16
16
|
Interfaces: Click CLI commands registered to main CLI group
|
|
17
17
|
|
|
18
18
|
Implementation: Click decorators for command definition, orchestrator-based linting execution
|
|
19
|
-
|
|
20
|
-
SRP Exception: CLI command modules follow Click framework patterns requiring similar command
|
|
21
|
-
structure across all linter commands. This is intentional design for consistency.
|
|
22
|
-
|
|
23
|
-
Suppressions:
|
|
24
|
-
- too-many-arguments,too-many-positional-arguments: Click commands require many parameters by framework design
|
|
25
19
|
"""
|
|
26
|
-
# dry: ignore-block - CLI commands follow Click framework pattern with intentional repetition
|
|
27
20
|
|
|
28
21
|
import logging
|
|
29
22
|
import sys
|
|
30
23
|
from pathlib import Path
|
|
31
24
|
from typing import TYPE_CHECKING, NoReturn
|
|
32
25
|
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
from src.cli.main import cli
|
|
36
|
-
from src.cli.utils import (
|
|
37
|
-
execute_linting_on_paths,
|
|
38
|
-
format_option,
|
|
39
|
-
get_project_root_from_context,
|
|
40
|
-
handle_linting_error,
|
|
41
|
-
setup_base_orchestrator,
|
|
42
|
-
validate_paths_exist,
|
|
43
|
-
)
|
|
26
|
+
from src.cli.linters.shared import ExecuteParams, create_linter_command
|
|
27
|
+
from src.cli.utils import execute_linting_on_paths, setup_base_orchestrator, validate_paths_exist
|
|
44
28
|
from src.core.cli_utils import format_violations
|
|
45
29
|
from src.core.types import Violation
|
|
46
30
|
|
|
@@ -71,90 +55,32 @@ def _run_print_statements_lint(
|
|
|
71
55
|
return [v for v in all_violations if "print-statement" in v.rule_id]
|
|
72
56
|
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
@click.argument("paths", nargs=-1, type=click.Path())
|
|
76
|
-
@click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
|
|
77
|
-
@format_option
|
|
78
|
-
@click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
|
|
79
|
-
@click.pass_context
|
|
80
|
-
def print_statements( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
81
|
-
ctx: click.Context,
|
|
82
|
-
paths: tuple[str, ...],
|
|
83
|
-
config_file: str | None,
|
|
84
|
-
format: str,
|
|
85
|
-
recursive: bool,
|
|
86
|
-
) -> None:
|
|
87
|
-
"""Check for print/console statements in code.
|
|
88
|
-
|
|
89
|
-
Detects print() calls in Python and console.log/warn/error/debug/info calls
|
|
90
|
-
in TypeScript/JavaScript that should be replaced with proper logging.
|
|
91
|
-
|
|
92
|
-
PATHS: Files or directories to lint (defaults to current directory if none provided)
|
|
93
|
-
|
|
94
|
-
Examples:
|
|
95
|
-
|
|
96
|
-
\b
|
|
97
|
-
# Check current directory (all files recursively)
|
|
98
|
-
thai-lint print-statements
|
|
99
|
-
|
|
100
|
-
\b
|
|
101
|
-
# Check specific directory
|
|
102
|
-
thai-lint print-statements src/
|
|
103
|
-
|
|
104
|
-
\b
|
|
105
|
-
# Check single file
|
|
106
|
-
thai-lint print-statements src/app.py
|
|
107
|
-
|
|
108
|
-
\b
|
|
109
|
-
# Check multiple files
|
|
110
|
-
thai-lint print-statements src/app.py src/utils.ts tests/test_app.py
|
|
111
|
-
|
|
112
|
-
\b
|
|
113
|
-
# Get JSON output
|
|
114
|
-
thai-lint print-statements --format json .
|
|
115
|
-
|
|
116
|
-
\b
|
|
117
|
-
# Use custom config file
|
|
118
|
-
thai-lint print-statements --config .thailint.yaml src/
|
|
119
|
-
"""
|
|
120
|
-
verbose: bool = ctx.obj.get("verbose", False)
|
|
121
|
-
project_root = get_project_root_from_context(ctx)
|
|
122
|
-
|
|
123
|
-
if not paths:
|
|
124
|
-
paths = (".",)
|
|
125
|
-
|
|
126
|
-
path_objs = [Path(p) for p in paths]
|
|
127
|
-
|
|
128
|
-
try:
|
|
129
|
-
_execute_print_statements_lint(
|
|
130
|
-
path_objs, config_file, format, recursive, verbose, project_root
|
|
131
|
-
)
|
|
132
|
-
except Exception as e:
|
|
133
|
-
handle_linting_error(e, verbose)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _execute_print_statements_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
137
|
-
path_objs: list[Path],
|
|
138
|
-
config_file: str | None,
|
|
139
|
-
format: str,
|
|
140
|
-
recursive: bool,
|
|
141
|
-
verbose: bool,
|
|
142
|
-
project_root: Path | None = None,
|
|
143
|
-
) -> NoReturn:
|
|
58
|
+
def _execute_print_statements_lint(params: ExecuteParams) -> NoReturn:
|
|
144
59
|
"""Execute print-statements lint."""
|
|
145
|
-
validate_paths_exist(path_objs)
|
|
60
|
+
validate_paths_exist(params.path_objs)
|
|
146
61
|
orchestrator = _setup_print_statements_orchestrator(
|
|
147
|
-
path_objs, config_file, verbose, project_root
|
|
62
|
+
params.path_objs, params.config_file, params.verbose, params.project_root
|
|
63
|
+
)
|
|
64
|
+
print_statements_violations = _run_print_statements_lint(
|
|
65
|
+
orchestrator, params.path_objs, params.recursive
|
|
148
66
|
)
|
|
149
|
-
print_statements_violations = _run_print_statements_lint(orchestrator, path_objs, recursive)
|
|
150
67
|
|
|
151
|
-
if verbose:
|
|
68
|
+
if params.verbose:
|
|
152
69
|
logger.info(f"Found {len(print_statements_violations)} print statement violation(s)")
|
|
153
70
|
|
|
154
|
-
format_violations(print_statements_violations, format)
|
|
71
|
+
format_violations(print_statements_violations, params.format)
|
|
155
72
|
sys.exit(1 if print_statements_violations else 0)
|
|
156
73
|
|
|
157
74
|
|
|
75
|
+
print_statements = create_linter_command(
|
|
76
|
+
"print-statements",
|
|
77
|
+
_execute_print_statements_lint,
|
|
78
|
+
"Check for print/console statements in code.",
|
|
79
|
+
"Detects print() calls in Python and console.log/warn/error/debug/info calls\n"
|
|
80
|
+
" in TypeScript/JavaScript that should be replaced with proper logging.",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
158
84
|
# =============================================================================
|
|
159
85
|
# Method Property Command
|
|
160
86
|
# =============================================================================
|
|
@@ -175,97 +101,33 @@ def _run_method_property_lint(
|
|
|
175
101
|
return [v for v in all_violations if "method-property" in v.rule_id]
|
|
176
102
|
|
|
177
103
|
|
|
178
|
-
|
|
179
|
-
@click.argument("paths", nargs=-1, type=click.Path())
|
|
180
|
-
@click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
|
|
181
|
-
@format_option
|
|
182
|
-
@click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
|
|
183
|
-
@click.pass_context
|
|
184
|
-
def method_property(
|
|
185
|
-
ctx: click.Context,
|
|
186
|
-
paths: tuple[str, ...],
|
|
187
|
-
config_file: str | None,
|
|
188
|
-
format: str,
|
|
189
|
-
recursive: bool,
|
|
190
|
-
) -> None:
|
|
191
|
-
"""Check for methods that should be @property decorators.
|
|
192
|
-
|
|
193
|
-
Detects Python methods that could be converted to properties following
|
|
194
|
-
Pythonic conventions:
|
|
195
|
-
- Methods returning only self._attribute or self.attribute
|
|
196
|
-
- get_* prefixed methods (Java-style getters)
|
|
197
|
-
- Simple computed values with no side effects
|
|
198
|
-
|
|
199
|
-
PATHS: Files or directories to lint (defaults to current directory if none provided)
|
|
200
|
-
|
|
201
|
-
Examples:
|
|
202
|
-
|
|
203
|
-
\b
|
|
204
|
-
# Check current directory (all files recursively)
|
|
205
|
-
thai-lint method-property
|
|
206
|
-
|
|
207
|
-
\b
|
|
208
|
-
# Check specific directory
|
|
209
|
-
thai-lint method-property src/
|
|
210
|
-
|
|
211
|
-
\b
|
|
212
|
-
# Check single file
|
|
213
|
-
thai-lint method-property src/models.py
|
|
214
|
-
|
|
215
|
-
\b
|
|
216
|
-
# Check multiple files
|
|
217
|
-
thai-lint method-property src/models.py src/services.py
|
|
218
|
-
|
|
219
|
-
\b
|
|
220
|
-
# Get JSON output
|
|
221
|
-
thai-lint method-property --format json .
|
|
222
|
-
|
|
223
|
-
\b
|
|
224
|
-
# Get SARIF output for CI/CD integration
|
|
225
|
-
thai-lint method-property --format sarif src/
|
|
226
|
-
|
|
227
|
-
\b
|
|
228
|
-
# Use custom config file
|
|
229
|
-
thai-lint method-property --config .thailint.yaml src/
|
|
230
|
-
"""
|
|
231
|
-
verbose: bool = ctx.obj.get("verbose", False)
|
|
232
|
-
project_root = get_project_root_from_context(ctx)
|
|
233
|
-
|
|
234
|
-
if not paths:
|
|
235
|
-
paths = (".",)
|
|
236
|
-
|
|
237
|
-
path_objs = [Path(p) for p in paths]
|
|
238
|
-
|
|
239
|
-
try:
|
|
240
|
-
_execute_method_property_lint(
|
|
241
|
-
path_objs, config_file, format, recursive, verbose, project_root
|
|
242
|
-
)
|
|
243
|
-
except Exception as e:
|
|
244
|
-
handle_linting_error(e, verbose)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def _execute_method_property_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
248
|
-
path_objs: list[Path],
|
|
249
|
-
config_file: str | None,
|
|
250
|
-
format: str,
|
|
251
|
-
recursive: bool,
|
|
252
|
-
verbose: bool,
|
|
253
|
-
project_root: Path | None = None,
|
|
254
|
-
) -> NoReturn:
|
|
104
|
+
def _execute_method_property_lint(params: ExecuteParams) -> NoReturn:
|
|
255
105
|
"""Execute method-property lint."""
|
|
256
|
-
validate_paths_exist(path_objs)
|
|
106
|
+
validate_paths_exist(params.path_objs)
|
|
257
107
|
orchestrator = _setup_method_property_orchestrator(
|
|
258
|
-
path_objs, config_file, verbose, project_root
|
|
108
|
+
params.path_objs, params.config_file, params.verbose, params.project_root
|
|
109
|
+
)
|
|
110
|
+
method_property_violations = _run_method_property_lint(
|
|
111
|
+
orchestrator, params.path_objs, params.recursive
|
|
259
112
|
)
|
|
260
|
-
method_property_violations = _run_method_property_lint(orchestrator, path_objs, recursive)
|
|
261
113
|
|
|
262
|
-
if verbose:
|
|
114
|
+
if params.verbose:
|
|
263
115
|
logger.info(f"Found {len(method_property_violations)} method-property violation(s)")
|
|
264
116
|
|
|
265
|
-
format_violations(method_property_violations, format)
|
|
117
|
+
format_violations(method_property_violations, params.format)
|
|
266
118
|
sys.exit(1 if method_property_violations else 0)
|
|
267
119
|
|
|
268
120
|
|
|
121
|
+
method_property = create_linter_command(
|
|
122
|
+
"method-property",
|
|
123
|
+
_execute_method_property_lint,
|
|
124
|
+
"Check for methods that should be @property decorators.",
|
|
125
|
+
"Detects Python methods that could be converted to properties following\n"
|
|
126
|
+
" Pythonic conventions: methods returning self._attribute, get_* prefixed\n"
|
|
127
|
+
" methods (Java-style getters), or simple computed values with no side effects.",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
269
131
|
# =============================================================================
|
|
270
132
|
# Stateless Class Command
|
|
271
133
|
# =============================================================================
|
|
@@ -286,95 +148,33 @@ def _run_stateless_class_lint(
|
|
|
286
148
|
return [v for v in all_violations if "stateless-class" in v.rule_id]
|
|
287
149
|
|
|
288
150
|
|
|
289
|
-
|
|
290
|
-
@click.argument("paths", nargs=-1, type=click.Path())
|
|
291
|
-
@click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
|
|
292
|
-
@format_option
|
|
293
|
-
@click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
|
|
294
|
-
@click.pass_context
|
|
295
|
-
def stateless_class(
|
|
296
|
-
ctx: click.Context,
|
|
297
|
-
paths: tuple[str, ...],
|
|
298
|
-
config_file: str | None,
|
|
299
|
-
format: str,
|
|
300
|
-
recursive: bool,
|
|
301
|
-
) -> None:
|
|
302
|
-
"""Check for stateless classes that should be module functions.
|
|
303
|
-
|
|
304
|
-
Detects Python classes that have no constructor (__init__), no instance
|
|
305
|
-
state, and 2+ methods - indicating they should be refactored to module-level
|
|
306
|
-
functions instead of using a class as a namespace.
|
|
307
|
-
|
|
308
|
-
PATHS: Files or directories to lint (defaults to current directory if none provided)
|
|
309
|
-
|
|
310
|
-
Examples:
|
|
311
|
-
|
|
312
|
-
\b
|
|
313
|
-
# Check current directory (all files recursively)
|
|
314
|
-
thai-lint stateless-class
|
|
315
|
-
|
|
316
|
-
\b
|
|
317
|
-
# Check specific directory
|
|
318
|
-
thai-lint stateless-class src/
|
|
319
|
-
|
|
320
|
-
\b
|
|
321
|
-
# Check single file
|
|
322
|
-
thai-lint stateless-class src/utils.py
|
|
323
|
-
|
|
324
|
-
\b
|
|
325
|
-
# Check multiple files
|
|
326
|
-
thai-lint stateless-class src/utils.py src/helpers.py
|
|
327
|
-
|
|
328
|
-
\b
|
|
329
|
-
# Get JSON output
|
|
330
|
-
thai-lint stateless-class --format json .
|
|
331
|
-
|
|
332
|
-
\b
|
|
333
|
-
# Get SARIF output for CI/CD integration
|
|
334
|
-
thai-lint stateless-class --format sarif src/
|
|
335
|
-
|
|
336
|
-
\b
|
|
337
|
-
# Use custom config file
|
|
338
|
-
thai-lint stateless-class --config .thailint.yaml src/
|
|
339
|
-
"""
|
|
340
|
-
verbose: bool = ctx.obj.get("verbose", False)
|
|
341
|
-
project_root = get_project_root_from_context(ctx)
|
|
342
|
-
|
|
343
|
-
if not paths:
|
|
344
|
-
paths = (".",)
|
|
345
|
-
|
|
346
|
-
path_objs = [Path(p) for p in paths]
|
|
347
|
-
|
|
348
|
-
try:
|
|
349
|
-
_execute_stateless_class_lint(
|
|
350
|
-
path_objs, config_file, format, recursive, verbose, project_root
|
|
351
|
-
)
|
|
352
|
-
except Exception as e:
|
|
353
|
-
handle_linting_error(e, verbose)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def _execute_stateless_class_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
357
|
-
path_objs: list[Path],
|
|
358
|
-
config_file: str | None,
|
|
359
|
-
format: str,
|
|
360
|
-
recursive: bool,
|
|
361
|
-
verbose: bool,
|
|
362
|
-
project_root: Path | None = None,
|
|
363
|
-
) -> NoReturn:
|
|
151
|
+
def _execute_stateless_class_lint(params: ExecuteParams) -> NoReturn:
|
|
364
152
|
"""Execute stateless-class lint."""
|
|
365
|
-
validate_paths_exist(path_objs)
|
|
153
|
+
validate_paths_exist(params.path_objs)
|
|
366
154
|
orchestrator = _setup_stateless_class_orchestrator(
|
|
367
|
-
path_objs, config_file, verbose, project_root
|
|
155
|
+
params.path_objs, params.config_file, params.verbose, params.project_root
|
|
156
|
+
)
|
|
157
|
+
stateless_class_violations = _run_stateless_class_lint(
|
|
158
|
+
orchestrator, params.path_objs, params.recursive
|
|
368
159
|
)
|
|
369
|
-
stateless_class_violations = _run_stateless_class_lint(orchestrator, path_objs, recursive)
|
|
370
160
|
|
|
371
|
-
if verbose:
|
|
161
|
+
if params.verbose:
|
|
372
162
|
logger.info(f"Found {len(stateless_class_violations)} stateless-class violation(s)")
|
|
373
163
|
|
|
374
|
-
format_violations(stateless_class_violations, format)
|
|
164
|
+
format_violations(stateless_class_violations, params.format)
|
|
375
165
|
sys.exit(1 if stateless_class_violations else 0)
|
|
376
166
|
|
|
377
167
|
|
|
168
|
+
stateless_class = create_linter_command(
|
|
169
|
+
"stateless-class",
|
|
170
|
+
_execute_stateless_class_lint,
|
|
171
|
+
"Check for stateless classes that should be module functions.",
|
|
172
|
+
"Detects Python classes that have no constructor (__init__), no instance\n"
|
|
173
|
+
" state, and 2+ methods - indicating they should be refactored to module-level\n"
|
|
174
|
+
" functions instead of using a class as a namespace.",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
378
178
|
# =============================================================================
|
|
379
179
|
# Lazy Ignores Command
|
|
380
180
|
# =============================================================================
|
|
@@ -395,86 +195,28 @@ def _run_lazy_ignores_lint(
|
|
|
395
195
|
return [v for v in all_violations if v.rule_id.startswith("lazy-ignores")]
|
|
396
196
|
|
|
397
197
|
|
|
398
|
-
|
|
399
|
-
@click.argument("paths", nargs=-1, type=click.Path())
|
|
400
|
-
@click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
|
|
401
|
-
@format_option
|
|
402
|
-
@click.option("--recursive/--no-recursive", default=True, help="Scan directories recursively")
|
|
403
|
-
@click.pass_context
|
|
404
|
-
def lazy_ignores(
|
|
405
|
-
ctx: click.Context,
|
|
406
|
-
paths: tuple[str, ...],
|
|
407
|
-
config_file: str | None,
|
|
408
|
-
format: str,
|
|
409
|
-
recursive: bool,
|
|
410
|
-
) -> None:
|
|
411
|
-
"""Check for unjustified linting suppressions.
|
|
412
|
-
|
|
413
|
-
Detects ignore directives (noqa, type:ignore, pylint:disable, nosec) that lack
|
|
414
|
-
corresponding entries in the file header's Suppressions section. Enforces a
|
|
415
|
-
header-based suppression model requiring human approval for all linting bypasses.
|
|
416
|
-
|
|
417
|
-
PATHS: Files or directories to lint (defaults to current directory if none provided)
|
|
418
|
-
|
|
419
|
-
Examples:
|
|
420
|
-
|
|
421
|
-
\b
|
|
422
|
-
# Check current directory (all files recursively)
|
|
423
|
-
thai-lint lazy-ignores
|
|
424
|
-
|
|
425
|
-
\b
|
|
426
|
-
# Check specific directory
|
|
427
|
-
thai-lint lazy-ignores src/
|
|
428
|
-
|
|
429
|
-
\b
|
|
430
|
-
# Check single file
|
|
431
|
-
thai-lint lazy-ignores src/routes.py
|
|
432
|
-
|
|
433
|
-
\b
|
|
434
|
-
# Check multiple files
|
|
435
|
-
thai-lint lazy-ignores src/routes.py src/utils.py
|
|
436
|
-
|
|
437
|
-
\b
|
|
438
|
-
# Get JSON output
|
|
439
|
-
thai-lint lazy-ignores --format json .
|
|
440
|
-
|
|
441
|
-
\b
|
|
442
|
-
# Get SARIF output for CI/CD integration
|
|
443
|
-
thai-lint lazy-ignores --format sarif src/
|
|
444
|
-
|
|
445
|
-
\b
|
|
446
|
-
# Use custom config file
|
|
447
|
-
thai-lint lazy-ignores --config .thailint.yaml src/
|
|
448
|
-
"""
|
|
449
|
-
verbose: bool = ctx.obj.get("verbose", False)
|
|
450
|
-
project_root = get_project_root_from_context(ctx)
|
|
451
|
-
|
|
452
|
-
if not paths:
|
|
453
|
-
paths = (".",)
|
|
454
|
-
|
|
455
|
-
path_objs = [Path(p) for p in paths]
|
|
456
|
-
|
|
457
|
-
try:
|
|
458
|
-
_execute_lazy_ignores_lint(path_objs, config_file, format, recursive, verbose, project_root)
|
|
459
|
-
except Exception as e:
|
|
460
|
-
handle_linting_error(e, verbose)
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
def _execute_lazy_ignores_lint( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
464
|
-
path_objs: list[Path],
|
|
465
|
-
config_file: str | None,
|
|
466
|
-
format: str,
|
|
467
|
-
recursive: bool,
|
|
468
|
-
verbose: bool,
|
|
469
|
-
project_root: Path | None = None,
|
|
470
|
-
) -> NoReturn:
|
|
198
|
+
def _execute_lazy_ignores_lint(params: ExecuteParams) -> NoReturn:
|
|
471
199
|
"""Execute lazy-ignores lint."""
|
|
472
|
-
validate_paths_exist(path_objs)
|
|
473
|
-
orchestrator = _setup_lazy_ignores_orchestrator(
|
|
474
|
-
|
|
200
|
+
validate_paths_exist(params.path_objs)
|
|
201
|
+
orchestrator = _setup_lazy_ignores_orchestrator(
|
|
202
|
+
params.path_objs, params.config_file, params.verbose, params.project_root
|
|
203
|
+
)
|
|
204
|
+
lazy_ignores_violations = _run_lazy_ignores_lint(
|
|
205
|
+
orchestrator, params.path_objs, params.recursive
|
|
206
|
+
)
|
|
475
207
|
|
|
476
|
-
if verbose:
|
|
208
|
+
if params.verbose:
|
|
477
209
|
logger.info(f"Found {len(lazy_ignores_violations)} lazy-ignores violation(s)")
|
|
478
210
|
|
|
479
|
-
format_violations(lazy_ignores_violations, format)
|
|
211
|
+
format_violations(lazy_ignores_violations, params.format)
|
|
480
212
|
sys.exit(1 if lazy_ignores_violations else 0)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
lazy_ignores = create_linter_command(
|
|
216
|
+
"lazy-ignores",
|
|
217
|
+
_execute_lazy_ignores_lint,
|
|
218
|
+
"Check for unjustified linting suppressions.",
|
|
219
|
+
"Detects ignore directives (noqa, type:ignore, pylint:disable, nosec) that lack\n"
|
|
220
|
+
" corresponding entries in the file header's Suppressions section. Enforces a\n"
|
|
221
|
+
" header-based suppression model requiring human approval for all linting bypasses.",
|
|
222
|
+
)
|