thailint 0.3.2__py3-none-any.whl → 0.4.1__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 CHANGED
@@ -362,6 +362,141 @@ def config_reset(ctx, yes: bool):
362
362
  sys.exit(1)
363
363
 
364
364
 
365
+ @cli.command("init-config")
366
+ @click.option(
367
+ "--preset",
368
+ "-p",
369
+ type=click.Choice(["strict", "standard", "lenient"]),
370
+ default="standard",
371
+ help="Configuration preset",
372
+ )
373
+ @click.option("--non-interactive", is_flag=True, help="Skip interactive prompts (for AI agents)")
374
+ @click.option("--force", is_flag=True, help="Overwrite existing .thailint.yaml file")
375
+ @click.option(
376
+ "--output", "-o", type=click.Path(), default=".thailint.yaml", help="Output file path"
377
+ )
378
+ def init_config(preset: str, non_interactive: bool, force: bool, output: str):
379
+ """
380
+ Generate a .thailint.yaml configuration file with preset values.
381
+
382
+ Creates a richly-commented configuration file with sensible defaults
383
+ and optional customizations for different strictness levels.
384
+
385
+ For AI agents, use --non-interactive mode:
386
+ thailint init-config --non-interactive --preset lenient
387
+
388
+ Presets:
389
+ strict: Minimal allowed numbers (only -1, 0, 1)
390
+ standard: Balanced defaults (includes 2, 3, 4, 5, 10, 100, 1000)
391
+ lenient: Includes time conversions (adds 60, 3600)
392
+
393
+ Examples:
394
+
395
+ \\b
396
+ # Interactive mode (default, for humans)
397
+ thailint init-config
398
+
399
+ \\b
400
+ # Non-interactive mode (for AI agents)
401
+ thailint init-config --non-interactive
402
+
403
+ \\b
404
+ # Generate with lenient preset
405
+ thailint init-config --preset lenient
406
+
407
+ \\b
408
+ # Overwrite existing config
409
+ thailint init-config --force
410
+
411
+ \\b
412
+ # Custom output path
413
+ thailint init-config --output my-config.yaml
414
+ """
415
+ output_path = Path(output)
416
+
417
+ # Check if file exists (unless --force)
418
+ if output_path.exists() and not force:
419
+ click.echo(f"Error: {output} already exists", err=True)
420
+ click.echo("", err=True)
421
+ click.echo("Use --force to overwrite:", err=True)
422
+ click.echo(" thailint init-config --force", err=True)
423
+ sys.exit(1)
424
+
425
+ # Interactive mode: Ask user for preferences
426
+ if not non_interactive:
427
+ click.echo("thai-lint Configuration Generator")
428
+ click.echo("=" * 50)
429
+ click.echo("")
430
+ click.echo("This will create a .thailint.yaml configuration file.")
431
+ click.echo("For non-interactive mode (AI agents), use:")
432
+ click.echo(" thailint init-config --non-interactive")
433
+ click.echo("")
434
+
435
+ # Ask for preset
436
+ click.echo("Available presets:")
437
+ click.echo(" strict: Only -1, 0, 1 allowed (strictest)")
438
+ click.echo(" standard: -1, 0, 1, 2, 3, 4, 5, 10, 100, 1000 (balanced)")
439
+ click.echo(" lenient: Includes time conversions 60, 3600 (most permissive)")
440
+ click.echo("")
441
+
442
+ preset = click.prompt(
443
+ "Choose preset", type=click.Choice(["strict", "standard", "lenient"]), default=preset
444
+ )
445
+
446
+ # Generate config based on preset
447
+ config_content = _generate_config_content(preset)
448
+
449
+ # Write config file
450
+ try:
451
+ output_path.write_text(config_content, encoding="utf-8")
452
+ click.echo("")
453
+ click.echo(f"✓ Created {output}")
454
+ click.echo(f"✓ Preset: {preset}")
455
+ click.echo("")
456
+ click.echo("Next steps:")
457
+ click.echo(f" 1. Review and customize {output}")
458
+ click.echo(" 2. Run: thailint magic-numbers .")
459
+ click.echo(" 3. See docs: https://github.com/your-org/thai-lint")
460
+ except OSError as e:
461
+ click.echo(f"Error writing config file: {e}", err=True)
462
+ sys.exit(1)
463
+
464
+
465
+ def _generate_config_content(preset: str) -> str:
466
+ """Generate config file content based on preset."""
467
+ # Preset configurations
468
+ presets = {
469
+ "strict": {
470
+ "allowed_numbers": "[-1, 0, 1]",
471
+ "max_small_integer": "3",
472
+ "description": "Strict (only universal values)",
473
+ },
474
+ "standard": {
475
+ "allowed_numbers": "[-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000]",
476
+ "max_small_integer": "10",
477
+ "description": "Standard (balanced defaults)",
478
+ },
479
+ "lenient": {
480
+ "allowed_numbers": "[-1, 0, 1, 2, 3, 4, 5, 10, 60, 100, 1000, 3600]",
481
+ "max_small_integer": "10",
482
+ "description": "Lenient (includes time conversions)",
483
+ },
484
+ }
485
+
486
+ config = presets[preset]
487
+
488
+ # Read template
489
+ template_path = Path(__file__).parent / "templates" / "thailint_config_template.yaml"
490
+ template = template_path.read_text(encoding="utf-8")
491
+
492
+ # Replace placeholders
493
+ content = template.replace("{{PRESET}}", config["description"])
494
+ content = content.replace("{{ALLOWED_NUMBERS}}", config["allowed_numbers"])
495
+ content = content.replace("{{MAX_SMALL_INTEGER}}", config["max_small_integer"])
496
+
497
+ return content
498
+
499
+
365
500
  @cli.command("file-placement")
