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

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__ = "1.5.0"
14
+ __version__ = "1.6.1"
15
15
  __author__ = "aisheng.yu"
16
16
  __email__ = "aimasteracc@gmail.com"
17
17
 
@@ -152,7 +152,9 @@ def create_argument_parser() -> argparse.ArgumentParser:
152
152
  help="Specify output format",
153
153
  )
154
154
  parser.add_argument(
155
- "--table", choices=["full", "compact", "csv"], help="Output in table format"
155
+ "--table",
156
+ choices=["full", "compact", "csv", "json"],
157
+ help="Output in table format",
156
158
  )
157
159
  parser.add_argument(
158
160
  "--include-javadoc",
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Python-specific table formatter.
4
+
5
+ Provides specialized formatting for Python code analysis results,
6
+ handling modern Python features like async/await, type hints, decorators,
7
+ context managers, and framework-specific patterns.
4
8
  """
5
9
 
6
10
  from typing import Any
@@ -11,29 +15,58 @@ from .base_formatter import BaseTableFormatter
11
15
  class PythonTableFormatter(BaseTableFormatter):
12
16
  """Table formatter specialized for Python"""
13
17
 
18
+ def format(self, data: dict[str, Any]) -> str:
19
+ """Format data using the configured format type"""
20
+ return self.format_structure(data)
21
+
14
22
  def _format_full_table(self, data: dict[str, Any]) -> str:
15
23
  """Full table format for Python"""
16
24
  lines = []
17
25
 
18
- # Header - Python (multi-class supported)
26
+ # Header - Python (module/package based)
27
+ file_path = data.get("file_path", "Unknown")
28
+ file_name = file_path.split("/")[-1].split("\\")[-1]
29
+ module_name = file_name.replace(".py", "").replace(".pyw", "").replace(".pyi", "")
30
+
31
+ # Check if this is a package module
19
32
  classes = data.get("classes", [])
20
- if len(classes) > 1:
21
- # If multiple classes exist, use filename
22
- file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
23
- lines.append(f"# {file_name}")
33
+ functions = data.get("functions", [])
34
+ imports = data.get("imports", [])
35
+
36
+ # Determine module type
37
+ is_package = "__init__.py" in file_name
38
+ is_script = any("if __name__ == '__main__'" in func.get("raw_text", "") for func in functions)
39
+
40
+ if is_package:
41
+ lines.append(f"# Package: {module_name}")
42
+ elif is_script:
43
+ lines.append(f"# Script: {module_name}")
24
44
  else:
25
- # Single class: use class name
26
- class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
27
- lines.append(f"# {class_name}")
45
+ lines.append(f"# Module: {module_name}")
28
46
  lines.append("")
29
47
 
48
+ # Module docstring
49
+ module_docstring = self._extract_module_docstring(data)
50
+ if module_docstring:
51
+ lines.append("## Description")
52
+ lines.append(f'"{module_docstring}"')
53
+ lines.append("")
54
+
30
55
  # Imports
31
- imports = data.get("imports", [])
32
56
  if imports:
33
57
  lines.append("## Imports")
34
58
  lines.append("```python")
35
59
  for imp in imports:
36
- lines.append(str(imp.get("statement", "")))
60
+ import_statement = imp.get("raw_text", "")
61
+ if not import_statement:
62
+ # Fallback construction
63
+ module_name = imp.get("module_name", "")
64
+ name = imp.get("name", "")
65
+ if module_name:
66
+ import_statement = f"from {module_name} import {name}"
67
+ else:
68
+ import_statement = f"import {name}"
69
+ lines.append(import_statement)
37
70
  lines.append("```")
38
71
  lines.append("")
39
72
 
@@ -112,12 +145,12 @@ class PythonTableFormatter(BaseTableFormatter):
112
145
  )
113
146
  lines.append("")
114
147
 
115
- # Methods - Python (no constructor separation)
116
- methods = data.get("methods", [])
148
+ # Methods - Python (with decorators and async support)
149
+ methods = data.get("methods", []) or functions # Use functions if methods not available
117
150
  if methods:
118
151
  lines.append("## Methods")
119
- lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Doc |")
120
- lines.append("|--------|-----------|-----|-------|------|----|----|")
152
+ lines.append("| Method | Signature | Vis | Lines | Cols | Cx | Decorators | Doc |")
153
+ lines.append("|--------|-----------|-----|-------|------|----|-----------|----|")
121
154
 
122
155
  for method in methods:
123
156
  lines.append(self._format_method_row(method))
@@ -185,17 +218,41 @@ class PythonTableFormatter(BaseTableFormatter):
185
218
  def _format_method_row(self, method: dict[str, Any]) -> str:
186
219
  """Format a method table row for Python"""
187
220
  name = str(method.get("name", ""))
188
- signature = self._create_full_signature(method)
189
- visibility = self._convert_visibility(str(method.get("visibility", "")))
221
+ signature = self._format_python_signature(method)
222
+
223
+ # Python-specific visibility handling
224
+ visibility = method.get("visibility", "public")
225
+ if name.startswith("__") and name.endswith("__"):
226
+ visibility = "magic"
227
+ elif name.startswith("_"):
228
+ visibility = "private"
229
+
230
+ vis_symbol = self._get_python_visibility_symbol(visibility)
231
+
190
232
  line_range = method.get("line_range", {})
191
- lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
233
+ if not line_range:
234
+ start_line = method.get("start_line", 0)
235
+ end_line = method.get("end_line", 0)
236
+ lines_str = f"{start_line}-{end_line}"
237
+ else:
238
+ lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
239
+
192
240
  cols_str = "5-6" # default placeholder
193
241
  complexity = method.get("complexity_score", 0)
242
+
243
+ # Use docstring instead of javadoc
194
244
  doc = self._clean_csv_text(
195
- self._extract_doc_summary(str(method.get("javadoc", "")))
245
+ self._extract_doc_summary(str(method.get("docstring", "")))
196
246
  )
197
-
198
- return f"| {name} | {signature} | {visibility} | {lines_str} | {cols_str} | {complexity} | {doc} |"
247
+
248
+ # Add decorators info
249
+ decorators = method.get("modifiers", []) or method.get("decorators", [])
250
+ decorator_str = self._format_decorators(decorators)
251
+
252
+ # Add async indicator
253
+ async_indicator = "🔄" if method.get("is_async", False) else ""
254
+
255
+ return f"| {name}{async_indicator} | {signature} | {vis_symbol} | {lines_str} | {cols_str} | {complexity} | {decorator_str} | {doc} |"
199
256
 
200
257
  def _create_compact_signature(self, method: dict[str, Any]) -> str:
201
258
  """Create compact method signature for Python"""
@@ -257,3 +314,87 @@ class PythonTableFormatter(BaseTableFormatter):
257
314
  type_name, type_name[:3] if len(type_name) > 3 else type_name
258
315
  )
259
316
  return str(result)
317
+
318
+ def _extract_module_docstring(self, data: dict[str, Any]) -> str | None:
319
+ """Extract module-level docstring"""
320
+ # Look for module docstring in the first few lines
321
+ source_code = data.get("source_code", "")
322
+ if not source_code:
323
+ return None
324
+
325
+ lines = source_code.split("\n")
326
+ for i, line in enumerate(lines[:10]): # Check first 10 lines
327
+ stripped = line.strip()
328
+ if stripped.startswith('"""') or stripped.startswith("'''"):
329
+ quote_type = '"""' if stripped.startswith('"""') else "'''"
330
+
331
+ # Single line docstring
332
+ if stripped.count(quote_type) >= 2:
333
+ return stripped.replace(quote_type, "").strip()
334
+
335
+ # Multi-line docstring
336
+ docstring_lines = [stripped.replace(quote_type, "")]
337
+ for j in range(i + 1, len(lines)):
338
+ next_line = lines[j]
339
+ if quote_type in next_line:
340
+ docstring_lines.append(next_line.replace(quote_type, ""))
341
+ break
342
+ docstring_lines.append(next_line)
343
+
344
+ return "\n".join(docstring_lines).strip()
345
+
346
+ return None
347
+
348
+ def _format_python_signature(self, method: dict[str, Any]) -> str:
349
+ """Create Python method signature"""
350
+ params = method.get("parameters", [])
351
+ param_strs = []
352
+
353
+ for p in params:
354
+ if isinstance(p, dict):
355
+ param_name = p.get("name", "")
356
+ param_type = p.get("type", "")
357
+ if param_type:
358
+ param_strs.append(f"{param_name}: {param_type}")
359
+ else:
360
+ param_strs.append(param_name)
361
+ else:
362
+ param_strs.append(str(p))
363
+
364
+ params_str = ", ".join(param_strs)
365
+ return_type = method.get("return_type", "")
366
+
367
+ if return_type and return_type != "Any":
368
+ return f"({params_str}) -> {return_type}"
369
+ else:
370
+ return f"({params_str})"
371
+
372
+ def _get_python_visibility_symbol(self, visibility: str) -> str:
373
+ """Get Python visibility symbol"""
374
+ visibility_map = {
375
+ "public": "🔓",
376
+ "private": "🔒",
377
+ "protected": "🔐",
378
+ "magic": "✨",
379
+ }
380
+ return visibility_map.get(visibility, "🔓")
381
+
382
+ def _format_decorators(self, decorators: list[str]) -> str:
383
+ """Format Python decorators"""
384
+ if not decorators:
385
+ return "-"
386
+
387
+ # Show important decorators
388
+ important = ["property", "staticmethod", "classmethod", "dataclass", "abstractmethod"]
389
+ shown_decorators = []
390
+
391
+ for dec in decorators:
392
+ if any(imp in dec for imp in important):
393
+ shown_decorators.append(f"@{dec}")
394
+
395
+ if shown_decorators:
396
+ return ", ".join(shown_decorators)
397
+ elif len(decorators) == 1:
398
+ return f"@{decorators[0]}"
399
+ else:
400
+ return f"@{decorators[0]} (+{len(decorators)-1})"