thailint 0.9.0__py3-none-any.whl → 0.10.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 +127 -0
- src/config.py +2 -3
- src/core/rule_discovery.py +43 -10
- src/linters/collection_pipeline/__init__.py +90 -0
- src/linters/collection_pipeline/config.py +63 -0
- src/linters/collection_pipeline/continue_analyzer.py +100 -0
- src/linters/collection_pipeline/detector.py +130 -0
- src/linters/collection_pipeline/linter.py +437 -0
- src/linters/collection_pipeline/suggestion_builder.py +63 -0
- src/linters/dry/block_filter.py +2 -8
- src/linters/dry/python_analyzer.py +34 -18
- src/linters/dry/typescript_analyzer.py +61 -31
- src/linters/file_header/linter.py +7 -11
- src/linters/file_placement/linter.py +28 -8
- src/linters/srp/heuristics.py +4 -3
- src/linters/srp/linter.py +2 -3
- {thailint-0.9.0.dist-info → thailint-0.10.0.dist-info}/METADATA +116 -3
- {thailint-0.9.0.dist-info → thailint-0.10.0.dist-info}/RECORD +21 -15
- {thailint-0.9.0.dist-info → thailint-0.10.0.dist-info}/WHEEL +0 -0
- {thailint-0.9.0.dist-info → thailint-0.10.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.9.0.dist-info → thailint-0.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -30,7 +30,7 @@ SRP Exception: TypeScriptDuplicateAnalyzer has 20 methods and 324 lines (exceeds
|
|
|
30
30
|
responsibility: accurately detecting duplicate TypeScript/JavaScript code while minimizing false positives.
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
from collections.abc import Generator
|
|
33
|
+
from collections.abc import Generator, Iterable
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
|
|
36
36
|
from src.analyzers.typescript_base import TREE_SITTER_AVAILABLE
|
|
@@ -84,16 +84,33 @@ class TypeScriptDuplicateAnalyzer(BaseTokenAnalyzer): # thailint: ignore[srp.vi
|
|
|
84
84
|
# Generate rolling hash windows
|
|
85
85
|
windows = self._rolling_hash_with_tracking(lines_with_numbers, config.min_duplicate_lines)
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
# Filter out interface/type definitions and single statement patterns
|
|
88
|
+
valid_windows = (
|
|
89
|
+
(hash_val, start_line, end_line, snippet)
|
|
90
|
+
for hash_val, start_line, end_line, snippet in windows
|
|
91
|
+
if self._should_include_block(content, start_line, end_line)
|
|
92
|
+
and not self._is_single_statement_in_source(content, start_line, end_line)
|
|
93
|
+
)
|
|
94
|
+
return self._build_blocks(valid_windows, file_path, content)
|
|
95
|
+
|
|
96
|
+
def _build_blocks(
|
|
97
|
+
self,
|
|
98
|
+
windows: Iterable[tuple[int, int, int, str]],
|
|
99
|
+
file_path: Path,
|
|
100
|
+
content: str,
|
|
101
|
+
) -> list[CodeBlock]:
|
|
102
|
+
"""Build CodeBlock objects from valid windows, applying filters.
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
Args:
|
|
105
|
+
windows: Iterable of (hash_val, start_line, end_line, snippet) tuples
|
|
106
|
+
file_path: Path to source file
|
|
107
|
+
content: File content
|
|
96
108
|
|
|
109
|
+
Returns:
|
|
110
|
+
List of CodeBlock instances that pass all filters
|
|
111
|
+
"""
|
|
112
|
+
blocks = []
|
|
113
|
+
for hash_val, start_line, end_line, snippet in windows:
|
|
97
114
|
block = CodeBlock(
|
|
98
115
|
file_path=file_path,
|
|
99
116
|
start_line=start_line,
|
|
@@ -101,13 +118,8 @@ class TypeScriptDuplicateAnalyzer(BaseTokenAnalyzer): # thailint: ignore[srp.vi
|
|
|
101
118
|
snippet=snippet,
|
|
102
119
|
hash_value=hash_val,
|
|
103
120
|
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if self._filter_registry.should_filter_block(block, content):
|
|
107
|
-
continue
|
|
108
|
-
|
|
109
|
-
blocks.append(block)
|
|
110
|
-
|
|
121
|
+
if not self._filter_registry.should_filter_block(block, content):
|
|
122
|
+
blocks.append(block)
|
|
111
123
|
return blocks
|
|
112
124
|
|
|
113
125
|
def _get_jsdoc_ranges_from_content(self, content: str) -> set[int]:
|
|
@@ -188,26 +200,44 @@ class TypeScriptDuplicateAnalyzer(BaseTokenAnalyzer): # thailint: ignore[srp.vi
|
|
|
188
200
|
lines_with_numbers = []
|
|
189
201
|
in_multiline_import = False
|
|
190
202
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
# Update multi-line import state and check if line should be skipped
|
|
201
|
-
in_multiline_import, should_skip = self._hasher._should_skip_import_line( # pylint: disable=protected-access
|
|
203
|
+
# Skip JSDoc comment lines
|
|
204
|
+
non_jsdoc_lines = (
|
|
205
|
+
(line_num, line)
|
|
206
|
+
for line_num, line in enumerate(content.split("\n"), start=1)
|
|
207
|
+
if line_num not in jsdoc_lines
|
|
208
|
+
)
|
|
209
|
+
for line_num, line in non_jsdoc_lines:
|
|
210
|
+
in_multiline_import, normalized = self._normalize_and_filter_line(
|
|
202
211
|
line, in_multiline_import
|
|
203
212
|
)
|
|
204
|
-
if
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
lines_with_numbers.append((line_num, line))
|
|
213
|
+
if normalized is not None:
|
|
214
|
+
lines_with_numbers.append((line_num, normalized))
|
|
208
215
|
|
|
209
216
|
return lines_with_numbers
|
|
210
217
|
|
|
218
|
+
def _normalize_and_filter_line(
|
|
219
|
+
self, line: str, in_multiline_import: bool
|
|
220
|
+
) -> tuple[bool, str | None]:
|
|
221
|
+
"""Normalize line and check if it should be included.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
line: Raw source line
|
|
225
|
+
in_multiline_import: Current multi-line import state
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Tuple of (new_import_state, normalized_line or None if should skip)
|
|
229
|
+
"""
|
|
230
|
+
normalized = self._hasher._normalize_line(line) # pylint: disable=protected-access
|
|
231
|
+
if not normalized:
|
|
232
|
+
return in_multiline_import, None
|
|
233
|
+
|
|
234
|
+
new_state, should_skip = self._hasher._should_skip_import_line( # pylint: disable=protected-access
|
|
235
|
+
normalized, in_multiline_import
|
|
236
|
+
)
|
|
237
|
+
if should_skip:
|
|
238
|
+
return new_state, None
|
|
239
|
+
return new_state, normalized
|
|
240
|
+
|
|
211
241
|
def _rolling_hash_with_tracking(
|
|
212
242
|
self, lines_with_numbers: list[tuple[int, str]], window_size: int
|
|
213
243
|
) -> list[tuple[int, int, int, str]]:
|
|
@@ -273,17 +273,13 @@ class FileHeaderRule(BaseLintRule): # thailint: ignore[srp]
|
|
|
273
273
|
file_content = context.file_content or ""
|
|
274
274
|
lines = file_content.splitlines()
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
filtered.append(v)
|
|
285
|
-
|
|
286
|
-
return filtered
|
|
276
|
+
non_ignored = (
|
|
277
|
+
v
|
|
278
|
+
for v in violations
|
|
279
|
+
if not self._ignore_parser.should_ignore_violation(v, file_content)
|
|
280
|
+
and not self._has_line_level_ignore(lines, v)
|
|
281
|
+
)
|
|
282
|
+
return list(non_ignored)
|
|
287
283
|
|
|
288
284
|
def _has_line_level_ignore(self, lines: list[str], violation: Violation) -> bool:
|
|
289
285
|
"""Check for thailint-ignore-line directive."""
|
|
@@ -124,20 +124,40 @@ class FilePlacementLinter:
|
|
|
124
124
|
Returns:
|
|
125
125
|
List of all violations found
|
|
126
126
|
"""
|
|
127
|
+
valid_files = self._get_valid_files(dir_path, recursive)
|
|
128
|
+
return self._lint_files(valid_files)
|
|
129
|
+
|
|
130
|
+
def _get_valid_files(self, dir_path: Path, recursive: bool) -> list[Path]:
|
|
131
|
+
"""Get list of valid files to lint from directory.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
dir_path: Directory to scan
|
|
135
|
+
recursive: Scan recursively
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
List of file paths to lint
|
|
139
|
+
"""
|
|
127
140
|
from src.linter_config.ignore import IgnoreDirectiveParser
|
|
128
141
|
|
|
129
142
|
ignore_parser = IgnoreDirectiveParser(self.project_root)
|
|
130
143
|
pattern = "**/*" if recursive else "*"
|
|
131
144
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
return [
|
|
146
|
+
f for f in dir_path.glob(pattern) if f.is_file() and not ignore_parser.is_ignored(f)
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
def _lint_files(self, file_paths: list[Path]) -> list[Violation]:
|
|
150
|
+
"""Lint multiple files and collect violations.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
file_paths: List of file paths to lint
|
|
140
154
|
|
|
155
|
+
Returns:
|
|
156
|
+
List of all violations found
|
|
157
|
+
"""
|
|
158
|
+
violations = []
|
|
159
|
+
for file_path in file_paths:
|
|
160
|
+
violations.extend(self.lint_path(file_path))
|
|
141
161
|
return violations
|
|
142
162
|
|
|
143
163
|
|
src/linters/srp/heuristics.py
CHANGED
|
@@ -33,9 +33,10 @@ def count_methods(class_node: ast.ClassDef) -> int:
|
|
|
33
33
|
Number of methods in the class
|
|
34
34
|
"""
|
|
35
35
|
methods = 0
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
|
|
36
|
+
func_nodes = (
|
|
37
|
+
n for n in class_node.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
38
|
+
)
|
|
39
|
+
for node in func_nodes:
|
|
39
40
|
# Don't count @property decorators as methods
|
|
40
41
|
if not has_property_decorator(node):
|
|
41
42
|
methods += 1
|
src/linters/srp/linter.py
CHANGED
|
@@ -171,9 +171,8 @@ class SRPRule(MultiLanguageLintRule):
|
|
|
171
171
|
List of violations
|
|
172
172
|
"""
|
|
173
173
|
violations = []
|
|
174
|
-
for
|
|
175
|
-
|
|
176
|
-
continue
|
|
174
|
+
valid_metrics = (m for m in metrics_list if isinstance(m, dict))
|
|
175
|
+
for metrics in valid_metrics:
|
|
177
176
|
violation = self._create_violation_if_needed(metrics, config, context)
|
|
178
177
|
if violation:
|
|
179
178
|
violations.append(violation)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thailint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.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,8 +37,8 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
[](https://opensource.org/licenses/MIT)
|
|
39
39
|
[](https://www.python.org/downloads/)
|
|
40
|
-
[](tests/)
|
|
41
|
+
[](htmlcov/)
|
|
42
42
|
[](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
|
|
43
43
|
[](docs/sarif-output.md)
|
|
44
44
|
|
|
@@ -98,6 +98,12 @@ thailint complements your existing linting stack by catching the patterns AI too
|
|
|
98
98
|
- Configurable thresholds (lines, tokens, occurrences)
|
|
99
99
|
- Language-specific detection (Python, TypeScript, JavaScript)
|
|
100
100
|
- False positive filtering (keyword args, imports)
|
|
101
|
+
- **Collection Pipeline Linting** - Detect for loops with embedded filtering
|
|
102
|
+
- Based on Martin Fowler's "Replace Loop with Pipeline" refactoring
|
|
103
|
+
- Detects if/continue patterns that should use generator expressions
|
|
104
|
+
- Generates refactoring suggestions with generator syntax
|
|
105
|
+
- Configurable threshold (min_continues)
|
|
106
|
+
- Python support with AST analysis
|
|
101
107
|
- **Method Property Linting** - Detect methods that should be @property decorators
|
|
102
108
|
- Python AST-based detection
|
|
103
109
|
- get_* prefix detection (Java-style getters)
|
|
@@ -741,6 +747,109 @@ Built-in filters automatically exclude common non-duplication patterns:
|
|
|
741
747
|
|
|
742
748
|
See [DRY Linter Guide](https://thai-lint.readthedocs.io/en/latest/dry-linter/) for comprehensive documentation, storage modes, and refactoring patterns.
|
|
743
749
|
|
|
750
|
+
## Collection Pipeline Linter
|
|
751
|
+
|
|
752
|
+
### Overview
|
|
753
|
+
|
|
754
|
+
The collection-pipeline linter detects for loops with embedded filtering (if/continue patterns) that should be refactored to use generator expressions or other collection pipelines. Based on Martin Fowler's "Replace Loop with Pipeline" refactoring pattern.
|
|
755
|
+
|
|
756
|
+
### The Anti-Pattern
|
|
757
|
+
|
|
758
|
+
```python
|
|
759
|
+
# Anti-pattern: Embedded filtering in loop body
|
|
760
|
+
for file_path in dir_path.glob(pattern):
|
|
761
|
+
if not file_path.is_file():
|
|
762
|
+
continue
|
|
763
|
+
if ignore_parser.is_ignored(file_path):
|
|
764
|
+
continue
|
|
765
|
+
violations.extend(lint_file(file_path))
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### The Solution
|
|
769
|
+
|
|
770
|
+
```python
|
|
771
|
+
# Collection pipeline: Filtering separated from processing
|
|
772
|
+
valid_files = (
|
|
773
|
+
f for f in dir_path.glob(pattern)
|
|
774
|
+
if f.is_file() and not ignore_parser.is_ignored(f)
|
|
775
|
+
)
|
|
776
|
+
for file_path in valid_files:
|
|
777
|
+
violations.extend(lint_file(file_path))
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### Quick Start
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
# Check current directory
|
|
784
|
+
thailint pipeline .
|
|
785
|
+
|
|
786
|
+
# Check specific directory
|
|
787
|
+
thailint pipeline src/
|
|
788
|
+
|
|
789
|
+
# Only flag patterns with 2+ filter conditions
|
|
790
|
+
thailint pipeline --min-continues 2 src/
|
|
791
|
+
|
|
792
|
+
# JSON output
|
|
793
|
+
thailint pipeline --format json src/
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Configuration
|
|
797
|
+
|
|
798
|
+
```yaml
|
|
799
|
+
# .thailint.yaml
|
|
800
|
+
collection-pipeline:
|
|
801
|
+
enabled: true
|
|
802
|
+
min_continues: 1 # Minimum if/continue patterns to flag
|
|
803
|
+
ignore:
|
|
804
|
+
- "tests/**"
|
|
805
|
+
- "**/legacy/**"
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### Example Violation
|
|
809
|
+
|
|
810
|
+
**Detected Pattern:**
|
|
811
|
+
```python
|
|
812
|
+
def process_files(paths):
|
|
813
|
+
for path in paths:
|
|
814
|
+
if not path.is_file():
|
|
815
|
+
continue
|
|
816
|
+
analyze(path)
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**Violation Message:**
|
|
820
|
+
```
|
|
821
|
+
src/processor.py:3 - For loop over 'paths' has embedded filtering.
|
|
822
|
+
Consider using a generator expression:
|
|
823
|
+
for path in (path for path in paths if path.is_file()):
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
**Refactored Code:**
|
|
827
|
+
```python
|
|
828
|
+
def process_files(paths):
|
|
829
|
+
valid_paths = (p for p in paths if p.is_file())
|
|
830
|
+
for path in valid_paths:
|
|
831
|
+
analyze(path)
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
### Why This Matters
|
|
835
|
+
|
|
836
|
+
- **Separation of concerns**: Filtering logic is separate from processing logic
|
|
837
|
+
- **Readability**: Intent is clear at a glance
|
|
838
|
+
- **Testability**: Filtering can be tested independently
|
|
839
|
+
- **Based on**: Martin Fowler's "Replace Loop with Pipeline" refactoring
|
|
840
|
+
|
|
841
|
+
### Ignoring Violations
|
|
842
|
+
|
|
843
|
+
```python
|
|
844
|
+
# Line-level ignore
|
|
845
|
+
for item in items: # thailint: ignore[collection-pipeline]
|
|
846
|
+
if not item.valid:
|
|
847
|
+
continue
|
|
848
|
+
process(item)
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
See [Collection Pipeline Linter Guide](docs/collection-pipeline-linter.md) for comprehensive documentation and refactoring patterns.
|
|
852
|
+
|
|
744
853
|
## Magic Numbers Linter
|
|
745
854
|
|
|
746
855
|
### Overview
|
|
@@ -1351,6 +1460,9 @@ docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src/file1.p
|
|
|
1351
1460
|
# Lint specific subdirectory
|
|
1352
1461
|
docker run --rm -v $(pwd):/data washad/thailint:latest nesting /data/src
|
|
1353
1462
|
|
|
1463
|
+
# Collection pipeline linter
|
|
1464
|
+
docker run --rm -v $(pwd):/data washad/thailint:latest pipeline /data/src
|
|
1465
|
+
|
|
1354
1466
|
# With custom config
|
|
1355
1467
|
docker run --rm -v $(pwd):/data \
|
|
1356
1468
|
washad/thailint:latest nesting --config /data/.thailint.yaml /data
|
|
@@ -1414,6 +1526,7 @@ docker run --rm -v /path/to/workspace:/workspace \
|
|
|
1414
1526
|
- **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
|
|
1415
1527
|
- **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
|
|
1416
1528
|
- **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
|
|
1529
|
+
- **[Collection Pipeline Linter](https://thai-lint.readthedocs.io/en/latest/collection-pipeline-linter/)** - Loop filtering refactoring guide
|
|
1417
1530
|
- **[Method Property Linter](https://thai-lint.readthedocs.io/en/latest/method-property-linter/)** - Method-to-property conversion guide
|
|
1418
1531
|
- **[Stateless Class Linter](https://thai-lint.readthedocs.io/en/latest/stateless-class-linter/)** - Stateless class detection guide
|
|
1419
1532
|
- **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
|
|
@@ -2,15 +2,15 @@ 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=
|
|
6
|
-
src/config.py,sha256=
|
|
5
|
+
src/cli.py,sha256=NoCEg1jLDkS7My4XhUfrssQmBGX-EaC2ULMwQs23Rt0,67580
|
|
6
|
+
src/config.py,sha256=gQ2DoBSZra4Dw3zUVd-t0lyD_pOY4iufx7sL_sXGBGU,12482
|
|
7
7
|
src/core/__init__.py,sha256=5FtsDvhMt4SNRx3pbcGURrxn135XRbeRrjSUxiXwkNc,381
|
|
8
8
|
src/core/base.py,sha256=7bb_CAEohgEFXDgIK6Qc_zt6Cz4U9K6uhLhunbMX8f0,7966
|
|
9
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
12
|
src/core/registry.py,sha256=yRA8mQLiZwjmgxl1wSTgdj1cuo_QXuRdrXt3NpCBUgE,3285
|
|
13
|
-
src/core/rule_discovery.py,sha256=
|
|
13
|
+
src/core/rule_discovery.py,sha256=GL6X-38qiqmPHpk96iOCrFOLjENIze-QamO0WwugZEg,5365
|
|
14
14
|
src/core/types.py,sha256=dIYLaCDNtCAzVaplx5S5yxywkLIuX0BN9No_l2zCfV0,2887
|
|
15
15
|
src/core/violation_builder.py,sha256=le9YCMcJt0hFji41-cCplpowys-jT44kGe-59_Du1ZM,6592
|
|
16
16
|
src/formatters/__init__.py,sha256=yE1yIL8lplTMEjsmQm7F-kOMaYq7OjmbFuiwwK0D-gM,815
|
|
@@ -19,9 +19,15 @@ src/linter_config/__init__.py,sha256=_I2VVlZlfKyT-tKukuUA5-aVcHLOe3m6C2cev43AiEc
|
|
|
19
19
|
src/linter_config/ignore.py,sha256=S2Ub0CCOOC-wpU5Y_EodMprciw18fgWcnp4z_h1MYNk,19638
|
|
20
20
|
src/linter_config/loader.py,sha256=K6mKRkP2jgwar-pwBoJGWgwynLVjqdez-l3Nd6bUCMk,3363
|
|
21
21
|
src/linters/__init__.py,sha256=-nnNsL8E5-2p9qlLKp_TaShHAjPH-NacOEU1sXmAR9k,77
|
|
22
|
+
src/linters/collection_pipeline/__init__.py,sha256=BcnbY3wgJB1XLfZ9J9qfUJQ1_yCo_THjGDTppxJEMZY,3231
|
|
23
|
+
src/linters/collection_pipeline/config.py,sha256=Z3V84X2rlj27x1eZfFBJwOMzoqFzOiwdjk-ze5sDop0,2240
|
|
24
|
+
src/linters/collection_pipeline/continue_analyzer.py,sha256=51DBvR92no01rlMOjukGhsLX8jtuLbrEU--fbehUW6A,2812
|
|
25
|
+
src/linters/collection_pipeline/detector.py,sha256=67M0LZwBHqZHNy5E6OS1CnBFVqis1LzkO0YP615BFr4,4291
|
|
26
|
+
src/linters/collection_pipeline/linter.py,sha256=jvLqKsBAFj_QRrQVBTTMdLzDOjn2H2YlD9cSrdfbJbk,13947
|
|
27
|
+
src/linters/collection_pipeline/suggestion_builder.py,sha256=xwMWFCTm8UWvn_6XAJerH68XayJJGWiPFmpe-NztUlc,2103
|
|
22
28
|
src/linters/dry/__init__.py,sha256=p58tN3z_VbulfTkRm1kLZJ43Bemt66T2sro1teirUY8,826
|
|
23
29
|
src/linters/dry/base_token_analyzer.py,sha256=SgnsX4a4kUzOsA9hTkLkg4yB8I77ewAyiUp6cAybUrg,2684
|
|
24
|
-
src/linters/dry/block_filter.py,sha256=
|
|
30
|
+
src/linters/dry/block_filter.py,sha256=sSCaut6U8sZQUhpFKey4qxgdB0E43_8X2K_lkhIWIXE,8297
|
|
25
31
|
src/linters/dry/block_grouper.py,sha256=NP66BlofaY7HVXcWwmK5lyiNXbaTlU1V3IbcZubIq_I,1937
|
|
26
32
|
src/linters/dry/cache.py,sha256=dedkGU5SolTjmTTsmj-dwPEnxyhWZ2aBiHGkQy1JwXo,5881
|
|
27
33
|
src/linters/dry/cache_query.py,sha256=qu_uHe360ZvKmFTBvfREjjPMGbJgLQsFTKPVIA2jQJ0,1949
|
|
@@ -32,10 +38,10 @@ src/linters/dry/duplicate_storage.py,sha256=9pIALnwAuz5BJUYNXrPbObbP932CE9x0vgUk
|
|
|
32
38
|
src/linters/dry/file_analyzer.py,sha256=ufSQ85ddsGTqGnBHZNTdV_5DGfTpUmJOB58sIdJNV0I,2928
|
|
33
39
|
src/linters/dry/inline_ignore.py,sha256=ASfA-fp_1aPpkakN2e0T6qdTh8S7Jqj89ovxXJLmFlc,4439
|
|
34
40
|
src/linters/dry/linter.py,sha256=XMLwCgGrFX0l0dVUJs1jpsXOfgxeKKDbxOtN5h5Emhk,5835
|
|
35
|
-
src/linters/dry/python_analyzer.py,sha256=
|
|
41
|
+
src/linters/dry/python_analyzer.py,sha256=S0eEpBcMI0ufyMKfK0Lep-VgJ9-xt0M9525o_4hXDaQ,28025
|
|
36
42
|
src/linters/dry/storage_initializer.py,sha256=ykMALFs4uMUrN0_skEwySDl_t5Dm_LGHllF0OxDhiUI,1366
|
|
37
43
|
src/linters/dry/token_hasher.py,sha256=VT7BkzYMIWadvGrnL8bFlH3lzRfgdZSj5lDU364tRNA,5589
|
|
38
|
-
src/linters/dry/typescript_analyzer.py,sha256=
|
|
44
|
+
src/linters/dry/typescript_analyzer.py,sha256=ecAJZjNRtzw_XKyvXX9v53Ewyfuex_obVxmq5aBk0h4,22854
|
|
39
45
|
src/linters/dry/violation_builder.py,sha256=WkCibSNytoqMHGC-3GrVff4PD7-SOnVzzZgkMeqmzco,2952
|
|
40
46
|
src/linters/dry/violation_filter.py,sha256=RJVZZ3RWqJhIdkRQ8YcXXG9XGhiQmaiZ4_IzHWpbCG8,3265
|
|
41
47
|
src/linters/dry/violation_generator.py,sha256=hbMs8Fo2hS8JCXicZcZni6NEkv4fJRsyrsjzrqN6qVw,6122
|
|
@@ -46,7 +52,7 @@ src/linters/file_header/bash_parser.py,sha256=y74YLSQ6FKXnkl9RCu31N3llDxeAH9wRgm
|
|
|
46
52
|
src/linters/file_header/config.py,sha256=Ewrln4W4QDnInTgWr8WgSlQEjAuDyMbUuh9GHAa9a4c,4030
|
|
47
53
|
src/linters/file_header/css_parser.py,sha256=ijpGMixg2ZqNWWdiZjSNtMXCOhm6XDfSY7OU68B9fS8,2332
|
|
48
54
|
src/linters/file_header/field_validator.py,sha256=uASqHj7ID4JJZzgc6X3SmRRLWV35NnX2iZElCt3HW1o,2830
|
|
49
|
-
src/linters/file_header/linter.py,sha256=
|
|
55
|
+
src/linters/file_header/linter.py,sha256=ffh-gMF9IW7syqE3EXuZUxo94RMP6Dfd97hGsS3Rabg,12153
|
|
50
56
|
src/linters/file_header/markdown_parser.py,sha256=dmrB8JCxKTHyw-qMU6S-UjKaFbqJ6ZQY1f23tND5_Jo,4964
|
|
51
57
|
src/linters/file_header/python_parser.py,sha256=RTOeEt1b3tCvFWbZIt89awQA37CUOSBIGagEYnayn-M,1432
|
|
52
58
|
src/linters/file_header/typescript_parser.py,sha256=R11Vkr6dUVaU8t90m8rrkMzODtBYk7u-TYFsMDRwzX8,2532
|
|
@@ -54,7 +60,7 @@ src/linters/file_header/violation_builder.py,sha256=HPYTmrcCmcO6Dx5dhmj85zZgEBM5
|
|
|
54
60
|
src/linters/file_placement/__init__.py,sha256=vJ43GZujcbAk-K3DwfsQZ0J3yP_5G35CKssatLyntXk,862
|
|
55
61
|
src/linters/file_placement/config_loader.py,sha256=Of5sTG2S-04efn3KOlXrSxpMcC1ipBpSvCjtJOMmWno,2640
|
|
56
62
|
src/linters/file_placement/directory_matcher.py,sha256=1rxJtCEzqDYDQnscVX6pzk7gxCMD11pVIGaWcli-tHY,2742
|
|
57
|
-
src/linters/file_placement/linter.py,sha256=
|
|
63
|
+
src/linters/file_placement/linter.py,sha256=VCqQN4aeYhAEPmzSzJGcAEVRPyoVVT9w3o1n3368oPo,14789
|
|
58
64
|
src/linters/file_placement/path_resolver.py,sha256=S6g7xOYsoSc0O_RDJh8j4Z2klcwzp16rSUfEAErGOTI,1972
|
|
59
65
|
src/linters/file_placement/pattern_matcher.py,sha256=0TWozhKGqQk66oJpP66ewlmOI3bI8GpDf37PyImZcUA,2030
|
|
60
66
|
src/linters/file_placement/pattern_validator.py,sha256=KW2Wv4owIGiLxxC3hhyVsKkIuzC95pGv3wQD3m4_eYo,3853
|
|
@@ -88,8 +94,8 @@ src/linters/print_statements/violation_builder.py,sha256=Vs5m3AnWjrQqQHf6JJDaPP5
|
|
|
88
94
|
src/linters/srp/__init__.py,sha256=GbhaSB2_AYY-mWgG_ThbyAcDXoVZuB5eLzguoShf38w,3367
|
|
89
95
|
src/linters/srp/class_analyzer.py,sha256=vSBKLDBc734Iy-p9Ux7ygnNYaUtkT3ivUUJS54tyKs0,3912
|
|
90
96
|
src/linters/srp/config.py,sha256=hTxrM21HIOmg0sM6eJ_h3hRnuxqRZEgs13Ie97-PDr4,3397
|
|
91
|
-
src/linters/srp/heuristics.py,sha256=
|
|
92
|
-
src/linters/srp/linter.py,sha256=
|
|
97
|
+
src/linters/srp/heuristics.py,sha256=0JxKitWXJo0BxubLGLPoKhr39h4bxkfos2633ypOXcs,3203
|
|
98
|
+
src/linters/srp/linter.py,sha256=Sqh4QPz6G14A9osLHFO4xegnZSYaCWucKZPNjXzvUYA,7627
|
|
93
99
|
src/linters/srp/metrics_evaluator.py,sha256=Prk_dPacas_dX7spAzV0g734srmzT5u0t5d4mTG9g2o,1606
|
|
94
100
|
src/linters/srp/python_analyzer.py,sha256=PH27l38BFPNmj22Z10QDBioLDCZ4xpJFzBfTh_4XMZ4,3585
|
|
95
101
|
src/linters/srp/typescript_analyzer.py,sha256=Wi0P_G1v5AnZYtMN3sNm1iHva84-8Kep2LZ5RmAS4c4,2885
|
|
@@ -105,8 +111,8 @@ src/orchestrator/language_detector.py,sha256=rHyVMApit80NTTNyDH1ObD1usKD8LjGmH3D
|
|
|
105
111
|
src/templates/thailint_config_template.yaml,sha256=vxyhRRi25_xOnHDRx0jzz69dgPqKU2IU5-YFGUoX5lM,4953
|
|
106
112
|
src/utils/__init__.py,sha256=NiBtKeQ09Y3kuUzeN4O1JNfUIYPQDS2AP1l5ODq-Dec,125
|
|
107
113
|
src/utils/project_root.py,sha256=b3YTEGTa9RPcOeHn1IByMMWyRiUabfVlpnlektL0A0o,6156
|
|
108
|
-
thailint-0.
|
|
109
|
-
thailint-0.
|
|
110
|
-
thailint-0.
|
|
111
|
-
thailint-0.
|
|
112
|
-
thailint-0.
|
|
114
|
+
thailint-0.10.0.dist-info/METADATA,sha256=ZeNW9Hi0Fvu5BB0Sr9Dro1fopDYG6C6hobA5nd3qTQs,48517
|
|
115
|
+
thailint-0.10.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
116
|
+
thailint-0.10.0.dist-info/entry_points.txt,sha256=l7DQJgU18sVLDpSaXOXY3lLhnQHQIRrSJZTQjG1cEAk,62
|
|
117
|
+
thailint-0.10.0.dist-info/licenses/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
|
|
118
|
+
thailint-0.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|