366
501
  @click.argument("paths", nargs=-1, type=click.Path())
367
502
  @click.option("--config", "-c", "config_file", type=click.Path(), help="Path to config file")
@@ -4,7 +4,7 @@ Purpose: Configuration schema for magic numbers linter
4
4
  Scope: MagicNumberConfig dataclass with allowed_numbers and max_small_integer settings
5
5
 
6
6
  Overview: Defines configuration schema for magic numbers linter. Provides MagicNumberConfig dataclass
7
- with allowed_numbers set (default includes common acceptable numbers like 0, 1, 2, -1, 10, 100, 1000)
7
+ with allowed_numbers set (default includes common acceptable numbers like -1, 0, 1, 2, 3, 4, 5, 10, 100, 1000)
8
8
  and max_small_integer threshold (default 10) for range() contexts. Supports per-file and per-directory
9
9
  config overrides through from_dict class method. Validates that configuration values are appropriate
10
10
  types. Integrates with orchestrator's configuration system to allow users to customize allowed numbers
@@ -29,8 +29,11 @@ class MagicNumberConfig:
29
29
  """Configuration for magic numbers linter."""
30
30
 
31
31
  enabled: bool = True
32
- allowed_numbers: set[int | float] = field(default_factory=lambda: {-1, 0, 1, 2, 10, 100, 1000})
32
+ allowed_numbers: set[int | float] = field(
33
+ default_factory=lambda: {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000}
34
+ )
33
35
  max_small_integer: int = 10
36
+ ignore: list[str] = field(default_factory=list)
34
37
 
35
38
  def __post_init__(self) -> None:
36
39
  """Validate configuration values."""
@@ -54,18 +57,26 @@ class MagicNumberConfig:
54
57
  lang_config = config[language]
55
58
  allowed_numbers = set(
56
59
  lang_config.get(
57
- "allowed_numbers", config.get("allowed_numbers", {-1, 0, 1, 2, 10, 100, 1000})
60
+ "allowed_numbers",
61
+ config.get("allowed_numbers", {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000}),
58
62
  )
59
63
  )
