tree-sitter-analyzer 1.7.7__py3-none-any.whl → 1.8.2__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 (38) hide show
  1. tree_sitter_analyzer/__init__.py +1 -1
  2. tree_sitter_analyzer/api.py +23 -30
  3. tree_sitter_analyzer/cli/argument_validator.py +77 -0
  4. tree_sitter_analyzer/cli/commands/table_command.py +7 -2
  5. tree_sitter_analyzer/cli_main.py +17 -3
  6. tree_sitter_analyzer/core/cache_service.py +15 -5
  7. tree_sitter_analyzer/core/query.py +33 -22
  8. tree_sitter_analyzer/core/query_service.py +179 -154
  9. tree_sitter_analyzer/formatters/formatter_registry.py +355 -0
  10. tree_sitter_analyzer/formatters/html_formatter.py +462 -0
  11. tree_sitter_analyzer/formatters/language_formatter_factory.py +3 -0
  12. tree_sitter_analyzer/formatters/markdown_formatter.py +1 -1
  13. tree_sitter_analyzer/language_detector.py +80 -7
  14. tree_sitter_analyzer/languages/css_plugin.py +390 -0
  15. tree_sitter_analyzer/languages/html_plugin.py +395 -0
  16. tree_sitter_analyzer/languages/java_plugin.py +116 -0
  17. tree_sitter_analyzer/languages/javascript_plugin.py +113 -0
  18. tree_sitter_analyzer/languages/markdown_plugin.py +266 -46
  19. tree_sitter_analyzer/languages/python_plugin.py +176 -33
  20. tree_sitter_analyzer/languages/typescript_plugin.py +130 -1
  21. tree_sitter_analyzer/mcp/tools/query_tool.py +99 -58
  22. tree_sitter_analyzer/mcp/tools/table_format_tool.py +24 -10
  23. tree_sitter_analyzer/models.py +53 -0
  24. tree_sitter_analyzer/output_manager.py +1 -1
  25. tree_sitter_analyzer/plugins/base.py +50 -0
  26. tree_sitter_analyzer/plugins/manager.py +5 -1
  27. tree_sitter_analyzer/queries/css.py +634 -0
  28. tree_sitter_analyzer/queries/html.py +556 -0
  29. tree_sitter_analyzer/queries/markdown.py +54 -164
  30. tree_sitter_analyzer/query_loader.py +16 -3
  31. tree_sitter_analyzer/security/validator.py +182 -44
  32. tree_sitter_analyzer/utils/__init__.py +113 -0
  33. tree_sitter_analyzer/utils/tree_sitter_compat.py +282 -0
  34. tree_sitter_analyzer/utils.py +62 -24
  35. {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/METADATA +120 -14
  36. {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/RECORD +38 -29
  37. {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/entry_points.txt +2 -0
  38. {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/WHEEL +0 -0
@@ -24,6 +24,7 @@ from ..encoding_utils import extract_text_slice, safe_encode
24
24
  from ..models import AnalysisResult, CodeElement
25
25
  from ..plugins.base import ElementExtractor, LanguagePlugin
26
26
  from ..utils import log_debug, log_error, log_warning
27
+ from ..utils.tree_sitter_compat import TreeSitterQueryCompat, get_node_text_safe
27
28
 
28
29
 
29
30
  class MarkdownElement(CodeElement):
@@ -1447,22 +1448,9 @@ class MarkdownPlugin(LanguagePlugin):
1447
1448
  import tree_sitter
1448
1449
  import tree_sitter_markdown as tsmarkdown
1449
1450
 
1450
- # Support for newer versions of tree-sitter-markdown
1451
- try:
1452
- # New API (0.3.1+)
1453
- language_capsule = tsmarkdown.language()
1454
- self._language_cache = tree_sitter.Language(language_capsule)
1455
- except (AttributeError, TypeError):
1456
- # For older API or different format
1457
- try:
1458
- # Get Language object directly
1459
- self._language_cache = tsmarkdown.language()
1460
- except Exception:
1461
- # Last resort: get directly from module
1462
- if hasattr(tsmarkdown, 'LANGUAGE'):
1463
- self._language_cache = tree_sitter.Language(tsmarkdown.LANGUAGE)
1464
- else:
1465
- raise ImportError("Cannot access markdown language")
1451
+ # Use modern tree-sitter-markdown API
1452
+ language_capsule = tsmarkdown.language()
1453
+ self._language_cache = tree_sitter.Language(language_capsule)
1466
1454
  except ImportError:
1467
1455
  log_error("tree-sitter-markdown not available")
1468
1456
  return None
@@ -1637,35 +1625,11 @@ class MarkdownPlugin(LanguagePlugin):
1637
1625
  except KeyError:
1638
1626
  return {"error": f"Unknown query: {query_name}"}
1639
1627
 
1640
- # Use new tree-sitter 0.25.x API
1641
- query = tree_sitter.Query(language, query_string)
1642
-
1643
- # Execute query using the new API
1644
- # In tree-sitter 0.25.x, we need to use a different approach
1645
- matches = []
1646
- captures = []
1647
-
1648
- # Walk through the tree and find matches manually
1649
- def walk_tree(node):
1650
- # This is a simplified approach - in practice, you'd want to use
1651
- # the proper query execution method when it becomes available
1652
- if query_name == "headers" and node.type in ["atx_heading", "setext_heading"]:
1653
- matches.append(node)
1654
- elif query_name == "code_blocks" and node.type in ["fenced_code_block", "indented_code_block"]:
1655
- matches.append(node)
1656
- elif query_name == "links" and node.type in ["link", "autolink", "reference_link"]:
1657
- matches.append(node)
1658
-
1659
- for child in node.children:
1660
- walk_tree(child)
1661
-
1662
- walk_tree(tree.root_node)
1663
-
1664
- # Convert matches to capture format
1665
- for match in matches:
1666
- captures.append((match, query_name))
1667
-
1668
- return {"captures": captures, "query": query_string, "matches": len(matches)}
1628
+ # Use tree-sitter API with modern handling
1629
+ captures = TreeSitterQueryCompat.safe_execute_query(
1630
+ language, query_string, tree.root_node, fallback_result=[]
1631
+ )
1632
+ return {"captures": captures, "query": query_string, "matches": len(captures)}
1669
1633
 
1670
1634
  except Exception as e:
1671
1635
  log_error(f"Query execution failed: {e}")
@@ -1692,4 +1656,260 @@ class MarkdownPlugin(LanguagePlugin):
1692
1656
  except Exception as e:
1693
1657
  log_error(f"Failed to extract elements: {e}")
1694
1658
 
1695
- return elements
1659
+ return elements
1660
+
1661
+ def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[CodeElement]:
1662
+ """Execute Markdown-specific query strategy based on query_key"""
1663
+ if not tree or not source_code:
1664
+ return []
1665
+
1666
+ # Initialize extractor with source code
1667
+ self._extractor.source_code = source_code
1668
+ self._extractor.content_lines = source_code.split("\n")
1669
+ self._extractor._reset_caches()
1670
+
1671
+ # Map query_key to appropriate extraction method
1672
+ query_mapping = {
1673
+ # Header-related queries (mapped to functions)
1674
+ "function": lambda: self._extractor.extract_headers(tree, source_code),
1675
+ "headers": lambda: self._extractor.extract_headers(tree, source_code),
1676
+ "heading": lambda: self._extractor.extract_headers(tree, source_code),
1677
+
1678
+ # Code block-related queries (mapped to classes)
1679
+ "class": lambda: self._extractor.extract_code_blocks(tree, source_code),
1680
+ "code_blocks": lambda: self._extractor.extract_code_blocks(tree, source_code),
1681
+ "code_block": lambda: self._extractor.extract_code_blocks(tree, source_code),
1682
+
1683
+ # Link and image queries (mapped to variables)
1684
+ "variable": lambda: self._extractor.extract_links(tree, source_code) + self._extractor.extract_images(tree, source_code),
1685
+ "links": lambda: self._extractor.extract_links(tree, source_code),
1686
+ "link": lambda: self._extractor.extract_links(tree, source_code),
1687
+ "images": lambda: self._extractor.extract_images(tree, source_code),
1688
+ "image": lambda: self._extractor.extract_images(tree, source_code),
1689
+
1690
+ # Reference queries (mapped to imports)
1691
+ "import": lambda: self._extractor.extract_references(tree, source_code),
1692
+ "references": lambda: self._extractor.extract_references(tree, source_code),
1693
+ "reference": lambda: self._extractor.extract_references(tree, source_code),
1694
+
1695
+ # List and table queries
1696
+ "lists": lambda: self._extractor.extract_lists(tree, source_code),
1697
+ "list": lambda: self._extractor.extract_lists(tree, source_code),
1698
+ "task_lists": lambda: [l for l in self._extractor.extract_lists(tree, source_code) if getattr(l, 'element_type', '') == 'task_list'],
1699
+ "tables": lambda: self._extractor.extract_tables(tree, source_code),
1700
+ "table": lambda: self._extractor.extract_tables(tree, source_code),
1701
+
1702
+ # Content structure queries
1703
+ "blockquotes": lambda: self._extractor.extract_blockquotes(tree, source_code),
1704
+ "blockquote": lambda: self._extractor.extract_blockquotes(tree, source_code),
1705
+ "horizontal_rules": lambda: self._extractor.extract_horizontal_rules(tree, source_code),
1706
+ "horizontal_rule": lambda: self._extractor.extract_horizontal_rules(tree, source_code),
1707
+
1708
+ # HTML and formatting queries
1709
+ "html_blocks": lambda: self._extractor.extract_html_elements(tree, source_code),
1710
+ "html_block": lambda: self._extractor.extract_html_elements(tree, source_code),
1711
+ "html": lambda: self._extractor.extract_html_elements(tree, source_code),
1712
+ "emphasis": lambda: self._extractor.extract_text_formatting(tree, source_code),
1713
+ "formatting": lambda: self._extractor.extract_text_formatting(tree, source_code),
1714
+ "text_formatting": lambda: self._extractor.extract_text_formatting(tree, source_code),
1715
+ "inline_code": lambda: [f for f in self._extractor.extract_text_formatting(tree, source_code) if getattr(f, 'element_type', '') == 'inline_code'],
1716
+ "strikethrough": lambda: [f for f in self._extractor.extract_text_formatting(tree, source_code) if getattr(f, 'element_type', '') == 'strikethrough'],
1717
+
1718
+ # Footnote queries
1719
+ "footnotes": lambda: self._extractor.extract_footnotes(tree, source_code),
1720
+ "footnote": lambda: self._extractor.extract_footnotes(tree, source_code),
1721
+
1722
+ # Comprehensive queries
1723
+ "all_elements": lambda: self.extract_elements(tree, source_code),
1724
+ "text_content": lambda: self._extractor.extract_headers(tree, source_code) + self._extractor.extract_text_formatting(tree, source_code),
1725
+ }
1726
+
1727
+ # Execute the appropriate extraction method
1728
+ if query_key in query_mapping:
1729
+ try:
1730
+ return query_mapping[query_key]()
1731
+ except Exception as e:
1732
+ log_error(f"Error executing Markdown query '{query_key}': {e}")
1733
+ return []
1734
+ else:
1735
+ log_warning(f"Unsupported Markdown query key: {query_key}")
1736
+ return []
1737
+
1738
+ def get_element_categories(self) -> dict[str, list[str]]:
1739
+ """Get Markdown element categories mapping query_key to node_types"""
1740
+ return {
1741
+ # Header categories (function-like)
1742
+ "function": [
1743
+ "atx_heading",
1744
+ "setext_heading"
1745
+ ],
1746
+ "headers": [
1747
+ "atx_heading",
1748
+ "setext_heading"
1749
+ ],
1750
+ "heading": [
1751
+ "atx_heading",
1752
+ "setext_heading"
1753
+ ],
1754
+
1755
+ # Code block categories (class-like)
1756
+ "class": [
1757
+ "fenced_code_block",
1758
+ "indented_code_block"
1759
+ ],
1760
+ "code_blocks": [
1761
+ "fenced_code_block",
1762
+ "indented_code_block"
1763
+ ],
1764
+ "code_block": [
1765
+ "fenced_code_block",
1766
+ "indented_code_block"
1767
+ ],
1768
+
1769
+ # Link and image categories (variable-like)
1770
+ "variable": [
1771
+ "inline", # Contains links and images
1772
+ "link",
1773
+ "autolink",
1774
+ "reference_link",
1775
+ "image"
1776
+ ],
1777
+ "links": [
1778
+ "inline", # Contains inline links
1779
+ "link",
1780
+ "autolink",
1781
+ "reference_link"
1782
+ ],
1783
+ "link": [
1784
+ "inline",
1785
+ "link",
1786
+ "autolink",
1787
+ "reference_link"
1788
+ ],
1789
+ "images": [
1790
+ "inline", # Contains inline images
1791
+ "image"
1792
+ ],
1793
+ "image": [
1794
+ "inline",
1795
+ "image"
1796
+ ],
1797
+
1798
+ # Reference categories (import-like)
1799
+ "import": [
1800
+ "link_reference_definition"
1801
+ ],
1802
+ "references": [
1803
+ "link_reference_definition"
1804
+ ],
1805
+ "reference": [
1806
+ "link_reference_definition"
1807
+ ],
1808
+
1809
+ # List categories
1810
+ "lists": [
1811
+ "list",
1812
+ "list_item"
1813
+ ],
1814
+ "list": [
1815
+ "list",
1816
+ "list_item"
1817
+ ],
1818
+ "task_lists": [
1819
+ "list",
1820
+ "list_item"
1821
+ ],
1822
+
1823
+ # Table categories
1824
+ "tables": [
1825
+ "pipe_table",
1826
+ "table"
1827
+ ],
1828
+ "table": [
1829
+ "pipe_table",
1830
+ "table"
1831
+ ],
1832
+
1833
+ # Content structure categories
1834
+ "blockquotes": [
1835
+ "block_quote"
1836
+ ],
1837
+ "blockquote": [
1838
+ "block_quote"
1839
+ ],
1840
+ "horizontal_rules": [
1841
+ "thematic_break"
1842
+ ],
1843
+ "horizontal_rule": [
1844
+ "thematic_break"
1845
+ ],
1846
+
1847
+ # HTML categories
1848
+ "html_blocks": [
1849
+ "html_block",
1850
+ "inline" # Contains inline HTML
1851
+ ],
1852
+ "html_block": [
1853
+ "html_block",
1854
+ "inline"
1855
+ ],
1856
+ "html": [
1857
+ "html_block",
1858
+ "inline"
1859
+ ],
1860
+
1861
+ # Text formatting categories
1862
+ "emphasis": [
1863
+ "inline" # Contains emphasis elements
1864
+ ],
1865
+ "formatting": [
1866
+ "inline"
1867
+ ],
1868
+ "text_formatting": [
1869
+ "inline"
1870
+ ],
1871
+ "inline_code": [
1872
+ "inline"
1873
+ ],
1874
+ "strikethrough": [
1875
+ "inline"
1876
+ ],
1877
+
1878
+ # Footnote categories
1879
+ "footnotes": [
1880
+ "inline", # Contains footnote references
1881
+ "paragraph" # Contains footnote definitions
1882
+ ],
1883
+ "footnote": [
1884
+ "inline",
1885
+ "paragraph"
1886
+ ],
1887
+
1888
+ # Comprehensive categories
1889
+ "all_elements": [
1890
+ "atx_heading",
1891
+ "setext_heading",
1892
+ "fenced_code_block",
1893
+ "indented_code_block",
1894
+ "inline",
1895
+ "link",
1896
+ "autolink",
1897
+ "reference_link",
1898
+ "image",
1899
+ "link_reference_definition",
1900
+ "list",
1901
+ "list_item",
1902
+ "pipe_table",
1903
+ "table",
1904
+ "block_quote",
1905
+ "thematic_break",
1906
+ "html_block",
1907
+ "paragraph"
1908
+ ],
1909
+ "text_content": [
1910
+ "atx_heading",
1911
+ "setext_heading",
1912
+ "inline",
1913
+ "paragraph"
1914
+ ]
1915
+ }
@@ -25,6 +25,7 @@ from ..encoding_utils import extract_text_slice, safe_encode
25
25
  from ..models import AnalysisResult, Class, CodeElement, Function, Import, Variable
26
26
  from ..plugins.base import ElementExtractor, LanguagePlugin
27
27
  from ..utils import log_debug, log_error, log_warning
28
+ from ..utils.tree_sitter_compat import TreeSitterQueryCompat, get_node_text_safe
28
29
 
29
30
 
30
31
  class PythonElementExtractor(ElementExtractor):
@@ -118,7 +119,7 @@ class PythonElementExtractor(ElementExtractor):
118
119
 
119
120
  # Only extract class-level attributes, not function-level variables
120
121
  try:
121
- # Find class declarations
122
+ # Find class declarations using compatible API
122
123
  class_query = """
123
124
  (class_definition
124
125
  body: (block) @class.body) @class.definition
@@ -126,18 +127,23 @@ class PythonElementExtractor(ElementExtractor):
126
127
 
127
128
  language = tree.language if hasattr(tree, "language") else None
128
129
  if language:
129
- import tree_sitter
130
- query = tree_sitter.Query(language, class_query)
131
- captures = query.captures(tree.root_node)
132
-
133
- if isinstance(captures, dict):
134
- class_bodies = captures.get("class.body", [])
135
-
136
- # For each class body, extract attribute assignments
137
- for class_body in class_bodies:
138
- variables.extend(
139
- self._extract_class_attributes(class_body, source_code)
140
- )
130
+ try:
131
+ captures = TreeSitterQueryCompat.safe_execute_query(
132
+ language, class_query, tree.root_node, fallback_result=[]
133
+ )
134
+ class_bodies = []
135
+ for node, capture_name in captures:
136
+ if capture_name == "class.body":
137
+ class_bodies.append(node)
138
+ except Exception as e:
139
+ log_debug(f"Could not extract Python class attributes using query: {e}")
140
+ class_bodies = []
141
+
142
+ # For each class body, extract attribute assignments
143
+ for class_body in class_bodies:
144
+ variables.extend(
145
+ self._extract_class_attributes(class_body, source_code)
146
+ )
141
147
 
142
148
  except Exception as e:
143
149
  log_warning(f"Could not extract Python class attributes: {e}")
@@ -731,24 +737,30 @@ class PythonElementExtractor(ElementExtractor):
731
737
  if language:
732
738
  for query_string in import_queries:
733
739
  try:
734
- import tree_sitter
735
- query = tree_sitter.Query(language, query_string)
736
- captures = query.captures(tree.root_node)
737
-
738
- if isinstance(captures, dict):
739
- # Process different types of imports
740
- for key, nodes in captures.items():
741
- if key.endswith("statement"):
742
- import_type = key.split(".")[0]
743
- for node in nodes:
744
- imp = self._extract_import_info(
745
- node, source_code, import_type
746
- )
747
- if imp:
748
- imports.append(imp)
740
+ captures = TreeSitterQueryCompat.safe_execute_query(
741
+ language, query_string, tree.root_node, fallback_result=[]
742
+ )
743
+
744
+ # Group captures by name
745
+ captures_dict = {}
746
+ for node, capture_name in captures:
747
+ if capture_name not in captures_dict:
748
+ captures_dict[capture_name] = []
749
+ captures_dict[capture_name].append(node)
750
+
751
+ # Process different types of imports
752
+ for key, nodes in captures_dict.items():
753
+ if key.endswith("statement"):
754
+ import_type = key.split(".")[0]
755
+ for node in nodes:
756
+ imp = self._extract_import_info(
757
+ node, source_code, import_type
758
+ )
759
+ if imp:
760
+ imports.append(imp)
749
761
  except Exception as query_error:
750
- # Fallback to manual extraction for tree-sitter 0.25.x compatibility
751
- log_warning(f"Query execution failed, using manual extraction: {query_error}")
762
+ # Fallback to manual extraction for tree-sitter compatibility
763
+ log_debug(f"Query execution failed, using manual extraction: {query_error}")
752
764
  imports.extend(self._extract_imports_manual(tree.root_node, source_code))
753
765
  break
754
766
 
@@ -1247,6 +1259,137 @@ class PythonPlugin(LanguagePlugin):
1247
1259
  ],
1248
1260
  }
1249
1261
 
1262
+ def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[dict]:
1263
+ """
1264
+ Execute query strategy for Python language
1265
+
1266
+ Args:
1267
+ tree: Tree-sitter tree object
1268
+ source_code: Source code string
1269
+ query_key: Query key to execute
1270
+
1271
+ Returns:
1272
+ List of query results
1273
+ """
1274
+ # Use the extractor to get elements based on query_key
1275
+ extractor = self.get_extractor()
1276
+
1277
+ # Map query keys to extraction methods
1278
+ if query_key in ["function", "functions", "method", "methods"]:
1279
+ elements = extractor.extract_functions(tree, source_code)
1280
+ elif query_key in ["class", "classes"]:
1281
+ elements = extractor.extract_classes(tree, source_code)
1282
+ elif query_key in ["variable", "variables"]:
1283
+ elements = extractor.extract_variables(tree, source_code)
1284
+ elif query_key in ["import", "imports", "from_import", "from_imports"]:
1285
+ elements = extractor.extract_imports(tree, source_code)
1286
+ else:
1287
+ # For unknown query keys, return empty list
1288
+ return []
1289
+
1290
+ # Convert elements to query result format
1291
+ results = []
1292
+ for element in elements:
1293
+ result = {
1294
+ "capture_name": query_key,
1295
+ "node_type": self._get_node_type_for_element(element),
1296
+ "start_line": element.start_line,
1297
+ "end_line": element.end_line,
1298
+ "text": element.raw_text,
1299
+ "name": element.name,
1300
+ }
1301
+ results.append(result)
1302
+
1303
+ return results
1304
+
1305
+ def _get_node_type_for_element(self, element) -> str:
1306
+ """Get appropriate node type for element"""
1307
+ from ..models import Function, Class, Variable, Import
1308
+
1309
+ if isinstance(element, Function):
1310
+ return "function_definition"
1311
+ elif isinstance(element, Class):
1312
+ return "class_definition"
1313
+ elif isinstance(element, Variable):
1314
+ return "assignment"
1315
+ elif isinstance(element, Import):
1316
+ return "import_statement"
1317
+ else:
1318
+ return "unknown"
1319
+
1320
+ def get_element_categories(self) -> dict[str, list[str]]:
1321
+ """
1322
+ Get element categories mapping query keys to node types
1323
+
1324
+ Returns:
1325
+ Dictionary mapping query keys to lists of node types
1326
+ """
1327
+ return {
1328
+ # Function-related queries
1329
+ "function": ["function_definition"],
1330
+ "functions": ["function_definition"],
1331
+ "async_function": ["function_definition"],
1332
+ "async_functions": ["function_definition"],
1333
+ "method": ["function_definition"],
1334
+ "methods": ["function_definition"],
1335
+ "lambda": ["lambda"],
1336
+ "lambdas": ["lambda"],
1337
+
1338
+ # Class-related queries
1339
+ "class": ["class_definition"],
1340
+ "classes": ["class_definition"],
1341
+
1342
+ # Import-related queries
1343
+ "import": ["import_statement", "import_from_statement"],
1344
+ "imports": ["import_statement", "import_from_statement"],
1345
+ "from_import": ["import_from_statement"],
1346
+ "from_imports": ["import_from_statement"],
1347
+
1348
+ # Variable-related queries
1349
+ "variable": ["assignment"],
1350
+ "variables": ["assignment"],
1351
+
1352
+ # Decorator-related queries
1353
+ "decorator": ["decorator"],
1354
+ "decorators": ["decorator"],
1355
+
1356
+ # Exception-related queries
1357
+ "exception": ["raise_statement", "except_clause"],
1358
+ "exceptions": ["raise_statement", "except_clause"],
1359
+
1360
+ # Comprehension-related queries
1361
+ "comprehension": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
1362
+ "comprehensions": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
1363
+
1364
+ # Context manager queries
1365
+ "context_manager": ["with_statement"],
1366
+ "context_managers": ["with_statement"],
1367
+
1368
+ # Type hint queries
1369
+ "type_hint": ["type"],
1370
+ "type_hints": ["type"],
1371
+
1372
+ # Docstring queries
1373
+ "docstring": ["string"],
1374
+ "docstrings": ["string"],
1375
+
1376
+ # Framework-specific queries
1377
+ "django_model": ["class_definition"],
1378
+ "django_models": ["class_definition"],
1379
+ "flask_route": ["decorator"],
1380
+ "flask_routes": ["decorator"],
1381
+ "fastapi_endpoint": ["function_definition"],
1382
+ "fastapi_endpoints": ["function_definition"],
1383
+
1384
+ # Generic queries
1385
+ "all_elements": [
1386
+ "function_definition", "class_definition", "import_statement", "import_from_statement",
1387
+ "assignment", "decorator", "raise_statement", "except_clause",
1388
+ "list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression",
1389
+ "with_statement", "type", "string", "lambda"
1390
+ ],
1391
+ }
1392
+
1250
1393
  async def analyze_file(
1251
1394
  self, file_path: str, request: AnalysisRequest
1252
1395
  ) -> AnalysisResult:
@@ -1330,9 +1473,9 @@ class PythonPlugin(LanguagePlugin):
1330
1473
  else:
1331
1474
  return {"error": f"Unknown query: {query_name}"}
1332
1475
 
1333
- import tree_sitter
1334
- query = tree_sitter.Query(language, query_string)
1335
- captures = query.captures(tree.root_node)
1476
+ captures = TreeSitterQueryCompat.safe_execute_query(
1477
+ language, query_string, tree.root_node, fallback_result=[]
1478
+ )
1336
1479
  return {"captures": captures, "query": query_string}
1337
1480
 
1338
1481
  except Exception as e: