thailint 0.2.0__py3-none-any.whl → 0.5.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 +646 -36
- src/config.py +6 -2
- src/core/base.py +90 -5
- src/core/config_parser.py +31 -4
- src/linters/dry/block_filter.py +5 -2
- src/linters/dry/cache.py +46 -92
- src/linters/dry/config.py +17 -13
- src/linters/dry/duplicate_storage.py +17 -80
- src/linters/dry/file_analyzer.py +11 -48
- src/linters/dry/linter.py +5 -12
- src/linters/dry/python_analyzer.py +188 -37
- src/linters/dry/storage_initializer.py +9 -18
- src/linters/dry/token_hasher.py +63 -9
- src/linters/dry/typescript_analyzer.py +7 -5
- src/linters/dry/violation_filter.py +4 -1
- src/linters/file_header/__init__.py +24 -0
- src/linters/file_header/atemporal_detector.py +87 -0
- src/linters/file_header/config.py +66 -0
- src/linters/file_header/field_validator.py +69 -0
- src/linters/file_header/linter.py +313 -0
- src/linters/file_header/python_parser.py +86 -0
- src/linters/file_header/violation_builder.py +78 -0
- src/linters/file_placement/linter.py +15 -4
- src/linters/magic_numbers/__init__.py +48 -0
- src/linters/magic_numbers/config.py +82 -0
- src/linters/magic_numbers/context_analyzer.py +247 -0
- src/linters/magic_numbers/linter.py +516 -0
- src/linters/magic_numbers/python_analyzer.py +76 -0
- src/linters/magic_numbers/typescript_analyzer.py +218 -0
- src/linters/magic_numbers/violation_builder.py +98 -0
- src/linters/nesting/__init__.py +6 -2
- src/linters/nesting/config.py +6 -3
- src/linters/nesting/linter.py +8 -19
- src/linters/nesting/typescript_analyzer.py +1 -0
- src/linters/print_statements/__init__.py +53 -0
- src/linters/print_statements/config.py +83 -0
- src/linters/print_statements/linter.py +430 -0
- src/linters/print_statements/python_analyzer.py +155 -0
- src/linters/print_statements/typescript_analyzer.py +135 -0
- src/linters/print_statements/violation_builder.py +98 -0
- src/linters/srp/__init__.py +3 -3
- src/linters/srp/config.py +12 -6
- src/linters/srp/linter.py +33 -24
- src/orchestrator/core.py +12 -2
- src/templates/thailint_config_template.yaml +158 -0
- src/utils/project_root.py +135 -16
- {thailint-0.2.0.dist-info → thailint-0.5.0.dist-info}/METADATA +387 -81
- thailint-0.5.0.dist-info/RECORD +96 -0
- {thailint-0.2.0.dist-info → thailint-0.5.0.dist-info}/WHEEL +1 -1
- thailint-0.2.0.dist-info/RECORD +0 -75
- {thailint-0.2.0.dist-info → thailint-0.5.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.2.0.dist-info → thailint-0.5.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: thailint
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: linter,ai,code-quality,static-analysis,file-placement,governance,multi-language,cli,docker,python
|
|
7
8
|
Author: Steve Jackson
|
|
8
9
|
Requires-Python: >=3.11,<4.0
|
|
@@ -15,6 +16,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
22
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
@@ -26,7 +28,7 @@ Requires-Dist: pyprojroot (>=0.3.0,<0.4.0)
|
|
|
26
28
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
27
29
|
Requires-Dist: tree-sitter (>=0.25.2,<0.26.0)
|
|
28
30
|
Requires-Dist: tree-sitter-typescript (>=0.23.2,<0.24.0)
|
|
29
|
-
Project-URL: Documentation, https://
|
|
31
|
+
Project-URL: Documentation, https://thai-lint.readthedocs.io/
|
|
30
32
|
Project-URL: Homepage, https://github.com/be-wise-be-kind/thai-lint
|
|
31
33
|
Project-URL: Repository, https://github.com/be-wise-be-kind/thai-lint
|
|
32
34
|
Description-Content-Type: text/markdown
|
|
@@ -35,19 +37,47 @@ Description-Content-Type: text/markdown
|
|
|
35
37
|
|
|
36
38
|
[](https://opensource.org/licenses/MIT)
|
|
37
39
|
[](https://www.python.org/downloads/)
|
|
38
|
-
[](tests/)
|
|
41
|
+
[](htmlcov/)
|
|
42
|
+
[](https://thai-lint.readthedocs.io/en/latest/?badge=latest)
|
|
40
43
|
|
|
41
44
|
The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
|
|
42
45
|
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
**New to thailint?** Start here:
|
|
49
|
+
- **[Quick Start Guide](https://thai-lint.readthedocs.io/en/latest/quick-start/)** - Get running in 5 minutes
|
|
50
|
+
- **[Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/)** - Complete config options for all linters
|
|
51
|
+
- **[Troubleshooting Guide](https://thai-lint.readthedocs.io/en/latest/troubleshooting/)** - Common issues and solutions
|
|
52
|
+
|
|
53
|
+
**Full Documentation:** Browse the **[documentation site](https://thai-lint.readthedocs.io/)** for comprehensive guides covering installation, all linters, configuration patterns, and integration examples.
|
|
54
|
+
|
|
43
55
|
## Overview
|
|
44
56
|
|
|
45
|
-
thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It
|
|
57
|
+
thailint is a modern, enterprise-ready multi-language linter designed specifically for AI-generated code. It focuses on common mistakes and anti-patterns that AI coding assistants frequently introduce—issues that existing linters don't catch or don't handle consistently across languages.
|
|
58
|
+
|
|
59
|
+
**Why thailint?**
|
|
60
|
+
|
|
61
|
+
We're not trying to replace the wonderful existing linters like Pylint, ESLint, or Ruff. Instead, thailint fills critical gaps:
|
|
62
|
+
|
|
63
|
+
- **AI-Specific Patterns**: AI assistants have predictable blind spots (excessive nesting, magic numbers, SRP violations) that traditional linters miss
|
|
64
|
+
- **Cross-Language Consistency**: Detects the same anti-patterns across Python, TypeScript, and JavaScript with unified rules
|
|
65
|
+
- **No Existing Solutions**: Issues like excessive nesting depth, file placement violations, and cross-project code duplication lack comprehensive multi-language detection
|
|
66
|
+
- **Governance Layer**: Enforces project-wide structure and organization patterns that AI can't infer from local context
|
|
67
|
+
|
|
68
|
+
thailint complements your existing linting stack by catching the patterns AI tools repeatedly miss.
|
|
69
|
+
|
|
70
|
+
**Complete documentation available at [thai-lint.readthedocs.io](https://thai-lint.readthedocs.io/)** covering installation, configuration, all linters, and troubleshooting.
|
|
46
71
|
|
|
47
72
|
## Features
|
|
48
73
|
|
|
49
74
|
### Core Capabilities
|
|
50
75
|
- **File Placement Linting** - Enforce project structure and organization
|
|
76
|
+
- **Magic Numbers Linting** - Detect unnamed numeric literals that should be constants
|
|
77
|
+
- Python and TypeScript support with AST analysis
|
|
78
|
+
- Context-aware detection (ignores constants, test files, range() usage)
|
|
79
|
+
- Configurable allowed numbers and thresholds
|
|
80
|
+
- Helpful suggestions for extracting to named constants
|
|
51
81
|
- **Nesting Depth Linting** - Detect excessive code nesting with AST analysis
|
|
52
82
|
- Python and TypeScript support with tree-sitter
|
|
53
83
|
- Configurable max depth (default: 4, recommended: 3)
|
|
@@ -57,8 +87,8 @@ thailint is a modern, enterprise-ready multi-language linter designed specifical
|
|
|
57
87
|
- Language-specific thresholds (Python, TypeScript, JavaScript)
|
|
58
88
|
- Refactoring patterns from real-world examples
|
|
59
89
|
- **DRY Linting** - Detect duplicate code across projects
|
|
60
|
-
- Token-based hash detection with SQLite
|
|
61
|
-
- Fast
|
|
90
|
+
- Token-based hash detection with SQLite storage
|
|
91
|
+
- Fast duplicate detection (in-memory or disk-backed)
|
|
62
92
|
- Configurable thresholds (lines, tokens, occurrences)
|
|
63
93
|
- Language-specific detection (Python, TypeScript, JavaScript)
|
|
64
94
|
- False positive filtering (keyword args, imports)
|
|
@@ -92,7 +122,7 @@ cd thai-lint
|
|
|
92
122
|
pip install -e ".[dev]"
|
|
93
123
|
```
|
|
94
124
|
|
|
95
|
-
### From PyPI
|
|
125
|
+
### From PyPI
|
|
96
126
|
|
|
97
127
|
```bash
|
|
98
128
|
pip install thai-lint
|
|
@@ -125,6 +155,9 @@ thailint nesting src/
|
|
|
125
155
|
# Check for duplicate code
|
|
126
156
|
thailint dry .
|
|
127
157
|
|
|
158
|
+
# Check for magic numbers
|
|
159
|
+
thailint magic-numbers src/
|
|
160
|
+
|
|
128
161
|
# With config file
|
|
129
162
|
thailint dry --config .thailint.yaml src/
|
|
130
163
|
|
|
@@ -132,6 +165,8 @@ thailint dry --config .thailint.yaml src/
|
|
|
132
165
|
thailint dry --format json src/
|
|
133
166
|
```
|
|
134
167
|
|
|
168
|
+
**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.
|
|
169
|
+
|
|
135
170
|
### Library Mode
|
|
136
171
|
|
|
137
172
|
```python
|
|
@@ -169,6 +204,44 @@ docker run --rm -v $(pwd):/data \
|
|
|
169
204
|
washad/thailint:latest nesting /data/src
|
|
170
205
|
```
|
|
171
206
|
|
|
207
|
+
### Docker with Sibling Directories
|
|
208
|
+
|
|
209
|
+
For Docker environments with sibling directories (e.g., separate config and source directories), use `--project-root` or config path inference:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Directory structure:
|
|
213
|
+
# /workspace/
|
|
214
|
+
# ├── root/ # Contains .thailint.yaml and .git
|
|
215
|
+
# ├── backend/ # Code to lint
|
|
216
|
+
# └── tools/
|
|
217
|
+
|
|
218
|
+
# Option 1: Explicit project root (recommended)
|
|
219
|
+
docker run --rm -v $(pwd):/data \
|
|
220
|
+
washad/thailint:latest \
|
|
221
|
+
--project-root /data/root \
|
|
222
|
+
magic-numbers /data/backend/
|
|
223
|
+
|
|
224
|
+
# Option 2: Config path inference (automatic)
|
|
225
|
+
docker run --rm -v $(pwd):/data \
|
|
226
|
+
washad/thailint:latest \
|
|
227
|
+
--config /data/root/.thailint.yaml \
|
|
228
|
+
magic-numbers /data/backend/
|
|
229
|
+
|
|
230
|
+
# With ignore patterns resolving from project root
|
|
231
|
+
docker run --rm -v $(pwd):/data \
|
|
232
|
+
washad/thailint:latest \
|
|
233
|
+
--project-root /data/root \
|
|
234
|
+
--config /data/root/.thailint.yaml \
|
|
235
|
+
magic-numbers /data/backend/
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Priority order:**
|
|
239
|
+
1. `--project-root` (highest priority - explicit specification)
|
|
240
|
+
2. Inferred from `--config` path directory
|
|
241
|
+
3. Auto-detection from file location (fallback)
|
|
242
|
+
|
|
243
|
+
See **[Docker Usage](#docker-usage)** section below for more examples.
|
|
244
|
+
|
|
172
245
|
## Configuration
|
|
173
246
|
|
|
174
247
|
Create `.thailint.yaml` in your project root:
|
|
@@ -228,14 +301,19 @@ dry:
|
|
|
228
301
|
python:
|
|
229
302
|
min_occurrences: 3 # Python: require 3+ occurrences
|
|
230
303
|
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
cache_path: ".thailint-cache/dry.db"
|
|
304
|
+
# Storage settings (SQLite)
|
|
305
|
+
storage_mode: "memory" # Options: "memory" (default) or "tempfile"
|
|
234
306
|
|
|
235
307
|
# Ignore patterns
|
|
236
308
|
ignore:
|
|
237
309
|
- "tests/"
|
|
238
310
|
- "__init__.py"
|
|
311
|
+
|
|
312
|
+
# Magic numbers linter configuration
|
|
313
|
+
magic-numbers:
|
|
314
|
+
enabled: true
|
|
315
|
+
allowed_numbers: [-1, 0, 1, 2, 10, 100, 1000] # Numbers allowed without constants
|
|
316
|
+
max_small_integer: 10 # Max value allowed in range() or enumerate()
|
|
239
317
|
```
|
|
240
318
|
|
|
241
319
|
**JSON format also supported** (`.thailint.json`):
|
|
@@ -268,14 +346,20 @@ dry:
|
|
|
268
346
|
"python": {
|
|
269
347
|
"min_occurrences": 3
|
|
270
348
|
},
|
|
271
|
-
"
|
|
272
|
-
"cache_path": ".thailint-cache/dry.db",
|
|
349
|
+
"storage_mode": "memory",
|
|
273
350
|
"ignore": ["tests/", "__init__.py"]
|
|
351
|
+
},
|
|
352
|
+
"magic-numbers": {
|
|
353
|
+
"enabled": true,
|
|
354
|
+
"allowed_numbers": [-1, 0, 1, 2, 10, 100, 1000],
|
|
355
|
+
"max_small_integer": 10
|
|
274
356
|
}
|
|
275
357
|
}
|
|
276
358
|
```
|
|
277
359
|
|
|
278
|
-
See [Configuration Guide](
|
|
360
|
+
See [Configuration Guide](https://thai-lint.readthedocs.io/en/latest/configuration/) for complete reference.
|
|
361
|
+
|
|
362
|
+
**Need help with ignores?** See **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** for complete guide to all ignore levels (line, method, class, file, repository).
|
|
279
363
|
|
|
280
364
|
## Nesting Depth Linter
|
|
281
365
|
|
|
@@ -361,7 +445,7 @@ Common patterns to reduce nesting:
|
|
|
361
445
|
- **TypeScript**: Full support (if/for/while/try/switch)
|
|
362
446
|
- **JavaScript**: Supported via TypeScript parser
|
|
363
447
|
|
|
364
|
-
See [Nesting Linter Guide](
|
|
448
|
+
See [Nesting Linter Guide](https://thai-lint.readthedocs.io/en/latest/nesting-linter/) for comprehensive documentation and refactoring patterns.
|
|
365
449
|
|
|
366
450
|
## Single Responsibility Principle (SRP) Linter
|
|
367
451
|
|
|
@@ -478,13 +562,13 @@ Common patterns to fix SRP violations (discovered during dogfooding):
|
|
|
478
562
|
- **After**: Extract Class pattern applied - 5 focused classes (ConfigLoader, PatternValidator, RuleChecker, PathResolver, FilePlacementLinter)
|
|
479
563
|
- **Result**: Each class ≤8 methods, ≤150 LOC, single responsibility
|
|
480
564
|
|
|
481
|
-
See [SRP Linter Guide](
|
|
565
|
+
See [SRP Linter Guide](https://thai-lint.readthedocs.io/en/latest/srp-linter/) for comprehensive documentation and refactoring patterns.
|
|
482
566
|
|
|
483
567
|
## DRY Linter (Don't Repeat Yourself)
|
|
484
568
|
|
|
485
569
|
### Overview
|
|
486
570
|
|
|
487
|
-
The DRY linter detects duplicate code blocks across your entire project using token-based hashing with SQLite
|
|
571
|
+
The DRY linter detects duplicate code blocks across your entire project using token-based hashing with SQLite storage. It identifies identical or near-identical code that violates the Don't Repeat Yourself (DRY) principle, helping maintain code quality at scale.
|
|
488
572
|
|
|
489
573
|
### Quick Start
|
|
490
574
|
|
|
@@ -495,8 +579,8 @@ thailint dry .
|
|
|
495
579
|
# Use custom thresholds
|
|
496
580
|
thailint dry --min-lines 5 src/
|
|
497
581
|
|
|
498
|
-
#
|
|
499
|
-
thailint dry --
|
|
582
|
+
# Use tempfile storage for large projects
|
|
583
|
+
thailint dry --storage-mode tempfile src/
|
|
500
584
|
|
|
501
585
|
# Get JSON output
|
|
502
586
|
thailint dry --format json src/
|
|
@@ -519,10 +603,8 @@ dry:
|
|
|
519
603
|
typescript:
|
|
520
604
|
min_occurrences: 3 # TypeScript: require 3+ occurrences
|
|
521
605
|
|
|
522
|
-
#
|
|
523
|
-
|
|
524
|
-
cache_path: ".thailint-cache/dry.db"
|
|
525
|
-
cache_max_age_days: 30
|
|
606
|
+
# Storage settings
|
|
607
|
+
storage_mode: "memory" # Options: "memory" (default) or "tempfile"
|
|
526
608
|
|
|
527
609
|
# Ignore patterns
|
|
528
610
|
ignore:
|
|
@@ -543,11 +625,11 @@ dry:
|
|
|
543
625
|
3. Store hashes in SQLite database with file locations
|
|
544
626
|
4. Query for hashes appearing 2+ times across project
|
|
545
627
|
|
|
546
|
-
**SQLite
|
|
547
|
-
-
|
|
548
|
-
-
|
|
549
|
-
-
|
|
550
|
-
-
|
|
628
|
+
**SQLite Storage:**
|
|
629
|
+
- In-memory mode (default): Stores in RAM for best performance
|
|
630
|
+
- Tempfile mode: Stores in temporary disk file for large projects
|
|
631
|
+
- Fresh analysis on every run (no persistence between runs)
|
|
632
|
+
- Fast duplicate detection using B-tree indexes
|
|
551
633
|
|
|
552
634
|
### Example Violation
|
|
553
635
|
|
|
@@ -605,31 +687,14 @@ def validate_admin(admin_data):
|
|
|
605
687
|
return validate_credentials(admin_data)
|
|
606
688
|
```
|
|
607
689
|
|
|
608
|
-
### Cache Management
|
|
609
|
-
|
|
610
|
-
```bash
|
|
611
|
-
# Normal cached run (default)
|
|
612
|
-
thailint dry src/
|
|
613
|
-
|
|
614
|
-
# Force re-analysis (ignore cache)
|
|
615
|
-
thailint dry --no-cache src/
|
|
616
|
-
|
|
617
|
-
# Clear cache before running
|
|
618
|
-
thailint dry --clear-cache src/
|
|
619
|
-
|
|
620
|
-
# Manual cache cleanup
|
|
621
|
-
rm -rf .thailint-cache/dry.db
|
|
622
|
-
```
|
|
623
|
-
|
|
624
690
|
### Performance
|
|
625
691
|
|
|
626
|
-
| Operation | Performance |
|
|
692
|
+
| Operation | Performance | Storage Mode |
|
|
627
693
|
|-----------|-------------|--------------|
|
|
628
|
-
|
|
|
629
|
-
|
|
|
630
|
-
| 50 changed files | 0.5-1s | Partial cache |
|
|
694
|
+
| Scan (1000 files) | 1-3s | Memory (default) |
|
|
695
|
+
| Large project (5000+ files) | Use tempfile mode | Tempfile |
|
|
631
696
|
|
|
632
|
-
**
|
|
697
|
+
**Note**: Every run analyzes files fresh - no persistence between runs ensures accurate results
|
|
633
698
|
|
|
634
699
|
### Language Support
|
|
635
700
|
|
|
@@ -650,7 +715,159 @@ Built-in filters automatically exclude common non-duplication patterns:
|
|
|
650
715
|
3. **Extract Utility Module**: Move helper functions to shared utilities
|
|
651
716
|
4. **Template Method**: Use function parameters for variations
|
|
652
717
|
|
|
653
|
-
See [DRY Linter Guide](
|
|
718
|
+
See [DRY Linter Guide](https://thai-lint.readthedocs.io/en/latest/dry-linter/) for comprehensive documentation, storage modes, and refactoring patterns.
|
|
719
|
+
|
|
720
|
+
## Magic Numbers Linter
|
|
721
|
+
|
|
722
|
+
### Overview
|
|
723
|
+
|
|
724
|
+
The magic numbers linter detects unnamed numeric literals (magic numbers) that should be extracted to named constants. It uses AST analysis to identify numeric literals that lack meaningful context.
|
|
725
|
+
|
|
726
|
+
### What are Magic Numbers?
|
|
727
|
+
|
|
728
|
+
**Magic numbers** are unnamed numeric literals in code without explanation:
|
|
729
|
+
|
|
730
|
+
```python
|
|
731
|
+
# Bad - Magic numbers
|
|
732
|
+
timeout = 3600 # What is 3600?
|
|
733
|
+
max_retries = 5 # Why 5?
|
|
734
|
+
|
|
735
|
+
# Good - Named constants
|
|
736
|
+
TIMEOUT_SECONDS = 3600
|
|
737
|
+
MAX_RETRY_ATTEMPTS = 5
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Quick Start
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
# Check for magic numbers in current directory
|
|
744
|
+
thailint magic-numbers .
|
|
745
|
+
|
|
746
|
+
# Check specific directory
|
|
747
|
+
thailint magic-numbers src/
|
|
748
|
+
|
|
749
|
+
# Get JSON output
|
|
750
|
+
thailint magic-numbers --format json src/
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
### Configuration
|
|
754
|
+
|
|
755
|
+
Add to `.thailint.yaml`:
|
|
756
|
+
|
|
757
|
+
```yaml
|
|
758
|
+
magic-numbers:
|
|
759
|
+
enabled: true
|
|
760
|
+
allowed_numbers: [-1, 0, 1, 2, 10, 100, 1000]
|
|
761
|
+
max_small_integer: 10 # Max for range() to be acceptable
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
### Example Violation
|
|
765
|
+
|
|
766
|
+
**Code with magic numbers:**
|
|
767
|
+
```python
|
|
768
|
+
def calculate_timeout():
|
|
769
|
+
return 3600 # Magic number - what is 3600?
|
|
770
|
+
|
|
771
|
+
def process_items(items):
|
|
772
|
+
for i in range(100): # Magic number - why 100?
|
|
773
|
+
items[i] *= 1.5 # Magic number - what is 1.5?
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
**Violation messages:**
|
|
777
|
+
```
|
|
778
|
+
src/example.py:2 - Magic number 3600 should be a named constant
|
|
779
|
+
src/example.py:5 - Magic number 100 should be a named constant
|
|
780
|
+
src/example.py:6 - Magic number 1.5 should be a named constant
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**Refactored code:**
|
|
784
|
+
```python
|
|
785
|
+
TIMEOUT_SECONDS = 3600
|
|
786
|
+
MAX_ITEMS = 100
|
|
787
|
+
PRICE_MULTIPLIER = 1.5
|
|
788
|
+
|
|
789
|
+
def calculate_timeout():
|
|
790
|
+
return TIMEOUT_SECONDS
|
|
791
|
+
|
|
792
|
+
def process_items(items):
|
|
793
|
+
for i in range(MAX_ITEMS):
|
|
794
|
+
items[i] *= PRICE_MULTIPLIER
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Acceptable Contexts
|
|
798
|
+
|
|
799
|
+
The linter **does not** flag numbers in these contexts:
|
|
800
|
+
|
|
801
|
+
| Context | Example | Why Acceptable |
|
|
802
|
+
|---------|---------|----------------|
|
|
803
|
+
| Constants | `MAX_SIZE = 100` | UPPERCASE name provides context |
|
|
804
|
+
| Small `range()` | `range(5)` | Small loop bounds are clear |
|
|
805
|
+
| Test files | `test_*.py` | Test data can be literal |
|
|
806
|
+
| Allowed numbers | `-1, 0, 1, 2, 10` | Common values are self-explanatory |
|
|
807
|
+
|
|
808
|
+
### Refactoring Patterns
|
|
809
|
+
|
|
810
|
+
**Pattern 1: Extract to Module Constants**
|
|
811
|
+
```python
|
|
812
|
+
# Before
|
|
813
|
+
def connect():
|
|
814
|
+
timeout = 30
|
|
815
|
+
retries = 3
|
|
816
|
+
|
|
817
|
+
# After
|
|
818
|
+
DEFAULT_TIMEOUT_SECONDS = 30
|
|
819
|
+
DEFAULT_MAX_RETRIES = 3
|
|
820
|
+
|
|
821
|
+
def connect():
|
|
822
|
+
timeout = DEFAULT_TIMEOUT_SECONDS
|
|
823
|
+
retries = DEFAULT_MAX_RETRIES
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
**Pattern 2: Extract with Units in Name**
|
|
827
|
+
```python
|
|
828
|
+
# Before
|
|
829
|
+
delay = 3600 # Is this seconds? Minutes?
|
|
830
|
+
|
|
831
|
+
# After
|
|
832
|
+
TASK_DELAY_SECONDS = 3600 # Clear unit
|
|
833
|
+
|
|
834
|
+
delay = TASK_DELAY_SECONDS
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**Pattern 3: Use Standard Library**
|
|
838
|
+
```python
|
|
839
|
+
# Before
|
|
840
|
+
if status == 200:
|
|
841
|
+
return "success"
|
|
842
|
+
|
|
843
|
+
# After
|
|
844
|
+
from http import HTTPStatus
|
|
845
|
+
|
|
846
|
+
if status == HTTPStatus.OK:
|
|
847
|
+
return "success"
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### Language Support
|
|
851
|
+
|
|
852
|
+
- **Python**: Full support (int, float, scientific notation)
|
|
853
|
+
- **TypeScript**: Full support (int, float, scientific notation)
|
|
854
|
+
- **JavaScript**: Supported via TypeScript parser
|
|
855
|
+
|
|
856
|
+
### Ignoring Violations
|
|
857
|
+
|
|
858
|
+
```python
|
|
859
|
+
# Line-level ignore
|
|
860
|
+
timeout = 3600 # thailint: ignore[magic-numbers] - Industry standard
|
|
861
|
+
|
|
862
|
+
# Method-level ignore
|
|
863
|
+
def get_ports(): # thailint: ignore[magic-numbers] - Standard ports
|
|
864
|
+
return {80: "HTTP", 443: "HTTPS"}
|
|
865
|
+
|
|
866
|
+
# File-level ignore
|
|
867
|
+
# thailint: ignore-file[magic-numbers]
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
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.
|
|
654
871
|
|
|
655
872
|
## Pre-commit Hooks
|
|
656
873
|
|
|
@@ -712,7 +929,7 @@ repos:
|
|
|
712
929
|
pass_filenames: true
|
|
713
930
|
```
|
|
714
931
|
|
|
715
|
-
See **[Pre-commit Hooks Guide](
|
|
932
|
+
See **[Pre-commit Hooks Guide](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** for complete documentation, troubleshooting, and advanced configuration.
|
|
716
933
|
|
|
717
934
|
## Common Use Cases
|
|
718
935
|
|
|
@@ -765,51 +982,100 @@ def test_no_violations():
|
|
|
765
982
|
### Setup Development Environment
|
|
766
983
|
|
|
767
984
|
```bash
|
|
768
|
-
# Install
|
|
769
|
-
|
|
985
|
+
# Install dependencies and activate virtualenv
|
|
986
|
+
just init
|
|
770
987
|
|
|
771
|
-
#
|
|
772
|
-
|
|
988
|
+
# Or manually:
|
|
989
|
+
poetry install
|
|
990
|
+
source $(poetry env info --path)/bin/activate
|
|
773
991
|
```
|
|
774
992
|
|
|
775
993
|
### Running Tests
|
|
776
994
|
|
|
777
995
|
```bash
|
|
778
|
-
# Run all tests
|
|
779
|
-
|
|
996
|
+
# Run all tests (parallel mode - fast)
|
|
997
|
+
just test
|
|
780
998
|
|
|
781
|
-
# Run with coverage
|
|
782
|
-
|
|
999
|
+
# Run with coverage (serial mode)
|
|
1000
|
+
just test-coverage
|
|
783
1001
|
|
|
784
1002
|
# Run specific test
|
|
785
|
-
pytest tests/test_cli.py::test_hello_command
|
|
1003
|
+
poetry run pytest tests/test_cli.py::test_hello_command -v
|
|
786
1004
|
```
|
|
787
1005
|
|
|
788
1006
|
### Code Quality
|
|
789
1007
|
|
|
790
1008
|
```bash
|
|
791
|
-
#
|
|
792
|
-
|
|
1009
|
+
# Fast linting (Ruff only - use during development)
|
|
1010
|
+
just lint
|
|
1011
|
+
|
|
1012
|
+
# Comprehensive linting (Ruff + Pylint + Flake8 + MyPy)
|
|
1013
|
+
just lint-all
|
|
1014
|
+
|
|
1015
|
+
# Security scanning
|
|
1016
|
+
just lint-security
|
|
793
1017
|
|
|
794
|
-
#
|
|
795
|
-
|
|
1018
|
+
# Complexity analysis (Radon + Xenon + Nesting)
|
|
1019
|
+
just lint-complexity
|
|
796
1020
|
|
|
797
|
-
#
|
|
798
|
-
|
|
1021
|
+
# SOLID principles (SRP)
|
|
1022
|
+
just lint-solid
|
|
1023
|
+
|
|
1024
|
+
# DRY principles (duplicate code detection)
|
|
1025
|
+
just lint-dry
|
|
1026
|
+
|
|
1027
|
+
# ALL quality checks (runs everything)
|
|
1028
|
+
just lint-full
|
|
1029
|
+
|
|
1030
|
+
# Auto-fix formatting issues
|
|
1031
|
+
just format
|
|
799
1032
|
```
|
|
800
1033
|
|
|
801
|
-
###
|
|
1034
|
+
### Dogfooding (Lint Our Own Code)
|
|
1035
|
+
|
|
1036
|
+
```bash
|
|
1037
|
+
# Lint file placement
|
|
1038
|
+
just lint-placement
|
|
1039
|
+
|
|
1040
|
+
# Check nesting depth
|
|
1041
|
+
just lint-nesting
|
|
1042
|
+
|
|
1043
|
+
# Check for magic numbers
|
|
1044
|
+
poetry run thai-lint magic-numbers src/
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Building and Publishing
|
|
802
1048
|
|
|
803
1049
|
```bash
|
|
804
1050
|
# Build Python package
|
|
805
1051
|
poetry build
|
|
806
1052
|
|
|
807
|
-
# Build Docker image locally
|
|
1053
|
+
# Build Docker image locally
|
|
808
1054
|
docker build -t washad/thailint:latest .
|
|
1055
|
+
|
|
1056
|
+
# Publish to PyPI and Docker Hub (runs tests + linting + version bump)
|
|
1057
|
+
just publish
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
### Quick Development Workflows
|
|
1061
|
+
|
|
1062
|
+
```bash
|
|
1063
|
+
# Make changes, then run quality checks
|
|
1064
|
+
just lint-full
|
|
1065
|
+
|
|
1066
|
+
# Share changes for collaboration (skips hooks)
|
|
1067
|
+
just share "WIP: feature description"
|
|
1068
|
+
|
|
1069
|
+
# Clean up cache and artifacts
|
|
1070
|
+
just clean
|
|
809
1071
|
```
|
|
810
1072
|
|
|
1073
|
+
See `just --list` or `just help` for all available commands.
|
|
1074
|
+
|
|
811
1075
|
## Docker Usage
|
|
812
1076
|
|
|
1077
|
+
### Basic Docker Commands
|
|
1078
|
+
|
|
813
1079
|
```bash
|
|
814
1080
|
# Pull published image
|
|
815
1081
|
docker pull washad/thailint:latest
|
|
@@ -838,22 +1104,62 @@ docker run --rm -v $(pwd):/data \
|
|
|
838
1104
|
washad/thailint:latest file-placement --format json /data
|
|
839
1105
|
```
|
|
840
1106
|
|
|
1107
|
+
### Docker with Sibling Directories (Advanced)
|
|
1108
|
+
|
|
1109
|
+
For complex Docker setups with sibling directories, use `--project-root` for explicit control:
|
|
1110
|
+
|
|
1111
|
+
```bash
|
|
1112
|
+
# Scenario: Monorepo with separate config and code directories
|
|
1113
|
+
# Directory structure:
|
|
1114
|
+
# /workspace/
|
|
1115
|
+
# ├── config/ # Contains .thailint.yaml
|
|
1116
|
+
# ├── backend/app/ # Python backend code
|
|
1117
|
+
# ├── frontend/ # TypeScript frontend
|
|
1118
|
+
# └── tools/ # Build tools
|
|
1119
|
+
|
|
1120
|
+
# Explicit project root (recommended for Docker)
|
|
1121
|
+
docker run --rm -v /path/to/workspace:/workspace \
|
|
1122
|
+
washad/thailint:latest \
|
|
1123
|
+
--project-root /workspace/config \
|
|
1124
|
+
magic-numbers /workspace/backend/
|
|
1125
|
+
|
|
1126
|
+
# Config path inference (automatic - no --project-root needed)
|
|
1127
|
+
docker run --rm -v /path/to/workspace:/workspace \
|
|
1128
|
+
washad/thailint:latest \
|
|
1129
|
+
--config /workspace/config/.thailint.yaml \
|
|
1130
|
+
magic-numbers /workspace/backend/
|
|
1131
|
+
|
|
1132
|
+
# Lint multiple sibling directories with shared config
|
|
1133
|
+
docker run --rm -v /path/to/workspace:/workspace \
|
|
1134
|
+
washad/thailint:latest \
|
|
1135
|
+
--project-root /workspace/config \
|
|
1136
|
+
nesting /workspace/backend/ /workspace/frontend/
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
**When to use `--project-root` in Docker:**
|
|
1140
|
+
- **Sibling directory structures** - When config/code aren't nested
|
|
1141
|
+
- **Monorepos** - Multiple projects sharing one config
|
|
1142
|
+
- **CI/CD** - Explicit paths prevent auto-detection issues
|
|
1143
|
+
- **Ignore patterns** - Ensures patterns resolve from correct base directory
|
|
1144
|
+
|
|
841
1145
|
## Documentation
|
|
842
1146
|
|
|
843
1147
|
### Comprehensive Guides
|
|
844
1148
|
|
|
845
|
-
- **[Getting Started](
|
|
846
|
-
- **[Configuration Reference](
|
|
847
|
-
- **[
|
|
848
|
-
- **[
|
|
849
|
-
- **[
|
|
850
|
-
- **[
|
|
851
|
-
- **[
|
|
852
|
-
- **[
|
|
853
|
-
- **[
|
|
854
|
-
- **[
|
|
855
|
-
- **[
|
|
856
|
-
- **[
|
|
1149
|
+
- **[Getting Started](https://thai-lint.readthedocs.io/en/latest/getting-started/)** - Installation, first lint, basic config
|
|
1150
|
+
- **[Configuration Reference](https://thai-lint.readthedocs.io/en/latest/configuration/)** - Complete config options (YAML/JSON)
|
|
1151
|
+
- **[How to Ignore Violations](https://thai-lint.readthedocs.io/en/latest/how-to-ignore-violations/)** - Complete guide to all ignore levels
|
|
1152
|
+
- **[API Reference](https://thai-lint.readthedocs.io/en/latest/api-reference/)** - Library API documentation
|
|
1153
|
+
- **[CLI Reference](https://thai-lint.readthedocs.io/en/latest/cli-reference/)** - All CLI commands and options
|
|
1154
|
+
- **[Deployment Modes](https://thai-lint.readthedocs.io/en/latest/deployment-modes/)** - CLI, Library, and Docker usage
|
|
1155
|
+
- **[File Placement Linter](https://thai-lint.readthedocs.io/en/latest/file-placement-linter/)** - Detailed linter guide
|
|
1156
|
+
- **[Magic Numbers Linter](https://thai-lint.readthedocs.io/en/latest/magic-numbers-linter/)** - Magic numbers detection guide
|
|
1157
|
+
- **[Nesting Depth Linter](https://thai-lint.readthedocs.io/en/latest/nesting-linter/)** - Nesting depth analysis guide
|
|
1158
|
+
- **[SRP Linter](https://thai-lint.readthedocs.io/en/latest/srp-linter/)** - Single Responsibility Principle guide
|
|
1159
|
+
- **[DRY Linter](https://thai-lint.readthedocs.io/en/latest/dry-linter/)** - Duplicate code detection guide
|
|
1160
|
+
- **[Pre-commit Hooks](https://thai-lint.readthedocs.io/en/latest/pre-commit-hooks/)** - Automated quality checks
|
|
1161
|
+
- **[Publishing Guide](https://thai-lint.readthedocs.io/en/latest/releasing/)** - Release and publishing workflow
|
|
1162
|
+
- **[Publishing Checklist](https://thai-lint.readthedocs.io/en/latest/publishing-checklist/)** - Post-publication validation
|
|
857
1163
|
|
|
858
1164
|
### Examples
|
|
859
1165
|
|