60
64
  max_small_integer = lang_config.get(
61
65
  "max_small_integer", config.get("max_small_integer", 10)
62
66
  )
63
67
  else:
64
- allowed_numbers = set(config.get("allowed_numbers", {-1, 0, 1, 2, 10, 100, 1000}))
68
+ allowed_numbers = set(
69
+ config.get("allowed_numbers", {-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000})
70
+ )
65
71
  max_small_integer = config.get("max_small_integer", 10)
66
72
 
73
+ ignore_patterns = config.get("ignore", [])
74
+ if not isinstance(ignore_patterns, list):
75
+ ignore_patterns = []
76
+
67
77
  return cls(
68
78
  enabled=config.get("enabled", True),
69
79
  allowed_numbers=allowed_numbers,
70
80
  max_small_integer=max_small_integer,
81
+ ignore=ignore_patterns,
71
82
  )
@@ -24,6 +24,7 @@ Implementation: Composition pattern with helper classes, AST-based analysis with
24
24
  """
25
25
 
26
26
  import ast
27
+ from pathlib import Path
27
28
 
28
29
  from src.core.base import BaseLintContext, MultiLanguageLintRule
29
30
  from src.core.linter_utils import load_linter_config
@@ -98,6 +99,48 @@ class MagicNumberRule(MultiLanguageLintRule): # thailint: ignore[srp]
98
99
  return None
99
100
  return load_linter_config(context, "magic_numbers", MagicNumberConfig)
100
101
 
102
+ def _is_file_ignored(self, context: BaseLintContext, config: MagicNumberConfig) -> bool:
103
+ """Check if file matches ignore patterns.
104
+
105
+ Args:
106
+ context: Lint context
107
+ config: Magic numbers configuration
108
+
109
+ Returns:
110
+ True if file should be ignored
111
+ """
112
+ if not config.ignore:
113
+ return False
114
+
115
+ if not context.file_path:
116
+ return False
117
+
118
+ file_path = Path(context.file_path)
119
+ for pattern in config.ignore:
120
+ if self._matches_pattern(file_path, pattern):
121
+ return True
122
+ return False
123
+
124
+ def _matches_pattern(self, file_path: Path, pattern: str) -> bool:
125
+ """Check if file path matches a glob pattern.
126
+
127
+ Args:
128
+ file_path: Path to check
129
+ pattern: Glob pattern (e.g., "test/**", "**/test_*.py", "specific/file.py")
130
+
131
+ Returns:
132
+ True if path matches pattern
133
+ """
134
+ # Try glob pattern matching first (handles **, *, etc.)
135
+ if file_path.match(pattern):
136
+ return True
137
+
138
+ # Also check if pattern is a substring (for partial path matching)
139
+ if pattern in str(file_path):
140
+ return True
141
+
142
+ return False
143
+
101
144
  def _check_python(self, context: BaseLintContext, config: MagicNumberConfig) -> list[Violation]:
102
145
  """Check Python code for magic number violations.
103
146
 
@@ -108,6 +151,9 @@ class MagicNumberRule(MultiLanguageLintRule): # thailint: ignore[srp]
108
151
  Returns:
109
152
  List of violations found in Python code
110
153
  """
154
+ if self._is_file_ignored(context, config):
155
+ return []
156
+
111
157
  tree = self._parse_python_code(context.file_content)
112
158
  if tree is None:
113
159
  return []
@@ -267,6 +313,9 @@ class MagicNumberRule(MultiLanguageLintRule): # thailint: ignore[srp]
267
313
  Returns:
268
314
  List of violations found in TypeScript/JavaScript code
269
315
  """
316
+ if self._is_file_ignored(context, config):
317
+ return []
318
+
270
319
  analyzer = TypeScriptMagicNumberAnalyzer()
271
320
  root_node = analyzer.parse_typescript(context.file_content or "")
272
321
  if root_node is None:
