tree-sitter-analyzer 0.9.6__py3-none-any.whl → 0.9.7__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.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

@@ -11,7 +11,7 @@ Architecture:
11
11
  - Data Models: Generic and language-specific code element representations
12
12
  """
13
13
 
14
- __version__ = "0.9.6"
14
+ __version__ = "0.9.7"
15
15
  __author__ = "aisheng.yu"
16
16
  __email__ = "aimasteracc@gmail.com"
17
17
 
@@ -1,162 +1,162 @@
1
- #!/usr/bin/env python3
2
- """
3
- Query Service
4
-
5
- Unified query service for both CLI and MCP interfaces to avoid code duplication.
6
- Provides core tree-sitter query functionality including predefined and custom queries.
7
- """
8
-
9
- import logging
10
- from typing import Any
11
-
12
- from ..encoding_utils import read_file_safe
13
- from ..query_loader import query_loader
14
- from .parser import Parser
15
- from .query_filter import QueryFilter
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class QueryService:
21
- """Unified query service providing tree-sitter query functionality"""
22
-
23
- def __init__(self, project_root: str | None = None) -> None:
24
- """Initialize the query service"""
25
- self.project_root = project_root
26
- self.parser = Parser()
27
- self.filter = QueryFilter()
28
-
29
- async def execute_query(
30
- self,
31
- file_path: str,
32
- language: str,
33
- query_key: str | None = None,
34
- query_string: str | None = None,
35
- filter_expression: str | None = None,
36
- ) -> list[dict[str, Any]] | None:
37
- """
38
- Execute a query
39
-
40
- Args:
41
- file_path: Path to the file to analyze
42
- language: Programming language
43
- query_key: Predefined query key (e.g., 'methods', 'class')
44
- query_string: Custom query string (e.g., '(method_declaration) @method')
45
- filter_expression: Filter expression (e.g., 'name=main', 'name=~get*,public=true')
46
-
47
- Returns:
48
- List of query results, each containing capture_name, node_type, start_line, end_line, content
49
-
50
- Raises:
51
- ValueError: If neither query_key nor query_string is provided
52
- FileNotFoundError: If file doesn't exist
53
- Exception: If query execution fails
54
- """
55
- if not query_key and not query_string:
56
- raise ValueError("Must provide either query_key or query_string")
57
-
58
- if query_key and query_string:
59
- raise ValueError("Cannot provide both query_key and query_string")
60
-
61
- try:
62
- # Read file content
63
- content, encoding = read_file_safe(file_path)
64
-
65
- # Parse file
66
- parse_result = self.parser.parse_code(content, language, file_path)
67
- if not parse_result or not parse_result.tree:
68
- raise Exception("Failed to parse file")
69
-
70
- tree = parse_result.tree
71
- language_obj = tree.language if hasattr(tree, "language") else None
72
- if not language_obj:
73
- raise Exception(f"Language object not available for {language}")
74
-
75
- # Get query string
76
- if query_key:
77
- query_string = query_loader.get_query(language, query_key)
78
- if not query_string:
79
- raise ValueError(
80
- f"Query '{query_key}' not found for language '{language}'"
81
- )
82
-
83
- # Execute tree-sitter query
84
- ts_query = language_obj.query(query_string)
85
- captures = ts_query.captures(tree.root_node)
86
-
87
- # Process capture results
88
- results = []
89
- if isinstance(captures, dict):
90
- # New tree-sitter API returns dictionary
91
- for capture_name, nodes in captures.items():
92
- for node in nodes:
93
- results.append(self._create_result_dict(node, capture_name))
94
- else:
95
- # Old tree-sitter API returns list of tuples
96
- for capture in captures:
97
- if isinstance(capture, tuple) and len(capture) == 2:
98
- node, name = capture
99
- results.append(self._create_result_dict(node, name))
100
-
101
- # Apply filters
102
- if filter_expression and results:
103
- results = self.filter.filter_results(results, filter_expression)
104
-
105
- return results
106
-
107
- except Exception as e:
108
- logger.error(f"Query execution failed: {e}")
109
- raise
110
-
111
- def _create_result_dict(self, node: Any, capture_name: str) -> dict[str, Any]:
112
- """
113
- Create result dictionary from tree-sitter node
114
-
115
- Args:
116
- node: tree-sitter node
117
- capture_name: capture name
118
-
119
- Returns:
120
- Result dictionary
121
- """
122
- return {
123
- "capture_name": capture_name,
124
- "node_type": node.type if hasattr(node, "type") else "unknown",
125
- "start_line": (
126
- node.start_point[0] + 1 if hasattr(node, "start_point") else 0
127
- ),
128
- "end_line": node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
129
- "content": (
130
- node.text.decode("utf-8", errors="replace")
131
- if hasattr(node, "text") and node.text
132
- else ""
133
- ),
134
- }
135
-
136
- def get_available_queries(self, language: str) -> list[str]:
137
- """
138
- Get available query keys for specified language
139
-
140
- Args:
141
- language: Programming language
142
-
143
- Returns:
144
- List of available query keys
145
- """
146
- return query_loader.list_queries(language)
147
-
148
- def get_query_description(self, language: str, query_key: str) -> str | None:
149
- """
150
- Get description for query key
151
-
152
- Args:
153
- language: Programming language
154
- query_key: Query key
155
-
156
- Returns:
157
- Query description, or None if not found
158
- """
159
- try:
160
- return query_loader.get_query_description(language, query_key)
161
- except Exception:
162
- return None
1
+ #!/usr/bin/env python3
2
+ """
3
+ Query Service
4
+
5
+ Unified query service for both CLI and MCP interfaces to avoid code duplication.
6
+ Provides core tree-sitter query functionality including predefined and custom queries.
7
+ """
8
+
9
+ import logging
10
+ from typing import Any
11
+
12
+ from ..encoding_utils import read_file_safe
13
+ from ..query_loader import query_loader
14
+ from .parser import Parser
15
+ from .query_filter import QueryFilter
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class QueryService:
21
+ """Unified query service providing tree-sitter query functionality"""
22
+
23
+ def __init__(self, project_root: str | None = None) -> None:
24
+ """Initialize the query service"""
25
+ self.project_root = project_root
26
+ self.parser = Parser()
27
+ self.filter = QueryFilter()
28
+
29
+ async def execute_query(
30
+ self,
31
+ file_path: str,
32
+ language: str,
33
+ query_key: str | None = None,
34
+ query_string: str | None = None,
35
+ filter_expression: str | None = None,
36
+ ) -> list[dict[str, Any]] | None:
37
+ """
38
+ Execute a query
39
+
40
+ Args:
41
+ file_path: Path to the file to analyze
42
+ language: Programming language
43
+ query_key: Predefined query key (e.g., 'methods', 'class')
44
+ query_string: Custom query string (e.g., '(method_declaration) @method')
45
+ filter_expression: Filter expression (e.g., 'name=main', 'name=~get*,public=true')
46
+
47
+ Returns:
48
+ List of query results, each containing capture_name, node_type, start_line, end_line, content
49
+
50
+ Raises:
51
+ ValueError: If neither query_key nor query_string is provided
52
+ FileNotFoundError: If file doesn't exist
53
+ Exception: If query execution fails
54
+ """
55
+ if not query_key and not query_string:
56
+ raise ValueError("Must provide either query_key or query_string")
57
+
58
+ if query_key and query_string:
59
+ raise ValueError("Cannot provide both query_key and query_string")
60
+
61
+ try:
62
+ # Read file content
63
+ content, encoding = read_file_safe(file_path)
64
+
65
+ # Parse file
66
+ parse_result = self.parser.parse_code(content, language, file_path)
67
+ if not parse_result or not parse_result.tree:
68
+ raise Exception("Failed to parse file")
69
+
70
+ tree = parse_result.tree
71
+ language_obj = tree.language if hasattr(tree, "language") else None
72
+ if not language_obj:
73
+ raise Exception(f"Language object not available for {language}")
74
+
75
+ # Get query string
76
+ if query_key:
77
+ query_string = query_loader.get_query(language, query_key)
78
+ if not query_string:
79
+ raise ValueError(
80
+ f"Query '{query_key}' not found for language '{language}'"
81
+ )
82
+
83
+ # Execute tree-sitter query
84
+ ts_query = language_obj.query(query_string)
85
+ captures = ts_query.captures(tree.root_node)
86
+
87
+ # Process capture results
88
+ results = []
89
+ if isinstance(captures, dict):
90
+ # New tree-sitter API returns dictionary
91
+ for capture_name, nodes in captures.items():
92
+ for node in nodes:
93
+ results.append(self._create_result_dict(node, capture_name))
94
+ else:
95
+ # Old tree-sitter API returns list of tuples
96
+ for capture in captures:
97
+ if isinstance(capture, tuple) and len(capture) == 2:
98
+ node, name = capture
99
+ results.append(self._create_result_dict(node, name))
100
+
101
+ # Apply filters
102
+ if filter_expression and results:
103
+ results = self.filter.filter_results(results, filter_expression)
104
+
105
+ return results
106
+
107
+ except Exception as e:
108
+ logger.error(f"Query execution failed: {e}")
109
+ raise
110
+
111
+ def _create_result_dict(self, node: Any, capture_name: str) -> dict[str, Any]:
112
+ """
113
+ Create result dictionary from tree-sitter node
114
+
115
+ Args:
116
+ node: tree-sitter node
117
+ capture_name: capture name
118
+
119
+ Returns:
120
+ Result dictionary
121
+ """
122
+ return {
123
+ "capture_name": capture_name,
124
+ "node_type": node.type if hasattr(node, "type") else "unknown",
125
+ "start_line": (
126
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
127
+ ),
128
+ "end_line": node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
129
+ "content": (
130
+ node.text.decode("utf-8", errors="replace")
131
+ if hasattr(node, "text") and node.text
132
+ else ""
133
+ ),
134
+ }
135
+
136
+ def get_available_queries(self, language: str) -> list[str]:
137
+ """
138
+ Get available query keys for specified language
139
+
140
+ Args:
141
+ language: Programming language
142
+
143
+ Returns:
144
+ List of available query keys
145
+ """
146
+ return query_loader.list_queries(language)
147
+
148
+ def get_query_description(self, language: str, query_key: str) -> str | None:
149
+ """
150
+ Get description for query key
151
+
152
+ Args:
153
+ language: Programming language
154
+ query_key: Query key
155
+
156
+ Returns:
157
+ Query description, or None if not found
158
+ """
159
+ try:
160
+ return query_loader.get_query_description(language, query_key)
161
+ except Exception:
162
+ return None
@@ -74,7 +74,7 @@ class QueryTool:
74
74
  },
75
75
  }
76
76
 
77
- @handle_mcp_errors
77
+ @handle_mcp_errors("query_code")
78
78
  async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
79
79
  """
80
80
  Execute query tool
@@ -91,7 +91,11 @@ class QueryTool:
91
91
  raise ValueError("file_path is required")
92
92
 
93
93
  # Security validation
94
- validated_path = self.security_validator.validate_file_path(file_path)
94
+ is_valid, error_msg = self.security_validator.validate_file_path(file_path)
95
+ if not is_valid:
96
+ raise ValueError(f"Invalid or unsafe file path: {error_msg or file_path}")
97
+ # Use the original path as validated path after checks
98
+ validated_path = file_path
95
99
 
96
100
  # Get query parameters
97
101
  query_key = arguments.get("query_key")