thailint 0.4.6__py3-none-any.whl → 0.7.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.py +228 -1
- src/core/cli_utils.py +16 -1
- src/core/registry.py +1 -1
- src/formatters/__init__.py +22 -0
- src/formatters/sarif.py +202 -0
- src/linters/file_header/atemporal_detector.py +11 -11
- src/linters/file_header/base_parser.py +89 -0
- src/linters/file_header/bash_parser.py +58 -0
- src/linters/file_header/config.py +76 -16
- src/linters/file_header/css_parser.py +70 -0
- src/linters/file_header/field_validator.py +35 -29
- src/linters/file_header/linter.py +113 -121
- src/linters/file_header/markdown_parser.py +124 -0
- src/linters/file_header/python_parser.py +14 -58
- src/linters/file_header/typescript_parser.py +73 -0
- src/linters/file_header/violation_builder.py +13 -12
- src/linters/file_placement/linter.py +9 -11
- src/linters/magic_numbers/typescript_analyzer.py +1 -0
- src/linters/nesting/typescript_analyzer.py +1 -0
- src/linters/print_statements/__init__.py +53 -0
- src/linters/print_statements/config.py +78 -0
- src/linters/print_statements/linter.py +428 -0
- src/linters/print_statements/python_analyzer.py +149 -0
- src/linters/print_statements/typescript_analyzer.py +130 -0
- src/linters/print_statements/violation_builder.py +96 -0
- src/templates/thailint_config_template.yaml +26 -0
- {thailint-0.4.6.dist-info → thailint-0.7.0.dist-info}/METADATA +149 -3
- {thailint-0.4.6.dist-info → thailint-0.7.0.dist-info}/RECORD +31 -18
- {thailint-0.4.6.dist-info → thailint-0.7.0.dist-info}/WHEEL +0 -0
- {thailint-0.4.6.dist-info → thailint-0.7.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.4.6.dist-info → thailint-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: TypeScript/JavaScript console.* call detection using Tree-sitter AST analysis
|
|
3
|
+
|
|
4
|
+
Scope: TypeScript and JavaScript console statement detection
|
|
5
|
+
|
|
6
|
+
Overview: Analyzes TypeScript and JavaScript code to detect console.* method calls that should
|
|
7
|
+
be replaced with proper logging. Uses Tree-sitter parser to traverse TypeScript/JavaScript
|
|
8
|
+
AST and identify call expressions where the callee is console.log, console.warn, console.error,
|
|
9
|
+
console.debug, or console.info (configurable). Returns structured data with the node, method
|
|
10
|
+
name, and line number for each detected console call. Supports both TypeScript and JavaScript
|
|
11
|
+
files with shared detection logic. Handles member expression pattern matching to identify
|
|
12
|
+
console object method calls.
|
|
13
|
+
|
|
14
|
+
Dependencies: TypeScriptBaseAnalyzer for tree-sitter parsing infrastructure, tree-sitter Node type, logging module
|
|
15
|
+
|
|
16
|
+
Exports: TypeScriptPrintStatementAnalyzer class
|
|
17
|
+
|
|
18
|
+
Interfaces: find_console_calls(root_node, methods) -> list[tuple[Node, str, int]]
|
|
19
|
+
|
|
20
|
+
Implementation: Tree-sitter node traversal with call_expression and member_expression pattern matching
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from src.analyzers.typescript_base import TypeScriptBaseAnalyzer
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
# dry: ignore-block - tree-sitter import pattern (common across TypeScript analyzers)
|
|
31
|
+
try:
|
|
32
|
+
from tree_sitter import Node
|
|
33
|
+
|
|
34
|
+
TREE_SITTER_AVAILABLE = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
TREE_SITTER_AVAILABLE = False
|
|
37
|
+
Node = Any # type: ignore
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TypeScriptPrintStatementAnalyzer(TypeScriptBaseAnalyzer):
|
|
41
|
+
"""Analyzes TypeScript/JavaScript code for console.* calls using Tree-sitter."""
|
|
42
|
+
|
|
43
|
+
def find_console_calls(self, root_node: Node, methods: set[str]) -> list[tuple[Node, str, int]]:
|
|
44
|
+
"""Find all console.* calls matching the specified methods.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
root_node: Root tree-sitter node to search from
|
|
48
|
+
methods: Set of console method names to detect (e.g., {"log", "warn"})
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of (node, method_name, line_number) tuples for each console call
|
|
52
|
+
"""
|
|
53
|
+
logger.debug(
|
|
54
|
+
"find_console_calls: TREE_SITTER_AVAILABLE=%s, root_node=%s",
|
|
55
|
+
TREE_SITTER_AVAILABLE,
|
|
56
|
+
root_node is not None,
|
|
57
|
+
)
|
|
58
|
+
if not TREE_SITTER_AVAILABLE or root_node is None:
|
|
59
|
+
logger.debug("Early return: tree-sitter not available or root_node is None")
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
calls: list[tuple[Node, str, int]] = []
|
|
63
|
+
self._collect_console_calls(root_node, methods, calls)
|
|
64
|
+
logger.debug("find_console_calls: found %d calls", len(calls))
|
|
65
|
+
return calls
|
|
66
|
+
|
|
67
|
+
def _collect_console_calls(
|
|
68
|
+
self, node: Node, methods: set[str], calls: list[tuple[Node, str, int]]
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Recursively collect console.* calls from AST.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
node: Current tree-sitter node
|
|
74
|
+
methods: Set of console method names to detect
|
|
75
|
+
calls: List to accumulate found calls
|
|
76
|
+
"""
|
|
77
|
+
if node.type == "call_expression":
|
|
78
|
+
method_name = self._extract_console_method(node, methods)
|
|
79
|
+
if method_name is not None:
|
|
80
|
+
line_number = node.start_point[0] + 1
|
|
81
|
+
calls.append((node, method_name, line_number))
|
|
82
|
+
|
|
83
|
+
for child in node.children:
|
|
84
|
+
self._collect_console_calls(child, methods, calls)
|
|
85
|
+
|
|
86
|
+
def _extract_console_method(self, node: Node, methods: set[str]) -> str | None:
|
|
87
|
+
"""Extract console method name if this is a console.* call.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
node: Tree-sitter call_expression node
|
|
91
|
+
methods: Set of console method names to detect
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Method name if this is a matching console call, None otherwise
|
|
95
|
+
"""
|
|
96
|
+
func_node = self.find_child_by_type(node, "member_expression")
|
|
97
|
+
if func_node is None:
|
|
98
|
+
return None
|
|
99
|
+
if not self._is_console_object(func_node):
|
|
100
|
+
return None
|
|
101
|
+
return self._get_matching_method(func_node, methods)
|
|
102
|
+
|
|
103
|
+
def _is_console_object(self, func_node: Node) -> bool:
|
|
104
|
+
"""Check if the member expression is on 'console' object."""
|
|
105
|
+
object_node = self._find_object_node(func_node)
|
|
106
|
+
if object_node is None:
|
|
107
|
+
return False
|
|
108
|
+
return self.extract_node_text(object_node) == "console"
|
|
109
|
+
|
|
110
|
+
def _get_matching_method(self, func_node: Node, methods: set[str]) -> str | None:
|
|
111
|
+
"""Get method name if it matches the configured methods."""
|
|
112
|
+
method_node = self.find_child_by_type(func_node, "property_identifier")
|
|
113
|
+
if method_node is None:
|
|
114
|
+
return None
|
|
115
|
+
method_name = self.extract_node_text(method_node)
|
|
116
|
+
return method_name if method_name in methods else None
|
|
117
|
+
|
|
118
|
+
def _find_object_node(self, member_expr: Node) -> Node | None:
|
|
119
|
+
"""Find the object node in a member expression.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
member_expr: Tree-sitter member_expression node
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Object node (identifier) or None
|
|
126
|
+
"""
|
|
127
|
+
for child in member_expr.children:
|
|
128
|
+
if child.type == "identifier":
|
|
129
|
+
return child
|
|
130
|
+
return None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Builds Violation objects for print statement detection
|
|
3
|
+
|
|
4
|
+
Scope: Violation creation for print and console statement detections
|
|
5
|
+
|
|
6
|
+
Overview: Provides ViolationBuilder class that creates Violation objects for print statement
|
|
7
|
+
detections. Generates descriptive messages suggesting the use of proper logging instead of
|
|
8
|
+
print/console statements. Constructs complete Violation instances with rule_id, file_path,
|
|
9
|
+
line number, column, message, and suggestions. Provides separate methods for Python print()
|
|
10
|
+
violations and TypeScript/JavaScript console.* violations with language-appropriate messages
|
|
11
|
+
and helpful remediation guidance.
|
|
12
|
+
|
|
13
|
+
Dependencies: ast module for Python AST nodes, pathlib.Path for file paths,
|
|
14
|
+
src.core.types.Violation for violation structure
|
|
15
|
+
|
|
16
|
+
Exports: ViolationBuilder class
|
|
17
|
+
|
|
18
|
+
Interfaces: create_python_violation(node, line, file_path) -> Violation,
|
|
19
|
+
create_typescript_violation(method, line, file_path) -> Violation
|
|
20
|
+
|
|
21
|
+
Implementation: Builder pattern with message templates suggesting logging as alternative
|
|
22
|
+
to print/console statements
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import ast
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
from src.core.types import Violation
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ViolationBuilder:
|
|
32
|
+
"""Builds violations for print statement detections."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, rule_id: str) -> None:
|
|
35
|
+
"""Initialize the violation builder.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
rule_id: The rule ID to use in violations
|
|
39
|
+
"""
|
|
40
|
+
self.rule_id = rule_id
|
|
41
|
+
|
|
42
|
+
def create_python_violation(
|
|
43
|
+
self,
|
|
44
|
+
node: ast.Call,
|
|
45
|
+
line: int,
|
|
46
|
+
file_path: Path | None,
|
|
47
|
+
) -> Violation:
|
|
48
|
+
"""Create a violation for a Python print() call.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
node: The AST Call node containing the print statement
|
|
52
|
+
line: Line number where the violation occurs
|
|
53
|
+
file_path: Path to the file
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Violation object with details about the print statement
|
|
57
|
+
"""
|
|
58
|
+
message = "print() statement should be replaced with proper logging"
|
|
59
|
+
suggestion = "Use logging.info(), logging.debug(), or similar instead of print()"
|
|
60
|
+
|
|
61
|
+
return Violation(
|
|
62
|
+
rule_id=self.rule_id,
|
|
63
|
+
file_path=str(file_path) if file_path else "",
|
|
64
|
+
line=line,
|
|
65
|
+
column=node.col_offset if hasattr(node, "col_offset") else 0,
|
|
66
|
+
message=message,
|
|
67
|
+
suggestion=suggestion,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def create_typescript_violation(
|
|
71
|
+
self,
|
|
72
|
+
method: str,
|
|
73
|
+
line: int,
|
|
74
|
+
file_path: Path | None,
|
|
75
|
+
) -> Violation:
|
|
76
|
+
"""Create a violation for a TypeScript/JavaScript console.* call.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
method: The console method name (log, warn, error, etc.)
|
|
80
|
+
line: Line number where the violation occurs
|
|
81
|
+
file_path: Path to the file
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Violation object with details about the console statement
|
|
85
|
+
"""
|
|
86
|
+
message = f"console.{method}() should be replaced with proper logging"
|
|
87
|
+
suggestion = f"Use a logging library instead of console.{method}()"
|
|
88
|
+
|
|
89
|
+
return Violation(
|
|
90
|
+
rule_id=self.rule_id,
|
|
91
|
+
file_path=str(file_path) if file_path else "",
|
|
92
|
+
line=line,
|
|
93
|
+
column=0, # Tree-sitter nodes don't provide easy column access
|
|
94
|
+
message=message,
|
|
95
|
+
suggestion=suggestion,
|
|
96
|
+
)
|
|
@@ -106,6 +106,32 @@ file-placement:
|
|
|
106
106
|
required_dir: ["config/", "./"]
|
|
107
107
|
message: "Config files should be in config/ or project root"
|
|
108
108
|
|
|
109
|
+
# ============================================================================
|
|
110
|
+
# PRINT STATEMENTS LINTER
|
|
111
|
+
# ============================================================================
|
|
112
|
+
# Detects print()/console.* statements that should use proper logging
|
|
113
|
+
#
|
|
114
|
+
print-statements:
|
|
115
|
+
enabled: true
|
|
116
|
+
|
|
117
|
+
# Allow print() in if __name__ == "__main__": blocks (Python only)
|
|
118
|
+
# Default: true
|
|
119
|
+
allow_in_scripts: true
|
|
120
|
+
|
|
121
|
+
# Console methods to detect in TypeScript/JavaScript
|
|
122
|
+
# Default: [log, warn, error, debug, info]
|
|
123
|
+
console_methods:
|
|
124
|
+
- log
|
|
125
|
+
- warn
|
|
126
|
+
- error
|
|
127
|
+
- debug
|
|
128
|
+
- info
|
|
129
|
+
|
|
130
|
+
# File patterns to ignore (glob syntax)
|
|
131
|
+
# ignore:
|
|
132
|
+
# - "scripts/**"
|
|
133
|
+
# - "**/debug.py"
|
|
134
|
+
|
|
109
135
|
# ============================================================================
|
|
110
136
|
# GLOBAL SETTINGS
|
|
111
137
|
# ============================================================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thailint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -37,9 +37,10 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
[](https://opensource.org/licenses/MIT)
|
|
39
39
|
[](https://www.python.org/downloads/)
|
|
40
|
-
[](tests/)
|
|
41
41
|
[](htmlcov/)
|
|
42
42
|
[](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
|
|
43
|
+
[](docs/sarif-output.md)
|
|
43
44
|
|
|
44
45
|
The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
|
|
45
46
|
|
|
@@ -73,6 +74,11 @@ thailint complements your existing linting stack by catching the patterns AI too
|
|
|
73
74
|
|
|
74
75
|
### Core Capabilities
|
|
75
76
|
- **File Placement Linting** - Enforce project structure and organization
|
|
77
|
+
- **File Header Linting** - Validate documentation headers in source files
|
|
78
|
+
- Python, TypeScript, JavaScript, Bash, Markdown, CSS support
|
|
79
|
+
- Mandatory field validation (Purpose, Scope, Overview)
|
|
80
|
+
- Atemporal language detection (no dates, "currently", "now")
|
|
81
|
+
- Language-specific header format parsing
|
|
76
82
|
- **Magic Numbers Linting** - Detect unnamed numeric literals that should be constants
|
|
77
83
|
- Python and TypeScript support with AST analysis
|
|
78
84
|
- Context-aware detection (ignores constants, test files, range() usage)
|
|
@@ -122,7 +128,7 @@ cd thai-lint
|
|
|
122
128
|
pip install -e ".[dev]"
|
|
123
129
|
```
|
|
124
130
|
|
|
125
|
-
### From PyPI
|
|
131
|
+
### From PyPI
|
|
126
132
|
|
|
127
133
|
```bash
|
|
128
134
|
pip install thai-lint
|
|
@@ -158,11 +164,17 @@ thailint dry .
|
|
|
158
164
|
# Check for magic numbers
|
|
159
165
|
thailint magic-numbers src/
|
|
160
166
|
|
|
167
|
+
# Check file headers
|
|
168
|
+
thailint file-header src/
|
|
169
|
+
|
|
161
170
|
# With config file
|
|
162
171
|
thailint dry --config .thailint.yaml src/
|
|
163
172
|
|
|
164
173
|
# JSON output for CI/CD
|
|
165
174
|
thailint dry --format json src/
|
|
175
|
+
|
|
176
|
+
# SARIF output for GitHub Code Scanning
|
|
177
|
+
thailint nesting --format sarif src/ > results.sarif
|
|
166
178
|
```
|
|
167
179
|
|
|
168
180
|
**New to thailint?** See the **[Quick Start Guide](https://thai-lint.readthedocs.io/en/latest/quick-start/)** for a complete walkthrough including config generation, understanding output, and next steps.
|
|
@@ -869,6 +881,136 @@ def get_ports(): # thailint: ignore[magic-numbers] - Standard ports
|
|
|
869
881
|
|
|
870
882
|
See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** and **[Magic Numbers Linter Guide](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** for complete documentation.
|
|
871
883
|
|
|
884
|
+
## File Header Linter
|
|
885
|
+
|
|
886
|
+
### Overview
|
|
887
|
+
|
|
888
|
+
The file header linter validates that source files have proper documentation headers containing required fields (Purpose, Scope, Overview) and don't use temporal language (dates, "currently", "now"). It enforces consistent documentation patterns across entire codebases.
|
|
889
|
+
|
|
890
|
+
### Why File Headers?
|
|
891
|
+
|
|
892
|
+
File headers serve as **self-documentation** that helps developers (and AI assistants) quickly understand:
|
|
893
|
+
|
|
894
|
+
- **Purpose**: What does this file do?
|
|
895
|
+
- **Scope**: What area of the system does it cover?
|
|
896
|
+
- **Dependencies**: What does it rely on?
|
|
897
|
+
- **Exports**: What does it provide to other modules?
|
|
898
|
+
|
|
899
|
+
### Quick Start
|
|
900
|
+
|
|
901
|
+
```bash
|
|
902
|
+
# Check file headers in current directory
|
|
903
|
+
thailint file-header .
|
|
904
|
+
|
|
905
|
+
# Check specific directory
|
|
906
|
+
thailint file-header src/
|
|
907
|
+
|
|
908
|
+
# Get JSON output
|
|
909
|
+
thailint file-header --format json src/
|
|
910
|
+
|
|
911
|
+
# Get SARIF output for CI/CD
|
|
912
|
+
thailint file-header --format sarif src/ > results.sarif
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### Configuration
|
|
916
|
+
|
|
917
|
+
Add to `.thailint.yaml`:
|
|
918
|
+
|
|
919
|
+
```yaml
|
|
920
|
+
file-header:
|
|
921
|
+
enabled: true
|
|
922
|
+
mandatory_fields:
|
|
923
|
+
- Purpose
|
|
924
|
+
- Scope
|
|
925
|
+
- Overview
|
|
926
|
+
ignore:
|
|
927
|
+
- "**/__init__.py"
|
|
928
|
+
- "**/migrations/**"
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### Example Violation
|
|
932
|
+
|
|
933
|
+
**Code without proper header:**
|
|
934
|
+
```python
|
|
935
|
+
import os
|
|
936
|
+
|
|
937
|
+
def process_data():
|
|
938
|
+
pass
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Violation messages:**
|
|
942
|
+
```
|
|
943
|
+
src/utils.py:1 - Missing mandatory field: Purpose
|
|
944
|
+
src/utils.py:1 - Missing mandatory field: Scope
|
|
945
|
+
src/utils.py:1 - Missing mandatory field: Overview
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
**Refactored with header:**
|
|
949
|
+
```python
|
|
950
|
+
"""
|
|
951
|
+
Purpose: Data processing utilities for ETL pipeline
|
|
952
|
+
|
|
953
|
+
Scope: Data transformation layer, used by batch processing jobs
|
|
954
|
+
|
|
955
|
+
Overview: Provides data transformation functions for the ETL pipeline.
|
|
956
|
+
Handles parsing, validation, and normalization of incoming data.
|
|
957
|
+
|
|
958
|
+
Dependencies: os, json
|
|
959
|
+
|
|
960
|
+
Exports: process_data(), validate_input(), transform_record()
|
|
961
|
+
"""
|
|
962
|
+
import os
|
|
963
|
+
|
|
964
|
+
def process_data():
|
|
965
|
+
pass
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
### Atemporal Language Detection
|
|
969
|
+
|
|
970
|
+
The linter detects temporal language that becomes stale:
|
|
971
|
+
|
|
972
|
+
**Temporal (flagged):**
|
|
973
|
+
```python
|
|
974
|
+
"""
|
|
975
|
+
Purpose: Authentication module
|
|
976
|
+
|
|
977
|
+
Overview: Currently handles OAuth. This was recently updated.
|
|
978
|
+
Created: 2024-01-15. Will be extended in the future.
|
|
979
|
+
"""
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
**Atemporal (correct):**
|
|
983
|
+
```python
|
|
984
|
+
"""
|
|
985
|
+
Purpose: Authentication module
|
|
986
|
+
|
|
987
|
+
Overview: Handles OAuth authentication with Google and GitHub.
|
|
988
|
+
Implements authorization code flow with PKCE for security.
|
|
989
|
+
"""
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
### Language Support
|
|
993
|
+
|
|
994
|
+
- **Python**: Module docstrings (`"""..."""`)
|
|
995
|
+
- **TypeScript/JavaScript**: JSDoc comments (`/** ... */`)
|
|
996
|
+
- **Bash**: Hash comments after shebang (`# ...`)
|
|
997
|
+
- **Markdown**: YAML frontmatter (`---...---`)
|
|
998
|
+
- **CSS/SCSS**: Block comments (`/* ... */`)
|
|
999
|
+
|
|
1000
|
+
### Ignoring Violations
|
|
1001
|
+
|
|
1002
|
+
```python
|
|
1003
|
+
# File-level ignore
|
|
1004
|
+
# thailint: ignore-file[file-header]
|
|
1005
|
+
|
|
1006
|
+
# Line-level ignore for atemporal violation
|
|
1007
|
+
"""
|
|
1008
|
+
Overview: Created 2024-01-15. # thailint: ignore[file-header]
|
|
1009
|
+
"""
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** and **[File Header Linter Guide](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** for complete documentation.
|
|
1013
|
+
|
|
872
1014
|
## Pre-commit Hooks
|
|
873
1015
|
|
|
874
1016
|
Automate code quality checks before every commit and push with pre-commit hooks.
|
|
@@ -1153,11 +1295,13 @@ docker run --rm -v /path/to/workspace:/workspace \
|
|
|
1153
1295
|
- **[CLI Reference](https://thai-lint.readthedocs.io/en/latest/cli-reference/)** - All CLI commands and options
|
|
1154
1296
|
- **[Deployment Modes](https://thai-lint.readthedocs.io/en/latest/deployment-modes/)** - CLI, Library, and Docker usage
|
|
1155
1297
|
- **[File Placement Linter](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/)** - Detailed linter guide
|
|
1298
|
+
- **[File Header Linter](https://thai-lint.readthedocs.io/en/latest/file-header-linter/)** - File header validation guide
|
|
1156
1299
|
- **[Magic Numbers Linter](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** - Magic numbers detection guide
|
|
1157
1300
|
- **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
|
|
1158
1301
|
- **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
|
|
1159
1302
|
- **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
|
|
1160
1303
|
- **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
|
|
1304
|
+
- **[SARIF Output Guide](docs/sarif-output.md)** - SARIF format for GitHub Code Scanning and CI/CD
|
|
1161
1305
|
- **[Publishing Guide](https://thai-lint.readthedocs.io/en/latest/releasing/)** - Release and publishing workflow
|
|
1162
1306
|
- **[Publishing Checklist](https://thai-lint.readthedocs.io/en/latest/publishing-checklist/)** - Post-publication validation
|
|
1163
1307
|
|
|
@@ -1168,6 +1312,8 @@ See [`examples/`](examples/) directory for working code:
|
|
|
1168
1312
|
- **[basic_usage.py](examples/basic_usage.py)** - Simple library API usage
|
|
1169
1313
|
- **[advanced_usage.py](examples/advanced_usage.py)** - Advanced patterns and workflows
|
|
1170
1314
|
- **[ci_integration.py](examples/ci_integration.py)** - CI/CD integration example
|
|
1315
|
+
- **[sarif_usage.py](examples/sarif_usage.py)** - SARIF output format examples
|
|
1316
|
+
- **[file_header_usage.py](examples/file_header_usage.py)** - File header validation examples
|
|
1171
1317
|
|
|
1172
1318
|
## Project Structure
|
|
1173
1319
|
|
|
@@ -2,17 +2,19 @@ src/__init__.py,sha256=f601zncODr2twrUHqTLS5wyOdZqZi9tMjAe2INhRKqU,2175
|
|
|
2
2
|
src/analyzers/__init__.py,sha256=fFloZtjkBGwYbAhKTxS3Qy3yDr2_3i3WSfKTw1mAioo,972
|
|
3
3
|
src/analyzers/typescript_base.py,sha256=4I7fAcMOAY9vY1AXh52QpohgFmguBECwOkvBRP4zCS4,5054
|
|
4
4
|
src/api.py,sha256=pJ5l3qxccKBEY-BkANwzTgLAl1ZFq7OP6hx6LSxbhDw,4664
|
|
5
|
-
src/cli.py,sha256=
|
|
5
|
+
src/cli.py,sha256=H8DAOCRIu4pK2fbTqryJ0arwKmJ9w3kkeRdtC-FVdAY,55579
|
|
6
6
|
src/config.py,sha256=2ebAjIpAhw4bHbOxViEA5nCjfBlDEIrMR59DBrzcYzM,12460
|
|
7
7
|
src/core/__init__.py,sha256=5FtsDvhMt4SNRx3pbcGURrxn135XRbeRrjSUxiXwkNc,381
|
|
8
8
|
src/core/base.py,sha256=Eklcagi2ktfY4Kytl_ObXov2U49N9OGDpw4cu4PUzGY,7824
|
|
9
|
-
src/core/cli_utils.py,sha256=
|
|
9
|
+
src/core/cli_utils.py,sha256=vKw0jF1rZv_N7gbzvV5TeO9rV5VPfd89fneKrglQ2Hs,6502
|
|
10
10
|
src/core/config_parser.py,sha256=zAY4bDptNlVID0a4JDXN0YlUKXLM92cFqTAwhp_8uGc,4183
|
|
11
11
|
src/core/linter_utils.py,sha256=4jmC2YfpPvGhS_XHlHXa5SBIJh9CQlNj5zuW_GpdPKc,5273
|
|
12
|
-
src/core/registry.py,sha256=
|
|
12
|
+
src/core/registry.py,sha256=yRA8mQLiZwjmgxl1wSTgdj1cuo_QXuRdrXt3NpCBUgE,3285
|
|
13
13
|
src/core/rule_discovery.py,sha256=smxJ9PEyMqEAIicsWaHOaSHD1PHUAOeFZT_a3DNRwgE,4163
|
|
14
14
|
src/core/types.py,sha256=dIYLaCDNtCAzVaplx5S5yxywkLIuX0BN9No_l2zCfV0,2887
|
|
15
15
|
src/core/violation_builder.py,sha256=7AQODqxwc3qp_4m1MPzknkeNGlht1LsDjPsTPpWAGJY,4678
|
|
16
|
+
src/formatters/__init__.py,sha256=yE1yIL8lplTMEjsmQm7F-kOMaYq7OjmbFuiwwK0D-gM,815
|
|
17
|
+
src/formatters/sarif.py,sha256=gGOwb_v7j4mx4bpvV1NNDd-JyHH8i8XX89iQ6uRSvG4,7050
|
|
16
18
|
src/linter_config/__init__.py,sha256=_I2VVlZlfKyT-tKukuUA5-aVcHLOe3m6C2cev43AiEc,298
|
|
17
19
|
src/linter_config/ignore.py,sha256=S2Ub0CCOOC-wpU5Y_EodMprciw18fgWcnp4z_h1MYNk,19638
|
|
18
20
|
src/linter_config/loader.py,sha256=HB09W-uVsEcgCbvUwUHS5Jm2n0bqBXA3744vMc4GAqk,2542
|
|
@@ -38,16 +40,21 @@ src/linters/dry/violation_builder.py,sha256=EUiEQIOZjzAoHEqZiIR8WZP8m4dgqJjcveR5
|
|
|
38
40
|
src/linters/dry/violation_filter.py,sha256=aTOMz8kXG2sZlSVcf3cAxgxHs7f2kBXInfr1V_04fUQ,3125
|
|
39
41
|
src/linters/dry/violation_generator.py,sha256=cc6aKvTxtHSZm0F7Y-gL1bmD3JUphRmAvcbqk9aUzGg,6128
|
|
40
42
|
src/linters/file_header/__init__.py,sha256=S3a2xrOlxnNWD02To5K2ZwILsNEvSj1IvUAH8RjgOV4,791
|
|
41
|
-
src/linters/file_header/atemporal_detector.py,sha256=
|
|
42
|
-
src/linters/file_header/
|
|
43
|
-
src/linters/file_header/
|
|
44
|
-
src/linters/file_header/
|
|
45
|
-
src/linters/file_header/
|
|
46
|
-
src/linters/file_header/
|
|
43
|
+
src/linters/file_header/atemporal_detector.py,sha256=bgQJPDuJj1J5gHKIIOz1TYbBwu8GHrcMafWFVqZ_zZE,3192
|
|
44
|
+
src/linters/file_header/base_parser.py,sha256=HbuJpXQ4V3zTDTP_D0iFqoT7kab6gk8A1lZdlqCb6tc,3202
|
|
45
|
+
src/linters/file_header/bash_parser.py,sha256=ZnPleRD4c16ibYMBc682N9W-Qgtz9lKtLSqSmbo7oqg,2147
|
|
46
|
+
src/linters/file_header/config.py,sha256=Ewrln4W4QDnInTgWr8WgSlQEjAuDyMbUuh9GHAa9a4c,4030
|
|
47
|
+
src/linters/file_header/css_parser.py,sha256=ijpGMixg2ZqNWWdiZjSNtMXCOhm6XDfSY7OU68B9fS8,2332
|
|
48
|
+
src/linters/file_header/field_validator.py,sha256=uASqHj7ID4JJZzgc6X3SmRRLWV35NnX2iZElCt3HW1o,2830
|
|
49
|
+
src/linters/file_header/linter.py,sha256=rbfpHBCCn0cRKUyadc2luZSQay_gJfVC79JcqkwUMb4,12192
|
|
50
|
+
src/linters/file_header/markdown_parser.py,sha256=dmrB8JCxKTHyw-qMU6S-UjKaFbqJ6ZQY1f23tND5_Jo,4964
|
|
51
|
+
src/linters/file_header/python_parser.py,sha256=RTOeEt1b3tCvFWbZIt89awQA37CUOSBIGagEYnayn-M,1432
|
|
52
|
+
src/linters/file_header/typescript_parser.py,sha256=R11Vkr6dUVaU8t90m8rrkMzODtBYk7u-TYFsMDRwzX8,2532
|
|
53
|
+
src/linters/file_header/violation_builder.py,sha256=HPYTmrcCmcO6Dx5dhmj85zZgEBM5EZqTgql-0CA0A0k,2745
|
|
47
54
|
src/linters/file_placement/__init__.py,sha256=vJ43GZujcbAk-K3DwfsQZ0J3yP_5G35CKssatLyntXk,862
|
|
48
55
|
src/linters/file_placement/config_loader.py,sha256=Of5sTG2S-04efn3KOlXrSxpMcC1ipBpSvCjtJOMmWno,2640
|
|
49
56
|
src/linters/file_placement/directory_matcher.py,sha256=YaBeLGiT4bgqN_v4FmEmSASOBxkMC1lyEYpL17wLIDY,2607
|
|
50
|
-
src/linters/file_placement/linter.py,sha256=
|
|
57
|
+
src/linters/file_placement/linter.py,sha256=A4mndpyIyxEzq64rLw2ILNA7APx_QmwzUfnhB0PyuCs,14190
|
|
51
58
|
src/linters/file_placement/path_resolver.py,sha256=S6g7xOYsoSc0O_RDJh8j4Z2klcwzp16rSUfEAErGOTI,1972
|
|
52
59
|
src/linters/file_placement/pattern_matcher.py,sha256=3HZWYgQKXz_y13z3lO1YHn51khCaiGOrneGxKXGWGw0,1898
|
|
53
60
|
src/linters/file_placement/pattern_validator.py,sha256=eMt5GB5lgJMhhQACOlfDXQFfSfNrOY-wJN1JanGka6Q,3717
|
|
@@ -58,15 +65,21 @@ src/linters/magic_numbers/config.py,sha256=3zV6ZNezouBWUYy4kMw5PUlPNvIWXVwOxTz1m
|
|
|
58
65
|
src/linters/magic_numbers/context_analyzer.py,sha256=cGXozlKll10Zao56c2E8ThIyH2mSQaPaUau_g7ngRLw,8446
|
|
59
66
|
src/linters/magic_numbers/linter.py,sha256=maj4NgrDapv0RurKvaVgOI1BUujixZv4E7UeYy4eGT4,18394
|
|
60
67
|
src/linters/magic_numbers/python_analyzer.py,sha256=0u1cFaaFCqOW5yeW-YbmPoZuVIeN_KtmkFyyxup6aR0,2803
|
|
61
|
-
src/linters/magic_numbers/typescript_analyzer.py,sha256=
|
|
68
|
+
src/linters/magic_numbers/typescript_analyzer.py,sha256=NTU1XY-Hudse5oxOtEiG6nA0Rs5Icn9HXELnyPj8OZU,7554
|
|
62
69
|
src/linters/magic_numbers/violation_builder.py,sha256=SqIQv3N9lpP2GRC1TC5InrvaEdrAq24V7Ec2Xj5olb0,3308
|
|
63
70
|
src/linters/nesting/__init__.py,sha256=tszmyCEQMpEwB5H84WcAUfRYDQl7jpsn04es5DtAHsM,3200
|
|
64
71
|
src/linters/nesting/config.py,sha256=PfPA2wJn3i6HHXeM0qu6Qx-v1KJdRwlRkFOdpf7NhS8,2405
|
|
65
72
|
src/linters/nesting/linter.py,sha256=-klbXIbg145beICop81CNQ5J1OInQaeycDT8u3Ff2Ww,6236
|
|
66
73
|
src/linters/nesting/python_analyzer.py,sha256=DF2lVnxYstakOpP_Zizox583Ypkb4eUpgQYEpu6x8gk,3078
|
|
67
|
-
src/linters/nesting/typescript_analyzer.py,sha256=
|
|
74
|
+
src/linters/nesting/typescript_analyzer.py,sha256=wO6p6QNnyW1uaieTCZjaqR56C68AjifAv23tYaSuR2c,3860
|
|
68
75
|
src/linters/nesting/typescript_function_extractor.py,sha256=dDB1otJnFMCo-Pj4mTr4gekKe7V4ArOAtX6gV0dBDc4,4494
|
|
69
76
|
src/linters/nesting/violation_builder.py,sha256=sMHS45F2lrA1WYrG3Uug8HfeRPljnXcyJPHSe2F76Bs,4828
|
|
77
|
+
src/linters/print_statements/__init__.py,sha256=yhvdTFSqBB4UDreeadTHKFzhayxeT6JkF3yxUHMgn1g,1893
|
|
78
|
+
src/linters/print_statements/config.py,sha256=rth3XmzqZGzXkRXDIVZswdtNOXIe1vIRaF46tVLKnyQ,3041
|
|
79
|
+
src/linters/print_statements/linter.py,sha256=J23gtU6FQ2uH2Sj38PPRiV4k2HU0uto2Bqf2D28pKrI,14630
|
|
80
|
+
src/linters/print_statements/python_analyzer.py,sha256=SyCkh8HkYAfcSM7LCwDE1ahhxwPgrlnSVLs1iRemyaw,5461
|
|
81
|
+
src/linters/print_statements/typescript_analyzer.py,sha256=v2R6ZG80eTQq92oE3Y3E4ZwUfBmXwB9G5QKftqbRbD8,5155
|
|
82
|
+
src/linters/print_statements/violation_builder.py,sha256=Vs5m3AnWjrQqQHf6JJDaPP5B1V3YNl5pepG_oiTJnx4,3333
|
|
70
83
|
src/linters/srp/__init__.py,sha256=GbhaSB2_AYY-mWgG_ThbyAcDXoVZuB5eLzguoShf38w,3367
|
|
71
84
|
src/linters/srp/class_analyzer.py,sha256=wuwviwhN1F_VVPaQ3pZvffmY3e9ToxPNJhv6BjhsgZc,3761
|
|
72
85
|
src/linters/srp/config.py,sha256=hTxrM21HIOmg0sM6eJ_h3hRnuxqRZEgs13Ie97-PDr4,3397
|
|
@@ -80,11 +93,11 @@ src/linters/srp/violation_builder.py,sha256=jaIjVtRYWUTs1SVJVwd0FxCojo0DxhPzfhyf
|
|
|
80
93
|
src/orchestrator/__init__.py,sha256=XXLDJq2oaB-TpP2Y97GRnde9EkITGuFCmuLrDfxI9nY,245
|
|
81
94
|
src/orchestrator/core.py,sha256=z0YcwsK18uhlztIPi54ux3mOm8fHMREYJoudsJPhC0Q,8857
|
|
82
95
|
src/orchestrator/language_detector.py,sha256=rHyVMApit80NTTNyDH1ObD1usKD8LjGmH3DwqNAWYGc,2736
|
|
83
|
-
src/templates/thailint_config_template.yaml,sha256=
|
|
96
|
+
src/templates/thailint_config_template.yaml,sha256=vxyhRRi25_xOnHDRx0jzz69dgPqKU2IU5-YFGUoX5lM,4953
|
|
84
97
|
src/utils/__init__.py,sha256=NiBtKeQ09Y3kuUzeN4O1JNfUIYPQDS2AP1l5ODq-Dec,125
|
|
85
98
|
src/utils/project_root.py,sha256=b3YTEGTa9RPcOeHn1IByMMWyRiUabfVlpnlektL0A0o,6156
|
|
86
|
-
thailint-0.
|
|
87
|
-
thailint-0.
|
|
88
|
-
thailint-0.
|
|
89
|
-
thailint-0.
|
|
90
|
-
thailint-0.
|
|
99
|
+
thailint-0.7.0.dist-info/METADATA,sha256=DiyHLUK6NNz3o2MtQsJPJX-3PWdUX0vyMt2SsSI3ot0,41840
|
|
100
|
+
thailint-0.7.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
101
|
+
thailint-0.7.0.dist-info/entry_points.txt,sha256=l7DQJgU18sVLDpSaXOXY3lLhnQHQIRrSJZTQjG1cEAk,62
|
|
102
|
+
thailint-0.7.0.dist-info/licenses/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
|
|
103
|
+
thailint-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|