@@ -0,0 +1,132 @@
1
+ # thai-lint Configuration File
2
+ # Generated by: thailint init-config
3
+ #
4
+ # For non-interactive mode (AI agents): thailint init-config --non-interactive
5
+ #
6
+ # Full documentation: https://github.com/your-org/thai-lint
7
+
8
+ # ============================================================================
9
+ # MAGIC NUMBERS LINTER
10
+ # ============================================================================
11
+ # Detects unnamed numeric literals that should be extracted as constants
12
+ #
13
+ # Preset: {{PRESET}}
14
+ #
15
+ magic-numbers:
16
+ enabled: true
17
+
18
+ # Numbers that are acceptable without being named constants
19
+ # Default: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000]
20
+ allowed_numbers: {{ALLOWED_NUMBERS}}
21
+
22
+ # Maximum integer allowed in range() or enumerate() without flagging
23
+ # Default: 10
24
+ max_small_integer: {{MAX_SMALL_INTEGER}}
25
+
26
+ # -------------------------------------------------------------------------
27
+ # OPTIONAL: Uncomment to add time conversions (lenient mode)
28
+ # -------------------------------------------------------------------------
29
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 60, 100, 1000, 3600]
30
+
31
+ # -------------------------------------------------------------------------
32
+ # OPTIONAL: Uncomment to add common HTTP status codes
33
+ # -------------------------------------------------------------------------
34
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 200, 201, 204, 400, 401, 403, 404, 500, 502, 503, 1000]
35
+
36
+ # -------------------------------------------------------------------------
37
+ # OPTIONAL: Uncomment to add decimal proportions (0.0-1.0)
38
+ # -------------------------------------------------------------------------
39
+ # allowed_numbers: [-1, 0, 1, 2, 3, 4, 5, 10, 100, 1000, 0.0, 0.1, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.9, 1.0]
40
+
41
+ # ============================================================================
42
+ # NESTING LINTER
43
+ # ============================================================================
44
+ # Checks for excessive nesting depth (if/for/while/try statements)
45
+ #
46
+ nesting:
47
+ enabled: true
48
+
49
+ # Maximum nesting depth allowed
50
+ # Default: 4
51
+ max_nesting_depth: 4
52
+
53
+ # ============================================================================
54
+ # SINGLE RESPONSIBILITY PRINCIPLE (SRP) LINTER
55
+ # ============================================================================
56
+ # Detects classes that may have too many responsibilities
57
+ #
58
+ srp:
59
+ enabled: true
60
+
61
+ # Maximum methods per class
62
+ # Default: 7
63
+ max_methods: 7
64
+
65
+ # Maximum lines of code per class
66
+ # Default: 200
67
+ max_loc: 200
68
+
69
+ # ============================================================================
70
+ # DRY (DON'T REPEAT YOURSELF) LINTER
71
+ # ============================================================================
72
+ # Detects duplicate code blocks
73
+ #
74
+ dry:
75
+ enabled: true
76
+
77
+ # Minimum lines for a block to be considered duplicate
78
+ # Default: 6
79
+ min_duplicate_lines: 6
80
+
81
+ # Enable SQLite caching for faster incremental scans
82
+ # Default: true
83
+ cache_enabled: true
84
+
85
+ # Cache file location (relative to project root)
86
+ # Default: .thailint-cache/dry.db
87
+ cache_path: .thailint-cache/dry.db
88
+
89
+ # ============================================================================
90
+ # FILE PLACEMENT LINTER
91
+ # ============================================================================
92
+ # Ensures files are in appropriate directories
93
+ #
94
+ file-placement:
95
+ enabled: true
96
+
97
+ # Rules for file placement
98
+ rules:
99
+ # Test files should be in tests/ directory
100
+ - pattern: "test_*.py"
101
+ required_dir: "tests/"
102
+ message: "Test files must be in tests/ directory"
103
+
104
+ # Config files should be in config/ or root
105
+ - pattern: "*config*.py"
106
+ required_dir: ["config/", "./"]
107
+ message: "Config files should be in config/ or project root"
108
+
109
+ # ============================================================================
110
+ # GLOBAL SETTINGS
111
+ # ============================================================================
112
+ #
113
+ # Exclude patterns (files/directories to ignore)
114
+ exclude:
115
+ - ".git/"
116
+ - ".venv/"
117
+ - "venv/"
118
+ - "node_modules/"
119
+ - "__pycache__/"
120
+ - "*.pyc"
121
+ - ".pytest_cache/"
122
+ - "dist/"
123
+ - "build/"
124
+ - ".eggs/"
125
+
126
+ # Output format (text or json)
127
+ # Default: text
128
+ output_format: text
129
+
130
+ # Exit with error code if violations found
131
+ # Default: true
132
+ fail_on_violations: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: thailint
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: The AI Linter - Enterprise-grade linting and governance for AI-generated code across multiple languages
5
5
  License: MIT
