tree-sitter-analyzer 0.2.0__py3-none-any.whl → 0.4.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.

Potentially problematic release.


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

Files changed (78) hide show
  1. tree_sitter_analyzer/__init__.py +134 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +533 -539
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -13
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -27
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -155
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -19
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -82
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
  14. tree_sitter_analyzer/cli/commands/table_command.py +235 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +278 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +555 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +559 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +502 -502
  23. tree_sitter_analyzer/encoding_utils.py +456 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +210 -222
  26. tree_sitter_analyzer/formatters/__init__.py +1 -1
  27. tree_sitter_analyzer/formatters/base_formatter.py +167 -168
  28. tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
  29. tree_sitter_analyzer/formatters/java_formatter.py +291 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +259 -235
  31. tree_sitter_analyzer/interfaces/__init__.py +9 -10
  32. tree_sitter_analyzer/interfaces/cli.py +528 -557
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +343 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +187 -219
  37. tree_sitter_analyzer/language_detector.py +398 -400
  38. tree_sitter_analyzer/language_loader.py +224 -228
  39. tree_sitter_analyzer/languages/__init__.py +10 -11
  40. tree_sitter_analyzer/languages/java_plugin.py +1174 -1113
  41. tree_sitter_analyzer/{plugins → languages}/javascript_plugin.py +446 -439
  42. tree_sitter_analyzer/languages/python_plugin.py +747 -712
  43. tree_sitter_analyzer/mcp/__init__.py +31 -32
  44. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  45. tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -213
  46. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +555 -550
  47. tree_sitter_analyzer/mcp/server.py +333 -345
  48. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -557
  50. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -245
  51. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  52. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  53. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  54. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  55. tree_sitter_analyzer/mcp/utils/__init__.py +107 -106
  56. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  57. tree_sitter_analyzer/models.py +470 -481
  58. tree_sitter_analyzer/output_manager.py +255 -264
  59. tree_sitter_analyzer/plugins/__init__.py +280 -334
  60. tree_sitter_analyzer/plugins/base.py +496 -446
  61. tree_sitter_analyzer/plugins/manager.py +379 -355
  62. tree_sitter_analyzer/queries/__init__.py +26 -27
  63. tree_sitter_analyzer/queries/java.py +391 -394
  64. tree_sitter_analyzer/queries/javascript.py +148 -149
  65. tree_sitter_analyzer/queries/python.py +285 -286
  66. tree_sitter_analyzer/queries/typescript.py +229 -230
  67. tree_sitter_analyzer/query_loader.py +257 -260
  68. tree_sitter_analyzer/table_formatter.py +471 -448
  69. tree_sitter_analyzer/utils.py +277 -277
  70. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/METADATA +23 -8
  71. tree_sitter_analyzer-0.4.0.dist-info/RECORD +73 -0
  72. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/entry_points.txt +2 -1
  73. tree_sitter_analyzer/plugins/java_plugin.py +0 -625
  74. tree_sitter_analyzer/plugins/plugin_loader.py +0 -83
  75. tree_sitter_analyzer/plugins/python_plugin.py +0 -598
  76. tree_sitter_analyzer/plugins/registry.py +0 -366
  77. tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
  78. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/WHEEL +0 -0
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Plugin Loader for Tree-sitter Analyzer
5
-
6
- Automatically loads and registers all available language plugins.
7
- """
8
-
9
- from typing import List
10
-
11
- from ..utils import log_debug, log_error, log_info
12
- from . import plugin_registry
13
-
14
-
15
- def load_all_plugins() -> List[str]:
16
- """Load and register all available language plugins"""
17
- loaded_plugins = []
18
-
19
- try:
20
- # Import and register Java plugin
21
- from .java_plugin import JavaPlugin
22
-
23
- java_plugin = JavaPlugin()
24
- plugin_registry.register_plugin(java_plugin)
25
- loaded_plugins.append("java")
26
- log_debug("Loaded Java plugin")
27
- except Exception as e:
28
- log_error(f"Failed to load Java plugin: {e}")
29
-
30
- try:
31
- # Import and register JavaScript plugin
32
- from .javascript_plugin import JavaScriptPlugin
33
-
34
- js_plugin = JavaScriptPlugin()
35
- plugin_registry.register_plugin(js_plugin)
36
- loaded_plugins.append("javascript")
37
- log_debug("Loaded JavaScript plugin")
38
- except Exception as e:
39
- log_error(f"Failed to load JavaScript plugin: {e}")
40
-
41
- try:
42
- # Import and register Python plugin
43
- from .python_plugin import PythonPlugin
44
-
45
- python_plugin = PythonPlugin()
46
- plugin_registry.register_plugin(python_plugin)
47
- loaded_plugins.append("python")
48
- log_debug("Loaded Python plugin")
49
- except Exception as e:
50
- log_error(f"Failed to load Python plugin: {e}")
51
-
52
- if loaded_plugins:
53
- log_info(
54
- f"Successfully loaded {len(loaded_plugins)} language plugins: {', '.join(loaded_plugins)}"
55
- )
56
- else:
57
- log_error("No language plugins were loaded successfully")
58
-
59
- return loaded_plugins
60
-
61
-
62
- def get_supported_languages() -> List[str]:
63
- """Get list of all supported languages"""
64
- return plugin_registry.list_supported_languages()
65
-
66
-
67
- def get_supported_extensions() -> List[str]:
68
- """Get list of all supported file extensions"""
69
- return plugin_registry.list_supported_extensions()
70
-
71
-
72
- def get_plugin_for_file(file_path: str):
73
- """Get appropriate plugin for a file"""
74
- return plugin_registry.get_plugin_for_file(file_path)
75
-
76
-
77
- def get_plugin_by_language(language: str):
78
- """Get plugin by language name"""
79
- return plugin_registry.get_plugin(language)
80
-
81
-
82
- # Auto-load plugins when module is imported
83
- _loaded_plugins = load_all_plugins()
@@ -1,598 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Python Language Plugin
5
-
6
- Provides Python-specific parsing and element extraction functionality.
7
- Integrates with the existing Python queries for comprehensive analysis.
8
- """
9
-
10
- import re
11
- from typing import TYPE_CHECKING, List, Optional
12
-
13
- if TYPE_CHECKING:
14
- import tree_sitter
15
-
16
- from ..language_loader import loader
17
- from ..models import Class, Function, Import, Variable
18
- from ..queries.python import ALL_QUERIES, get_query
19
- from ..utils import log_debug, log_error, log_warning
20
- from . import ElementExtractor, LanguagePlugin
21
-
22
-
23
- class PythonElementExtractor(ElementExtractor):
24
- """Python-specific element extractor with comprehensive analysis"""
25
-
26
- def __init__(self) -> None:
27
- # 分析コンテキスト
28
- self.current_module: str = ""
29
- self.current_file: str = ""
30
- self.source_code: str = ""
31
- self.imports: List[str] = []
32
-
33
- def extract_functions(
34
- self, tree: "tree_sitter.Tree", source_code: str
35
- ) -> List[Function]:
36
- """Extract Python function definitions with comprehensive analysis"""
37
- self.source_code = source_code
38
- functions: List[Function] = []
39
-
40
- try:
41
- language = tree.language if hasattr(tree, "language") else None
42
- if language:
43
- # 関数定義クエリを使用
44
- query_string = get_query("functions")
45
- query = language.query(query_string)
46
- captures = query.captures(tree.root_node)
47
-
48
- if captures is not None and isinstance(captures, dict):
49
- # 関数定義を処理
50
- function_nodes = captures.get("function.definition", [])
51
- for node in function_nodes:
52
- function = self._extract_detailed_function_info(
53
- node, source_code
54
- )
55
- if function:
56
- functions.append(function)
57
-
58
- # async関数も処理
59
- async_nodes = captures.get("function.async", [])
60
- for node in async_nodes:
61
- function = self._extract_detailed_function_info(
62
- node, source_code, is_async=True
63
- )
64
- if function:
65
- functions.append(function)
66
-
67
- except Exception as e:
68
- log_warning(f"Could not extract Python functions: {e}")
69
-
70
- return functions
71
-
72
- def extract_classes(
73
- self, tree: "tree_sitter.Tree", source_code: str
74
- ) -> List[Class]:
75
- """Extract Python class definitions with comprehensive analysis"""
76
- self.source_code = source_code
77
- classes: List[Class] = []
78
-
79
- try:
80
- language = tree.language if hasattr(tree, "language") else None
81
- if language:
82
- # クラス定義クエリを使用
83
- query_string = get_query("classes")
84
- query = language.query(query_string)
85
- captures = query.captures(tree.root_node)
86
-
87
- if captures is not None and isinstance(captures, dict):
88
- class_nodes = captures.get("class.definition", [])
89
- for node in class_nodes:
90
- cls = self._extract_detailed_class_info(node, source_code)
91
- if cls:
92
- classes.append(cls)
93
-
94
- except Exception as e:
95
- log_warning(f"Could not extract Python classes: {e}")
96
-
97
- return classes
98
-
99
- def extract_variables(
100
- self, tree: "tree_sitter.Tree", source_code: str
101
- ) -> List[Variable]:
102
- """Extract Python variable definitions"""
103
- variables: List[Variable] = []
104
-
105
- try:
106
- language = tree.language if hasattr(tree, "language") else None
107
- if language:
108
- # 変数代入クエリを使用
109
- query_string = get_query("variables")
110
- query = language.query(query_string)
111
- captures = query.captures(tree.root_node)
112
-
113
- if captures is not None and isinstance(captures, dict):
114
- # 通常の代入
115
- assignment_nodes = captures.get("variable.assignment", [])
116
- for node in assignment_nodes:
117
- variable = self._extract_variable_info(node, source_code)
118
- if variable:
119
- variables.append(variable)
120
-
121
- # 複数代入
122
- multiple_nodes = captures.get("variable.multiple", [])
123
- for node in multiple_nodes:
124
- variable = self._extract_variable_info(
125
- node, source_code, is_multiple=True
126
- )
127
- if variable:
128
- variables.append(variable)
129
-
130
- # 拡張代入
131
- augmented_nodes = captures.get("variable.augmented", [])
132
- for node in augmented_nodes:
133
- variable = self._extract_variable_info(
134
- node, source_code, is_augmented=True
135
- )
136
- if variable:
137
- variables.append(variable)
138
-
139
- except Exception as e:
140
- log_warning(f"Could not extract Python variables: {e}")
141
-
142
- return variables
143
-
144
- def extract_imports(
145
- self, tree: "tree_sitter.Tree", source_code: str
146
- ) -> List[Import]:
147
- """Extract Python import statements"""
148
- imports: List[Import] = []
149
-
150
- try:
151
- language = tree.language if hasattr(tree, "language") else None
152
- if language:
153
- # インポート文クエリを使用
154
- query_string = get_query("imports")
155
- query = language.query(query_string)
156
- captures = query.captures(tree.root_node)
157
-
158
- if captures is not None and isinstance(captures, dict):
159
- # 通常のimport
160
- import_nodes = captures.get("import.statement", [])
161
- for node in import_nodes:
162
- imp = self._extract_import_info(node, source_code)
163
- if imp:
164
- imports.append(imp)
165
-
166
- # from import
167
- from_nodes = captures.get("import.from", [])
168
- for node in from_nodes:
169
- imp = self._extract_import_info(node, source_code, is_from=True)
170
- if imp:
171
- imports.append(imp)
172
-
173
- # from import list
174
- from_list_nodes = captures.get("import.from_list", [])
175
- for node in from_list_nodes:
176
- imp = self._extract_import_info(
177
- node, source_code, is_from_list=True
178
- )
179
- if imp:
180
- imports.append(imp)
181
-
182
- # aliased import
183
- aliased_nodes = captures.get("import.aliased", [])
184
- for node in aliased_nodes:
185
- imp = self._extract_import_info(
186
- node, source_code, is_aliased=True
187
- )
188
- if imp:
189
- imports.append(imp)
190
-
191
- except Exception as e:
192
- log_warning(f"Could not extract Python imports: {e}")
193
-
194
- return imports
195
-
196
- def _extract_detailed_function_info(
197
- self, node: "tree_sitter.Node", source_code: str, is_async: bool = False
198
- ) -> Optional[Function]:
199
- """Extract comprehensive function information from AST node"""
200
- try:
201
- # 基本情報の抽出
202
- name = self._extract_name_from_node(node, source_code)
203
- if not name:
204
- return None
205
-
206
- # パラメータの抽出
207
- parameters = self._extract_parameters_from_node(node, source_code)
208
-
209
- # デコレータの抽出
210
- decorators = self._extract_decorators_from_node(node, source_code)
211
-
212
- # 戻り値の型ヒントの抽出
213
- return_type = self._extract_return_type_from_node(node, source_code)
214
-
215
- # docstringの抽出
216
- docstring = self._extract_docstring_from_node(node, source_code)
217
-
218
- # 関数ボディの抽出
219
- body = self._extract_function_body(node, source_code)
220
-
221
- # 複雑度の簡易計算
222
- complexity_score = self._calculate_complexity(body)
223
-
224
- # 可視性の判定(Pythonの慣例に基づく)
225
- visibility = "public"
226
- if name.startswith("__") and name.endswith("__"):
227
- visibility = "magic" # マジックメソッド
228
- elif name.startswith("_"):
229
- visibility = "private"
230
-
231
- start_byte = min(node.start_byte, len(source_code))
232
- end_byte = min(node.end_byte, len(source_code))
233
- raw_text = source_code[start_byte:end_byte] if start_byte < end_byte else source_code
234
-
235
- return Function(
236
- name=name,
237
- start_line=node.start_point[0] + 1,
238
- end_line=node.end_point[0] + 1,
239
- raw_text=raw_text,
240
- language="python",
241
- parameters=parameters,
242
- return_type=return_type or "Any",
243
- modifiers=decorators,
244
- is_static="staticmethod" in decorators,
245
- is_private=visibility == "private",
246
- is_public=visibility == "public",
247
- is_async=is_async,
248
- docstring=docstring,
249
- complexity_score=complexity_score,
250
- )
251
-
252
- except Exception as e:
253
- log_warning(f"Could not extract detailed function info: {e}")
254
- return None
255
-
256
- def _extract_detailed_class_info(
257
- self, node: "tree_sitter.Node", source_code: str
258
- ) -> Optional[Class]:
259
- """Extract comprehensive class information from AST node"""
260
- try:
261
- # 基本情報の抽出
262
- name = self._extract_name_from_node(node, source_code)
263
- if not name:
264
- return None
265
-
266
- # スーパークラスの抽出
267
- superclasses = self._extract_superclasses_from_node(node, source_code)
268
-
269
- # デコレータの抽出
270
- decorators = self._extract_decorators_from_node(node, source_code)
271
-
272
- # docstringの抽出
273
- docstring = self._extract_docstring_from_node(node, source_code)
274
-
275
- # 完全修飾名の生成
276
- full_qualified_name = (
277
- f"{self.current_module}.{name}" if self.current_module else name
278
- )
279
-
280
- # 可視性の判定
281
- visibility = "public"
282
- if name.startswith("_"):
283
- visibility = "private"
284
-
285
- return Class(
286
- name=name,
287
- start_line=node.start_point[0] + 1,
288
- end_line=node.end_point[0] + 1,
289
- raw_text=source_code[node.start_byte : node.end_byte],
290
- language="python",
291
- class_type="class",
292
- full_qualified_name=full_qualified_name,
293
- package_name=self.current_module,
294
- superclass=superclasses[0] if superclasses else None,
295
- interfaces=superclasses[1:] if len(superclasses) > 1 else [],
296
- modifiers=decorators,
297
- docstring=docstring,
298
- )
299
-
300
- except Exception as e:
301
- log_warning(f"Could not extract detailed class info: {e}")
302
- return None
303
-
304
- def _extract_name_from_node(
305
- self, node: "tree_sitter.Node", source_code: str
306
- ) -> Optional[str]:
307
- """Extract name from AST node"""
308
- for child in node.children:
309
- if child.type == "identifier":
310
- return source_code[child.start_byte : child.end_byte]
311
- return None
312
-
313
- def _extract_parameters_from_node(
314
- self, node: "tree_sitter.Node", source_code: str
315
- ) -> List[str]:
316
- """Extract parameters from function node"""
317
- parameters: List[str] = []
318
- for child in node.children:
319
- if child.type == "parameters":
320
- for param_child in child.children:
321
- if param_child.type in [
322
- "identifier",
323
- "typed_parameter",
324
- "default_parameter",
325
- ]:
326
- param_text = source_code[
327
- param_child.start_byte : param_child.end_byte
328
- ]
329
- parameters.append(param_text)
330
- return parameters
331
-
332
- def _extract_decorators_from_node(
333
- self, node: "tree_sitter.Node", source_code: str
334
- ) -> List[str]:
335
- """Extract decorators from node"""
336
- decorators: List[str] = []
337
-
338
- # デコレータは関数/クラス定義の前にある
339
- if hasattr(node, "parent") and node.parent:
340
- for sibling in node.parent.children:
341
- if (
342
- sibling.type == "decorator"
343
- and sibling.end_point[0] < node.start_point[0]
344
- ):
345
- decorator_text = source_code[sibling.start_byte : sibling.end_byte]
346
- # @を除去
347
- if decorator_text.startswith("@"):
348
- decorator_text = decorator_text[1:].strip()
349
- decorators.append(decorator_text)
350
-
351
- return decorators
352
-
353
- def _extract_return_type_from_node(
354
- self, node: "tree_sitter.Node", source_code: str
355
- ) -> Optional[str]:
356
- """Extract return type annotation from function node"""
357
- for child in node.children:
358
- if child.type == "type":
359
- return source_code[child.start_byte : child.end_byte]
360
- return None
361
-
362
- def _extract_docstring_from_node(
363
- self, node: "tree_sitter.Node", source_code: str
364
- ) -> Optional[str]:
365
- """Extract docstring from function/class node"""
366
- for child in node.children:
367
- if child.type == "block":
368
- # ブロックの最初の文がdocstringかチェック
369
- for stmt in child.children:
370
- if stmt.type == "expression_statement":
371
- for expr in stmt.children:
372
- if expr.type == "string":
373
- # start_byteとend_byteが整数であることを確認
374
- if (hasattr(expr, 'start_byte') and hasattr(expr, 'end_byte') and
375
- isinstance(expr.start_byte, int) and isinstance(expr.end_byte, int)):
376
- docstring = source_code[expr.start_byte : expr.end_byte]
377
- else:
378
- return None
379
- # クォートを除去
380
- if docstring.startswith('"""') or docstring.startswith(
381
- "'''"
382
- ):
383
- return docstring[3:-3].strip()
384
- elif docstring.startswith('"') or docstring.startswith(
385
- "'"
386
- ):
387
- return docstring[1:-1].strip()
388
- return docstring
389
- break
390
- break
391
- return None
392
-
393
- def _extract_function_body(self, node: "tree_sitter.Node", source_code: str) -> str:
394
- """Extract function body"""
395
- for child in node.children:
396
- if child.type == "block":
397
- return source_code[child.start_byte : child.end_byte]
398
- return ""
399
-
400
- def _extract_superclasses_from_node(
401
- self, node: "tree_sitter.Node", source_code: str
402
- ) -> List[str]:
403
- """Extract superclasses from class node"""
404
- superclasses: List[str] = []
405
- for child in node.children:
406
- if child.type == "argument_list":
407
- for arg in child.children:
408
- if arg.type == "identifier":
409
- superclasses.append(source_code[arg.start_byte : arg.end_byte])
410
- return superclasses
411
-
412
- def _calculate_complexity(self, body: str) -> int:
413
- """Calculate cyclomatic complexity (simplified)"""
414
- complexity = 1 # 基本複雑度
415
- keywords = ["if", "elif", "for", "while", "try", "except", "with", "and", "or"]
416
- for keyword in keywords:
417
- complexity += body.count(f" {keyword} ") + body.count(f"\n{keyword} ")
418
- return complexity
419
-
420
- def _extract_variable_info(
421
- self,
422
- node: "tree_sitter.Node",
423
- source_code: str,
424
- is_multiple: bool = False,
425
- is_augmented: bool = False,
426
- ) -> Optional[Variable]:
427
- """Extract detailed variable information from AST node"""
428
- try:
429
- if (
430
- not hasattr(node, "start_byte")
431
- or not hasattr(node, "end_byte")
432
- or not hasattr(node, "start_point")
433
- or not hasattr(node, "end_point")
434
- ):
435
- return None
436
- if (
437
- node.start_byte is None
438
- or node.end_byte is None
439
- or node.start_point is None
440
- or node.end_point is None
441
- ):
442
- return None
443
-
444
- # 変数名の抽出(簡略化)
445
- variable_text = source_code[node.start_byte : node.end_byte]
446
-
447
- # 変数名を抽出(=の左側)
448
- if "=" in variable_text:
449
- name_part = variable_text.split("=")[0].strip()
450
- if is_multiple and "," in name_part:
451
- name = name_part.split(",")[0].strip()
452
- else:
453
- name = name_part
454
- else:
455
- name = "variable"
456
-
457
- return Variable(
458
- name=name,
459
- start_line=node.start_point[0] + 1,
460
- end_line=node.end_point[0] + 1,
461
- raw_text=variable_text,
462
- language="python",
463
- variable_type=(
464
- "multiple"
465
- if is_multiple
466
- else "augmented" if is_augmented else "assignment"
467
- ),
468
- )
469
-
470
- except Exception as e:
471
- log_warning(f"Could not extract variable info: {e}")
472
- return None
473
-
474
- def _extract_import_info(
475
- self,
476
- node: "tree_sitter.Node",
477
- source_code: str,
478
- is_from: bool = False,
479
- is_from_list: bool = False,
480
- is_aliased: bool = False,
481
- ) -> Optional[Import]:
482
- """Extract detailed import information from AST node"""
483
- try:
484
- if (
485
- not hasattr(node, "start_byte")
486
- or not hasattr(node, "end_byte")
487
- or not hasattr(node, "start_point")
488
- or not hasattr(node, "end_point")
489
- ):
490
- return None
491
- if (
492
- node.start_byte is None
493
- or node.end_byte is None
494
- or node.start_point is None
495
- or node.end_point is None
496
- ):
497
- return None
498
-
499
- # テスト環境での安全な境界処理
500
- source_len = len(source_code)
501
- if node.start_byte >= source_len or node.end_byte > source_len:
502
- # ノードの範囲がソースコードを超える場合(テスト環境など)、全体を使用
503
- import_text = source_code
504
- else:
505
- start_byte = node.start_byte
506
- end_byte = node.end_byte
507
- import_text = source_code[start_byte:end_byte] if start_byte < end_byte else source_code
508
-
509
- # インポート名とモジュール名を抽出(完全なインポート文を保持)
510
- if is_from:
511
- import_type = "from_import"
512
- if "from" in import_text and "import" in import_text:
513
- parts = import_text.split("import")
514
- module_name = parts[0].replace("from", "").strip()
515
- # from importの場合は完全な文を保持
516
- import_name = import_text
517
- else:
518
- module_name = ""
519
- import_name = import_text
520
- elif is_aliased:
521
- import_type = "aliased_import"
522
- module_name = ""
523
- # エイリアスインポートも完全な文を保持
524
- import_name = import_text
525
- else:
526
- import_type = "import"
527
- module_name = ""
528
- # 通常のインポートも完全な文を保持
529
- import_name = import_text
530
-
531
- return Import(
532
- name=import_name,
533
- start_line=node.start_point[0] + 1,
534
- end_line=node.end_point[0] + 1,
535
- raw_text=import_text,
536
- language="python",
537
- module_name=module_name,
538
- import_statement=import_text,
539
- line_number=node.start_point[0] + 1,
540
- )
541
-
542
- except Exception as e:
543
- log_warning(f"Could not extract import info: {e}")
544
- return None
545
-
546
-
547
- class PythonPlugin(LanguagePlugin):
548
- """Python language plugin"""
549
-
550
- def __init__(self) -> None:
551
- self._extractor = PythonElementExtractor()
552
- self._language: Optional["tree_sitter.Language"] = None
553
-
554
- @property
555
- def language_name(self) -> str:
556
- return "python"
557
-
558
- @property
559
- def file_extensions(self) -> List[str]:
560
- return [".py", ".pyw", ".pyi"]
561
-
562
- def get_language_name(self) -> str:
563
- """Return the name of the programming language this plugin supports"""
564
- return "python"
565
-
566
- def get_file_extensions(self) -> List[str]:
567
- """Return list of file extensions this plugin supports"""
568
- return [".py", ".pyw", ".pyi"]
569
-
570
- def create_extractor(self) -> ElementExtractor:
571
- """Create and return an element extractor for this language"""
572
- return PythonElementExtractor()
573
-
574
- def get_extractor(self) -> ElementExtractor:
575
- return self._extractor
576
-
577
- def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
578
- """Load and return Python tree-sitter language"""
579
- if self._language is None:
580
- self._language = loader.load_language("python")
581
- return self._language
582
-
583
- def get_supported_queries(self) -> List[str]:
584
- """Get list of supported query types for Python"""
585
- return list(ALL_QUERIES.keys())
586
-
587
- def execute_query(self, tree: "tree_sitter.Tree", query_name: str) -> dict:
588
- """Execute a specific query on the tree"""
589
- try:
590
- query_string = get_query(query_name)
591
- language = self.get_tree_sitter_language()
592
- if language:
593
- query = language.query(query_string)
594
- captures = query.captures(tree.root_node)
595
- return captures if isinstance(captures, dict) else {}
596
- except Exception as e:
597
- log_warning(f"Could not execute query '{query_name}': {e}")
598
- return {}