thailint 0.1.5__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.
Files changed (91) hide show
  1. src/__init__.py +7 -2
  2. src/analyzers/__init__.py +23 -0
  3. src/analyzers/typescript_base.py +148 -0
  4. src/api.py +1 -1
  5. src/cli.py +1111 -144
  6. src/config.py +12 -33
  7. src/core/base.py +102 -5
  8. src/core/cli_utils.py +206 -0
  9. src/core/config_parser.py +126 -0
  10. src/core/linter_utils.py +168 -0
  11. src/core/registry.py +17 -92
  12. src/core/rule_discovery.py +132 -0
  13. src/core/violation_builder.py +122 -0
  14. src/linter_config/ignore.py +112 -40
  15. src/linter_config/loader.py +3 -13
  16. src/linters/dry/__init__.py +23 -0
  17. src/linters/dry/base_token_analyzer.py +76 -0
  18. src/linters/dry/block_filter.py +265 -0
  19. src/linters/dry/block_grouper.py +59 -0
  20. src/linters/dry/cache.py +172 -0
  21. src/linters/dry/cache_query.py +61 -0
  22. src/linters/dry/config.py +134 -0
  23. src/linters/dry/config_loader.py +44 -0
  24. src/linters/dry/deduplicator.py +120 -0
  25. src/linters/dry/duplicate_storage.py +63 -0
  26. src/linters/dry/file_analyzer.py +90 -0
  27. src/linters/dry/inline_ignore.py +140 -0
  28. src/linters/dry/linter.py +163 -0
  29. src/linters/dry/python_analyzer.py +668 -0
  30. src/linters/dry/storage_initializer.py +42 -0
  31. src/linters/dry/token_hasher.py +169 -0
  32. src/linters/dry/typescript_analyzer.py +592 -0
  33. src/linters/dry/violation_builder.py +74 -0
  34. src/linters/dry/violation_filter.py +94 -0
  35. src/linters/dry/violation_generator.py +174 -0
  36. src/linters/file_header/__init__.py +24 -0
  37. src/linters/file_header/atemporal_detector.py +87 -0
  38. src/linters/file_header/config.py +66 -0
  39. src/linters/file_header/field_validator.py +69 -0
  40. src/linters/file_header/linter.py +313 -0
  41. src/linters/file_header/python_parser.py +86 -0
  42. src/linters/file_header/violation_builder.py +78 -0
  43. src/linters/file_placement/config_loader.py +86 -0
  44. src/linters/file_placement/directory_matcher.py +80 -0
  45. src/linters/file_placement/linter.py +262 -471
  46. src/linters/file_placement/path_resolver.py +61 -0
  47. src/linters/file_placement/pattern_matcher.py +55 -0
  48. src/linters/file_placement/pattern_validator.py +106 -0
  49. src/linters/file_placement/rule_checker.py +229 -0
  50. src/linters/file_placement/violation_factory.py +177 -0
  51. src/linters/magic_numbers/__init__.py +48 -0
  52. src/linters/magic_numbers/config.py +82 -0
  53. src/linters/magic_numbers/context_analyzer.py +247 -0
  54. src/linters/magic_numbers/linter.py +516 -0
  55. src/linters/magic_numbers/python_analyzer.py +76 -0
  56. src/linters/magic_numbers/typescript_analyzer.py +218 -0
  57. src/linters/magic_numbers/violation_builder.py +98 -0
  58. src/linters/nesting/__init__.py +6 -2
  59. src/linters/nesting/config.py +17 -4
  60. src/linters/nesting/linter.py +81 -168
  61. src/linters/nesting/typescript_analyzer.py +39 -102
  62. src/linters/nesting/typescript_function_extractor.py +130 -0
  63. src/linters/nesting/violation_builder.py +139 -0
  64. src/linters/print_statements/__init__.py +53 -0
  65. src/linters/print_statements/config.py +83 -0
  66. src/linters/print_statements/linter.py +430 -0
  67. src/linters/print_statements/python_analyzer.py +155 -0
  68. src/linters/print_statements/typescript_analyzer.py +135 -0
  69. src/linters/print_statements/violation_builder.py +98 -0
  70. src/linters/srp/__init__.py +99 -0
  71. src/linters/srp/class_analyzer.py +113 -0
  72. src/linters/srp/config.py +82 -0
  73. src/linters/srp/heuristics.py +89 -0
  74. src/linters/srp/linter.py +234 -0
  75. src/linters/srp/metrics_evaluator.py +47 -0
  76. src/linters/srp/python_analyzer.py +72 -0
  77. src/linters/srp/typescript_analyzer.py +75 -0
  78. src/linters/srp/typescript_metrics_calculator.py +90 -0
  79. src/linters/srp/violation_builder.py +117 -0
  80. src/orchestrator/core.py +54 -9
  81. src/templates/thailint_config_template.yaml +158 -0
  82. src/utils/__init__.py +4 -0
  83. src/utils/project_root.py +203 -0
  84. thailint-0.5.0.dist-info/METADATA +1286 -0
  85. thailint-0.5.0.dist-info/RECORD +96 -0
  86. {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info}/WHEEL +1 -1
  87. src/.ai/layout.yaml +0 -48
  88. thailint-0.1.5.dist-info/METADATA +0 -629
  89. thailint-0.1.5.dist-info/RECORD +0 -28
  90. {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info}/entry_points.txt +0 -0
  91. {thailint-0.1.5.dist-info → thailint-0.5.0.dist-info/licenses}/LICENSE +0 -0