6
6
  Keywords: linter,ai,code-quality,static-analysis,file-placement,governance,multi-language,cli,docker,python
@@ -35,11 +35,20 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
37
37
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
38
- [![Tests](https://img.shields.io/badge/tests-253%2F253%20passing-brightgreen.svg)](tests/)
39
- [![Coverage](https://img.shields.io/badge/coverage-88%25-brightgreen.svg)](htmlcov/)
38
+ [![Tests](https://img.shields.io/badge/tests-263%2F263%20passing-brightgreen.svg)](tests/)
39
+ [![Coverage](https://img.shields.io/badge/coverage-87%25-brightgreen.svg)](htmlcov/)
40
40
 
41
41
  The AI Linter - Enterprise-ready linting and governance for AI-generated code across multiple languages.
42
42
 
43
+ ## Documentation
44
+
45
+ **New to thailint?** Start here:
46
+ - **[Quick Start Guide](docs/quick-start.md)** - Get running in 5 minutes
47
+ - **[Configuration Reference](docs/configuration.md)** - Complete config options for all linters
48
+ - **[Troubleshooting Guide](docs/troubleshooting.md)** - Common issues and solutions
49
+
50
+ **Full Documentation:** Browse the **[docs/](docs/)** folder for comprehensive guides covering installation, all linters, configuration patterns, and integration examples.
51
+
43
52
  ## Overview
44
53
 
45
54
  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.
@@ -55,6 +64,8 @@ We're not trying to replace the wonderful existing linters like Pylint, ESLint,
55
64
 
56
65
  thailint complements your existing linting stack by catching the patterns AI tools repeatedly miss.
57
66
 
67
+ **Complete documentation available in the [docs/](docs/) folder** covering installation, configuration, all linters, and troubleshooting.
68
+
58
69
  ## Features
59
70
 
60
71
  ### Core Capabilities
@@ -151,6 +162,8 @@ thailint dry --config .thailint.yaml src/
151
162
  thailint dry --format json src/
152
163
  ```
153
164
 
165
+ **New to thailint?** See the **[Quick Start Guide](docs/quick-start.md)** for a complete walkthrough including config generation, understanding output, and next steps.
166
+
154
167
  ### Library Mode
155
168
 
156
169
  ```python
@@ -2,7 +2,7 @@ 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=1qhjXAy7JDdav90xQ5pOeH1_f1A9qYJLIwxTQzq3oiU,35657
5
+ src/cli.py,sha256=zRl6FHyr24puWli77ogFtuPFWKbTNMI93kmu1V4kPEQ,40369
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
@@ -47,9 +47,9 @@ src/linters/file_placement/pattern_validator.py,sha256=eMt5GB5lgJMhhQACOlfDXQFfS
47
47
  src/linters/file_placement/rule_checker.py,sha256=JONXcaYxZ8CM_7Zg6Th01p5cve1rJ8YkReAUJ44nfUg,7795
48
48
  src/linters/file_placement/violation_factory.py,sha256=NkQmBcgpa3g3W2ZdFZNQ5djLVP4x9OKs65d7F1rCKvM,6040
49
49
  src/linters/magic_numbers/__init__.py,sha256=17dkCUf0uiYLvpOZF01VDojj92NzxXZMtRhrSBUzsdc,1689
50
- src/linters/magic_numbers/config.py,sha256=xtT1kFD68VScUf5BRL1mpzWUiQLIWoI6iqniwBu0GgY,2946
50
+ src/linters/magic_numbers/config.py,sha256=3zV6ZNezouBWUYy4kMw5PUlPNvIWXVwOxTz1moZfRoI,3270
51
51
  src/linters/magic_numbers/context_analyzer.py,sha256=cGXozlKll10Zao56c2E8ThIyH2mSQaPaUau_g7ngRLw,8446
52
- src/linters/magic_numbers/linter.py,sha256=f240nyxsSN9-ttF6c7gA0LNwo6mBd85bWVO2I45G3DI,16354
52
+ src/linters/magic_numbers/linter.py,sha256=_AIlPHVdZIi7hVZ-VmlBLw9OqdVPKAdNzHnPhTSiJb8,17768
53
53
  src/linters/magic_numbers/python_analyzer.py,sha256=0u1cFaaFCqOW5yeW-YbmPoZuVIeN_KtmkFyyxup6aR0,2803
54
54
  src/linters/magic_numbers/typescript_analyzer.py,sha256=DCYRdxjgMd6PkhJWKnc1W-S1T0sa-F9AHCLV2JwcR8g,7468
55
55
  src/linters/magic_numbers/violation_builder.py,sha256=SqIQv3N9lpP2GRC1TC5InrvaEdrAq24V7Ec2Xj5olb0,3308
@@ -73,10 +73,11 @@ src/linters/srp/violation_builder.py,sha256=jaIjVtRYWUTs1SVJVwd0FxCojo0DxhPzfhyf
73
73
  src/orchestrator/__init__.py,sha256=XXLDJq2oaB-TpP2Y97GRnde9EkITGuFCmuLrDfxI9nY,245
74
74
  src/orchestrator/core.py,sha256=zb4H4HtDNLmnsRCUXI3oNtfM3T-nTPW9Q2pAbI61VEs,8374
75
75
  src/orchestrator/language_detector.py,sha256=rHyVMApit80NTTNyDH1ObD1usKD8LjGmH3DwqNAWYGc,2736
76
+ src/templates/thailint_config_template.yaml,sha256=u8WFv2coE4uqfgf_slw7xjo4kGYIowDm1RIgxsKQzrE,4275
76
77
  src/utils/__init__.py,sha256=NiBtKeQ09Y3kuUzeN4O1JNfUIYPQDS2AP1l5ODq-Dec,125
77
78
  src/utils/project_root.py,sha256=ldv2-XeMT0IElpSgrHdTaP2CUfwmdZix8vQ2qXO1O5s,2735
78
- thailint-0.3.2.dist-info/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
79
- thailint-0.3.2.dist-info/METADATA,sha256=P46brGR5esX9nGSoCtedmac_zupk8pAN4iHS2dJhM_g,33249
80
- thailint-0.3.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
81
- thailint-0.3.2.dist-info/entry_points.txt,sha256=l7DQJgU18sVLDpSaXOXY3lLhnQHQIRrSJZTQjG1cEAk,62
82
- thailint-0.3.2.dist-info/RECORD,,
79
+ thailint-0.4.1.dist-info/LICENSE,sha256=kxh1J0Sb62XvhNJ6MZsVNe8PqNVJ7LHRn_EWa-T3djw,1070
80
+ thailint-0.4.1.dist-info/METADATA,sha256=eTR4Su6Xbo9tMZWxoefVZvDUJndYmF0JD6GhfhiVGOk,34038
81
+ thailint-0.4.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
82
+ thailint-0.4.1.dist-info/entry_points.txt,sha256=l7DQJgU18sVLDpSaXOXY3lLhnQHQIRrSJZTQjG1cEAk,62
83
+ thailint-0.4.1.dist-info/RECORD,,