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