src/__init__.py CHANGED
@@ -7,13 +7,14 @@ Overview: Initializes the CLI application package, defines version number using
7
7
  and exports the public API. Provides single source of truth for version information used by
8
8
  setup tools, CLI help text, and documentation. Exports main CLI entry point, high-level Linter
9
9
  class for library usage, configuration utilities, and direct linter imports for advanced usage.
10
- Includes nesting depth linter exports for convenient access to nesting analysis functionality.
10
+ Includes nesting depth linter and SRP linter exports for convenient access to code analysis.
11
11
  Version is dynamically loaded from package metadata (pyproject.toml) using importlib.metadata.
12
12
 
13
13
  Dependencies: importlib.metadata for dynamic version loading from installed package metadata
14
14
 
15
15
  Exports: __version__, Linter (high-level API), cli (CLI entry point), load_config, save_config,
16
- ConfigError, Orchestrator (advanced usage), file_placement_lint, nesting_lint, NestingDepthRule
16
+ ConfigError, Orchestrator (advanced usage), file_placement_lint, nesting_lint, NestingDepthRule,
17
+ srp_lint, SRPRule
17
18
 
18
19
  Interfaces: Package version string, Linter class API, CLI command group, configuration functions
19
20
  """
@@ -37,6 +38,8 @@ from src.config import ConfigError, load_config, save_config
37
38
  from src.linters.file_placement import lint as file_placement_lint
38
39
  from src.linters.nesting import NestingDepthRule
39
40
  from src.linters.nesting import lint as nesting_lint
41
+ from src.linters.srp import SRPRule
42
+ from src.linters.srp import lint as srp_lint
40
43
  from src.orchestrator.core import Orchestrator
41
44
 
42
45
  __all__ = [
@@ -53,4 +56,6 @@ __all__ = [
53
56
  "file_placement_lint",
54
57
  "nesting_lint",
55
58
  "NestingDepthRule",
59
+ "srp_lint",
60
+ "SRPRule",
56
61
  ]
@@ -0,0 +1,23 @@
1
+ """
2
+ Purpose: Analyzers package for language-specific AST analysis base classes
3
+
4
+ Scope: Language analyzers (TypeScript, Python) providing shared parsing and traversal utilities
5
+
6
+ Overview: Package containing base analyzer classes for different programming languages.
7
+ Provides common tree-sitter initialization, AST parsing, and node traversal patterns
8
+ to eliminate duplicate code across linters. Each language has a base analyzer class
9
+ (TypeScriptBaseAnalyzer, etc.) that linter-specific analyzers extend. Centralizes
10
+ language parsing infrastructure to improve maintainability and consistency.
11
+
12
+ Dependencies: tree-sitter, language-specific tree-sitter bindings
13
+
14
+ Exports: TypeScriptBaseAnalyzer
15
+
16
+ Interfaces: Base analyzer classes with parse(), walk_tree(), and extract() methods
17
+
18
+ Implementation: Composition-based design for linter analyzers to use base utilities
19
+ """
20
+
21
+ from .typescript_base import TypeScriptBaseAnalyzer
22
+
23
+ __all__ = ["TypeScriptBaseAnalyzer"]
@@ -0,0 +1,148 @@
1
+ """
2
+ Purpose: Base class for TypeScript AST analysis with tree-sitter parsing
3
+
4
+ Scope: Common tree-sitter initialization, parsing, and traversal utilities for TypeScript
5
+
6
+ Overview: Provides shared infrastructure for TypeScript code analysis using tree-sitter parser.
7
+ Implements common tree-sitter initialization with language setup and parser configuration.
8
+ Provides reusable parsing methods that convert TypeScript source to AST nodes. Includes
9
+ shared traversal utilities for walking AST trees recursively and finding nodes by type.
10
+ Centralizes node extraction patterns including name extraction from identifiers and
11
+ type identifiers. Serves as foundation for specialized analyzers (SRP, nesting, DRY)
12
+ to eliminate duplicate tree-sitter boilerplate.
13
+
14
+ Dependencies: tree-sitter, tree-sitter-typescript
15
+
16
+ Exports: TypeScriptBaseAnalyzer class with parsing and traversal utilities
17
+
18
+ Interfaces: parse_typescript(code), walk_tree(node, node_type), extract_node_text(node)
19
+
20
+ Implementation: Tree-sitter parser singleton, recursive AST traversal, composition pattern
21
+ """
22
+
23
+ from typing import Any
24
+
25
+ try:
26
+ import tree_sitter_typescript as tstypescript
27
+ from tree_sitter import Language, Node, Parser
28
+
29
+ TS_LANGUAGE = Language(tstypescript.language_typescript())
30
+ TS_PARSER = Parser(TS_LANGUAGE)
31
+ TREE_SITTER_AVAILABLE = True
32
+ except ImportError:
33
+ TREE_SITTER_AVAILABLE = False
34
+ TS_PARSER = None # type: ignore[assignment]
35
+ Node = Any # type: ignore[assignment,misc]
36
+
37
+
38
+ class TypeScriptBaseAnalyzer:
39
+ """Base analyzer for TypeScript code using tree-sitter."""
40
+
41
+ def __init__(self) -> None:
42
+ """Initialize TypeScript base analyzer."""
43
+ self.tree_sitter_available = TREE_SITTER_AVAILABLE
44
+
45
+ def parse_typescript(self, code: str) -> Node | None:
46
+ """Parse TypeScript code to AST using tree-sitter.
47
+
48
+ Args:
49
+ code: TypeScript source code to parse
50
+
51
+ Returns:
52
+ Tree-sitter AST root node, or None if parsing fails or tree-sitter unavailable
53
+ """
54
+ if not TREE_SITTER_AVAILABLE or TS_PARSER is None:
55
+ return None
56
+
57
+ tree = TS_PARSER.parse(bytes(code, "utf8"))
58
+ return tree.root_node
59
+
60
+ def walk_tree(self, node: Node, node_type: str) -> list[Node]:
61
+ """Find all nodes of a specific type in the AST.
62
+
63
+ Recursively walks the tree and collects all nodes matching the given type.
64
+
65
+ Args:
66
+ node: Root tree-sitter node to search from
67
+ node_type: Tree-sitter node type to find (e.g., "class_declaration")
68
+
69
+ Returns:
70
+ List of all matching nodes
71
+ """
72
+ if not TREE_SITTER_AVAILABLE or node is None:
73
+ return []
74
+
75
+ nodes: list[Node] = []
76
+ self._walk_tree_recursive(node, node_type, nodes)
77
+ return nodes
78
+
79
+ def _walk_tree_recursive(self, node: Node, node_type: str, nodes: list[Node]) -> None:
80
+ """Recursively walk tree to find nodes of specific type.
81
+
82
+ Args:
83
+ node: Current tree-sitter node
84
+ node_type: Node type to find
85
+ nodes: List to accumulate matching nodes
86
+ """
87
+ if node.type == node_type:
88
+ nodes.append(node)
89
+
90
+ for child in node.children:
91
+ self._walk_tree_recursive(child, node_type, nodes)
92
+
93
+ def extract_node_text(self, node: Node) -> str:
94
+ """Extract text content from a tree-sitter node.
95
+
96
+ Args:
97
+ node: Tree-sitter node
98
+
99
+ Returns:
100
+ Decoded text content of the node
101
+ """
102
+ text = node.text
103
+ if text is None:
104
+ return ""
105
+ return text.decode()
106
+
107
+ def find_child_by_type(self, node: Node, child_type: str) -> Node | None:
108
+ """Find first child node of a specific type.
109
+
110
+ Args:
111
+ node: Parent node to search
112
+ child_type: Child node type to find
113
+
114
+ Returns:
115
+ First matching child node or None
116
+ """
117
+ for child in node.children:
118
+ if child.type == child_type:
119
+ return child
120
+ return None
121
+
122
+ def find_children_by_types(self, node: Node, child_types: set[str]) -> list[Node]:
123
+ """Find all children matching any of the given types.
124
+
125
+ Args:
126
+ node: Parent node to search
127
+ child_types: Set of child node types to find
128
+
129
+ Returns:
130
+ List of matching child nodes
131
+ """
132
+ return [child for child in node.children if child.type in child_types]
133
+
134
+ def extract_identifier_name(self, node: Node) -> str:
135
+ """Extract identifier or type_identifier name from node children.
136
+
137
+ Common pattern for extracting names from class/function declarations.
138
+
139
+ Args:
140
+ node: Node to extract identifier from
141
+
142
+ Returns:
143
+ Identifier name or default fallback
144
+ """
145
+ for child in node.children:
146
+ if child.type in ("identifier", "type_identifier", "property_identifier"):
147
+ return self.extract_node_text(child)
148
+ return "anonymous"
src/api.py CHANGED
@@ -60,7 +60,7 @@ class Linter:
60
60
 
61
61
  config_path = self._resolve_config_path(config_file)
62
62
  self.config = self.config_loader.load(config_path)
63
- self.orchestrator = Orchestrator(project_root=self.project_root)
63
+ self.orchestrator = Orchestrator(project_root=self.project_root, config=self.config)
64
64
 
65
65
  def _resolve_config_path(self, config_file: str | Path | None) -> Path:
66
66
  """Resolve configuration file path."""