tree-sitter-analyzer 0.4.0__py3-none-any.whl → 0.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.
- tree_sitter_analyzer/__init__.py +1 -3
- tree_sitter_analyzer/__main__.py +2 -2
- tree_sitter_analyzer/cli/commands/default_command.py +1 -1
- tree_sitter_analyzer/cli/commands/query_command.py +5 -5
- tree_sitter_analyzer/cli/commands/table_command.py +3 -3
- tree_sitter_analyzer/cli/info_commands.py +14 -13
- tree_sitter_analyzer/cli_main.py +49 -30
- tree_sitter_analyzer/core/analysis_engine.py +21 -21
- tree_sitter_analyzer/core/cache_service.py +31 -31
- tree_sitter_analyzer/core/query.py +502 -502
- tree_sitter_analyzer/encoding_utils.py +5 -2
- tree_sitter_analyzer/file_handler.py +3 -3
- tree_sitter_analyzer/formatters/base_formatter.py +18 -18
- tree_sitter_analyzer/formatters/formatter_factory.py +15 -15
- tree_sitter_analyzer/formatters/java_formatter.py +291 -291
- tree_sitter_analyzer/formatters/python_formatter.py +259 -259
- tree_sitter_analyzer/interfaces/cli_adapter.py +32 -32
- tree_sitter_analyzer/interfaces/mcp_adapter.py +2 -2
- tree_sitter_analyzer/language_detector.py +398 -398
- tree_sitter_analyzer/language_loader.py +224 -224
- tree_sitter_analyzer/languages/java_plugin.py +1174 -1174
- tree_sitter_analyzer/languages/python_plugin.py +10 -2
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +555 -555
- tree_sitter_analyzer/models.py +470 -470
- tree_sitter_analyzer/output_manager.py +8 -10
- tree_sitter_analyzer/plugins/base.py +33 -0
- tree_sitter_analyzer/queries/java.py +78 -78
- tree_sitter_analyzer/queries/javascript.py +7 -7
- tree_sitter_analyzer/queries/python.py +18 -18
- tree_sitter_analyzer/queries/typescript.py +12 -12
- tree_sitter_analyzer/query_loader.py +13 -13
- tree_sitter_analyzer/table_formatter.py +20 -18
- tree_sitter_analyzer/utils.py +1 -1
- {tree_sitter_analyzer-0.4.0.dist-info → tree_sitter_analyzer-0.6.1.dist-info}/METADATA +10 -10
- {tree_sitter_analyzer-0.4.0.dist-info → tree_sitter_analyzer-0.6.1.dist-info}/RECORD +37 -38
- tree_sitter_analyzer/java_analyzer.py +0 -187
- {tree_sitter_analyzer-0.4.0.dist-info → tree_sitter_analyzer-0.6.1.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.4.0.dist-info → tree_sitter_analyzer-0.6.1.dist-info}/entry_points.txt +0 -0
tree_sitter_analyzer/__init__.py
CHANGED
|
@@ -17,7 +17,7 @@ __email__ = "aimasteracc@gmail.com"
|
|
|
17
17
|
|
|
18
18
|
# Legacy imports for backward compatibility
|
|
19
19
|
|
|
20
|
-
# Core Engine -
|
|
20
|
+
# Core Engine - temporary direct import
|
|
21
21
|
from .core.analysis_engine import UnifiedAnalysisEngine as UniversalCodeAnalyzer
|
|
22
22
|
from .encoding_utils import (
|
|
23
23
|
EncodingManager,
|
|
@@ -30,7 +30,6 @@ from .encoding_utils import (
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
# from .java_advanced_analyzer import AdvancedAnalyzer # Removed - migrated to plugin system
|
|
33
|
-
from .java_analyzer import CodeAnalyzer
|
|
34
33
|
from .language_detector import LanguageDetector
|
|
35
34
|
from .language_loader import get_loader
|
|
36
35
|
|
|
@@ -119,7 +118,6 @@ __all__ = [
|
|
|
119
118
|
"output_error",
|
|
120
119
|
"output_data",
|
|
121
120
|
# Legacy Components (backward compatibility)
|
|
122
|
-
"CodeAnalyzer",
|
|
123
121
|
"UniversalCodeAnalyzer",
|
|
124
122
|
# Version
|
|
125
123
|
"__version__",
|
tree_sitter_analyzer/__main__.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Tree-sitter Analyzer
|
|
3
|
+
Tree-sitter Analyzer package main entry point
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This file allows the package to be executed with `python -m tree_sitter_analyzer`.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from .cli_main import main
|
|
@@ -14,5 +14,5 @@ class DefaultCommand(BaseCommand):
|
|
|
14
14
|
|
|
15
15
|
async def execute_async(self, language: str) -> int:
|
|
16
16
|
"""Execute default command - show error for missing options."""
|
|
17
|
-
output_error("ERROR:
|
|
17
|
+
output_error("ERROR: Please specify a query or --advanced option")
|
|
18
18
|
return 1
|
|
@@ -22,7 +22,7 @@ class QueryCommand(BaseCommand):
|
|
|
22
22
|
query_to_execute = query_loader.get_query(language, self.args.query_key)
|
|
23
23
|
if query_to_execute is None:
|
|
24
24
|
output_error(
|
|
25
|
-
f"ERROR:
|
|
25
|
+
f"ERROR: Query '{self.args.query_key}' not found for language '{language}'"
|
|
26
26
|
)
|
|
27
27
|
return 1
|
|
28
28
|
except ValueError as e:
|
|
@@ -32,7 +32,7 @@ class QueryCommand(BaseCommand):
|
|
|
32
32
|
query_to_execute = self.args.query_string
|
|
33
33
|
|
|
34
34
|
if not query_to_execute:
|
|
35
|
-
output_error("ERROR:
|
|
35
|
+
output_error("ERROR: No query specified.")
|
|
36
36
|
return 1
|
|
37
37
|
|
|
38
38
|
# Perform analysis
|
|
@@ -72,10 +72,10 @@ class QueryCommand(BaseCommand):
|
|
|
72
72
|
f"\n{i}. {query_result['capture_name']} ({query_result['node_type']})"
|
|
73
73
|
)
|
|
74
74
|
output_data(
|
|
75
|
-
f"
|
|
75
|
+
f" Position: Line {query_result['start_line']}-{query_result['end_line']}"
|
|
76
76
|
)
|
|
77
|
-
output_data(f"
|
|
77
|
+
output_data(f" Content:\n{query_result['content']}")
|
|
78
78
|
else:
|
|
79
|
-
output_info("\nINFO:
|
|
79
|
+
output_info("\nINFO: No results found matching the query.")
|
|
80
80
|
|
|
81
81
|
return 0
|
|
@@ -42,7 +42,7 @@ class TableCommand(BaseCommand):
|
|
|
42
42
|
return 0
|
|
43
43
|
|
|
44
44
|
except Exception as e:
|
|
45
|
-
output_error(f"ERROR:
|
|
45
|
+
output_error(f"ERROR: An error occurred during table format analysis: {e}")
|
|
46
46
|
return 1
|
|
47
47
|
|
|
48
48
|
def _convert_to_structure_format(
|
|
@@ -228,8 +228,8 @@ class TableCommand(BaseCommand):
|
|
|
228
228
|
def _output_table(self, table_output: str) -> None:
|
|
229
229
|
"""Output the table with proper encoding."""
|
|
230
230
|
try:
|
|
231
|
-
# Windows
|
|
231
|
+
# Windows support: Output with UTF-8 encoding
|
|
232
232
|
sys.stdout.buffer.write(table_output.encode("utf-8"))
|
|
233
233
|
except (AttributeError, UnicodeEncodeError):
|
|
234
|
-
#
|
|
234
|
+
# Fallback: Normal print
|
|
235
235
|
print(table_output, end="")
|
|
@@ -34,22 +34,23 @@ class ListQueriesCommand(InfoCommand):
|
|
|
34
34
|
elif hasattr(self.args, "file_path") and self.args.file_path:
|
|
35
35
|
language = detect_language_from_file(self.args.file_path)
|
|
36
36
|
else:
|
|
37
|
-
output_list("
|
|
37
|
+
output_list("Supported languages:")
|
|
38
38
|
for lang in query_loader.list_supported_languages():
|
|
39
39
|
output_list(f" {lang}")
|
|
40
40
|
queries = query_loader.list_queries_for_language(lang)
|
|
41
41
|
for query_key in queries:
|
|
42
42
|
description = (
|
|
43
43
|
query_loader.get_query_description(lang, query_key)
|
|
44
|
-
or "
|
|
44
|
+
or "No description"
|
|
45
45
|
)
|
|
46
46
|
output_list(f" {query_key:<20} - {description}")
|
|
47
47
|
return 0
|
|
48
48
|
|
|
49
|
-
output_list(f"
|
|
49
|
+
output_list(f"Available query keys ({language}):")
|
|
50
50
|
for query_key in query_loader.list_queries_for_language(language):
|
|
51
51
|
description = (
|
|
52
|
-
query_loader.get_query_description(language, query_key)
|
|
52
|
+
query_loader.get_query_description(language, query_key)
|
|
53
|
+
or "No description"
|
|
53
54
|
)
|
|
54
55
|
output_list(f" {query_key:<20} - {description}")
|
|
55
56
|
return 0
|
|
@@ -65,7 +66,7 @@ class DescribeQueryCommand(InfoCommand):
|
|
|
65
66
|
language = detect_language_from_file(self.args.file_path)
|
|
66
67
|
else:
|
|
67
68
|
output_error(
|
|
68
|
-
"ERROR:
|
|
69
|
+
"ERROR: Query description display requires --language or target file specification"
|
|
69
70
|
)
|
|
70
71
|
return 1
|
|
71
72
|
|
|
@@ -77,14 +78,14 @@ class DescribeQueryCommand(InfoCommand):
|
|
|
77
78
|
|
|
78
79
|
if query_description is None or query_content is None:
|
|
79
80
|
output_error(
|
|
80
|
-
f"ERROR:
|
|
81
|
+
f"ERROR: Query '{self.args.describe_query}' not found for language '{language}'"
|
|
81
82
|
)
|
|
82
83
|
return 1
|
|
83
84
|
|
|
84
85
|
output_info(
|
|
85
|
-
f"
|
|
86
|
+
f"Query key '{self.args.describe_query}' ({language}): {query_description}"
|
|
86
87
|
)
|
|
87
|
-
output_data(f"
|
|
88
|
+
output_data(f"Query content:\n{query_content}")
|
|
88
89
|
except ValueError as e:
|
|
89
90
|
output_error(f"ERROR: {e}")
|
|
90
91
|
return 1
|
|
@@ -95,13 +96,13 @@ class ShowLanguagesCommand(InfoCommand):
|
|
|
95
96
|
"""Command to show supported languages."""
|
|
96
97
|
|
|
97
98
|
def execute(self) -> int:
|
|
98
|
-
output_list("
|
|
99
|
+
output_list("Supported languages:")
|
|
99
100
|
for language in detector.get_supported_languages():
|
|
100
101
|
info = detector.get_language_info(language)
|
|
101
102
|
extensions = ", ".join(info["extensions"][:5])
|
|
102
103
|
if len(info["extensions"]) > 5:
|
|
103
|
-
extensions += f", ... (
|
|
104
|
-
output_list(f" {language:<12} -
|
|
104
|
+
extensions += f", ... ({len(info['extensions'])-5} more)"
|
|
105
|
+
output_list(f" {language:<12} - Extensions: {extensions}")
|
|
105
106
|
return 0
|
|
106
107
|
|
|
107
108
|
|
|
@@ -109,12 +110,12 @@ class ShowExtensionsCommand(InfoCommand):
|
|
|
109
110
|
"""Command to show supported extensions."""
|
|
110
111
|
|
|
111
112
|
def execute(self) -> int:
|
|
112
|
-
output_list("
|
|
113
|
+
output_list("Supported file extensions:")
|
|
113
114
|
supported_extensions = detector.get_supported_extensions()
|
|
114
115
|
for i in range(0, len(supported_extensions), 8):
|
|
115
116
|
line = " " + " ".join(
|
|
116
117
|
f"{ext:<6}" for ext in supported_extensions[i : i + 8]
|
|
117
118
|
)
|
|
118
119
|
output_list(line)
|
|
119
|
-
output_info(f"\
|
|
120
|
+
output_info(f"\nTotal {len(supported_extensions)} extensions supported")
|
|
120
121
|
return 0
|
tree_sitter_analyzer/cli_main.py
CHANGED
|
@@ -79,46 +79,50 @@ class CLICommandFactory:
|
|
|
79
79
|
def create_argument_parser() -> argparse.ArgumentParser:
|
|
80
80
|
"""Create and configure the argument parser."""
|
|
81
81
|
parser = argparse.ArgumentParser(
|
|
82
|
-
description="Tree-sitter
|
|
83
|
-
epilog="
|
|
82
|
+
description="Analyze code using Tree-sitter and extract structured information.",
|
|
83
|
+
epilog="Example: tree-sitter-analyzer example.java --table=full",
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
# File path
|
|
87
|
-
parser.add_argument("file_path", nargs="?", help="
|
|
87
|
+
parser.add_argument("file_path", nargs="?", help="Path to the file to analyze")
|
|
88
88
|
|
|
89
89
|
# Query options
|
|
90
90
|
query_group = parser.add_mutually_exclusive_group(required=False)
|
|
91
91
|
query_group.add_argument(
|
|
92
|
-
"--query-key", help="
|
|
92
|
+
"--query-key", help="Available query key (e.g., class, method)"
|
|
93
93
|
)
|
|
94
94
|
query_group.add_argument(
|
|
95
|
-
"--query-string", help="
|
|
95
|
+
"--query-string", help="Directly specify Tree-sitter query to execute"
|
|
96
96
|
)
|
|
97
97
|
|
|
98
98
|
# Information options
|
|
99
99
|
parser.add_argument(
|
|
100
|
-
"--list-queries",
|
|
100
|
+
"--list-queries",
|
|
101
|
+
action="store_true",
|
|
102
|
+
help="Display list of available query keys",
|
|
103
|
+
)
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--describe-query", help="Display description of specified query key"
|
|
101
106
|
)
|
|
102
|
-
parser.add_argument("--describe-query", help="指定されたクエリキーの説明を表示")
|
|
103
107
|
parser.add_argument(
|
|
104
108
|
"--show-supported-languages",
|
|
105
109
|
action="store_true",
|
|
106
|
-
help="
|
|
110
|
+
help="Display list of supported languages",
|
|
107
111
|
)
|
|
108
112
|
parser.add_argument(
|
|
109
113
|
"--show-supported-extensions",
|
|
110
114
|
action="store_true",
|
|
111
|
-
help="
|
|
115
|
+
help="Display list of supported file extensions",
|
|
112
116
|
)
|
|
113
117
|
parser.add_argument(
|
|
114
118
|
"--show-common-queries",
|
|
115
119
|
action="store_true",
|
|
116
|
-
help="
|
|
120
|
+
help="Display list of common queries across multiple languages",
|
|
117
121
|
)
|
|
118
122
|
parser.add_argument(
|
|
119
123
|
"--show-query-languages",
|
|
120
124
|
action="store_true",
|
|
121
|
-
help="
|
|
125
|
+
help="Display list of languages with query support",
|
|
122
126
|
)
|
|
123
127
|
|
|
124
128
|
# Output format options
|
|
@@ -126,52 +130,67 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
126
130
|
"--output-format",
|
|
127
131
|
choices=["json", "text"],
|
|
128
132
|
default="json",
|
|
129
|
-
help="
|
|
133
|
+
help="Specify output format",
|
|
130
134
|
)
|
|
131
135
|
parser.add_argument(
|
|
132
|
-
"--table", choices=["full", "compact", "csv"], help="
|
|
136
|
+
"--table", choices=["full", "compact", "csv"], help="Output in table format"
|
|
133
137
|
)
|
|
134
138
|
parser.add_argument(
|
|
135
139
|
"--include-javadoc",
|
|
136
140
|
action="store_true",
|
|
137
|
-
help="JavaDoc
|
|
141
|
+
help="Include JavaDoc/documentation comments in output",
|
|
138
142
|
)
|
|
139
143
|
|
|
140
144
|
# Analysis options
|
|
141
|
-
parser.add_argument(
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
"--advanced", action="store_true", help="Use advanced analysis features"
|
|
147
|
+
)
|
|
142
148
|
parser.add_argument(
|
|
143
149
|
"--summary",
|
|
144
150
|
nargs="?",
|
|
145
151
|
const="classes,methods",
|
|
146
|
-
help="
|
|
152
|
+
help="Display summary of specified element types",
|
|
153
|
+
)
|
|
154
|
+
parser.add_argument(
|
|
155
|
+
"--structure",
|
|
156
|
+
action="store_true",
|
|
157
|
+
help="Output detailed structure information in JSON format",
|
|
147
158
|
)
|
|
148
159
|
parser.add_argument(
|
|
149
|
-
"--
|
|
160
|
+
"--statistics", action="store_true", help="Display only statistical information"
|
|
150
161
|
)
|
|
151
|
-
parser.add_argument("--statistics", action="store_true", help="統計情報のみを表示")
|
|
152
162
|
|
|
153
163
|
# Language options
|
|
154
164
|
parser.add_argument(
|
|
155
|
-
"--language",
|
|
165
|
+
"--language",
|
|
166
|
+
help="Explicitly specify language (auto-detected from extension if omitted)",
|
|
156
167
|
)
|
|
157
168
|
|
|
158
169
|
# Logging options
|
|
159
170
|
parser.add_argument(
|
|
160
|
-
"--quiet",
|
|
171
|
+
"--quiet",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Suppress INFO level logs (show errors only)",
|
|
161
174
|
)
|
|
162
175
|
|
|
163
176
|
# Partial reading options
|
|
164
177
|
parser.add_argument(
|
|
165
178
|
"--partial-read",
|
|
166
179
|
action="store_true",
|
|
167
|
-
help="
|
|
180
|
+
help="Enable partial file reading mode",
|
|
181
|
+
)
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
"--start-line", type=int, help="Starting line number for reading (1-based)"
|
|
184
|
+
)
|
|
185
|
+
parser.add_argument(
|
|
186
|
+
"--end-line", type=int, help="Ending line number for reading (1-based)"
|
|
187
|
+
)
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"--start-column", type=int, help="Starting column number for reading (0-based)"
|
|
168
190
|
)
|
|
169
|
-
parser.add_argument("--start-line", type=int, help="読み込み開始行番号(1ベース)")
|
|
170
|
-
parser.add_argument("--end-line", type=int, help="読み込み終了行番号(1ベース)")
|
|
171
191
|
parser.add_argument(
|
|
172
|
-
"--
|
|
192
|
+
"--end-column", type=int, help="Ending column number for reading (0-based)"
|
|
173
193
|
)
|
|
174
|
-
parser.add_argument("--end-column", type=int, help="読み込み終了列番号(0ベース)")
|
|
175
194
|
|
|
176
195
|
return parser
|
|
177
196
|
|
|
@@ -205,20 +224,20 @@ def handle_special_commands(args: argparse.Namespace) -> int | None:
|
|
|
205
224
|
|
|
206
225
|
# Query language commands
|
|
207
226
|
if args.show_query_languages:
|
|
208
|
-
output_list(["
|
|
227
|
+
output_list(["Languages with query support:"])
|
|
209
228
|
for lang in query_loader.list_supported_languages():
|
|
210
229
|
query_count = len(query_loader.list_queries_for_language(lang))
|
|
211
|
-
output_list([f" {lang:<15} ({query_count}
|
|
230
|
+
output_list([f" {lang:<15} ({query_count} queries)"])
|
|
212
231
|
return 0
|
|
213
232
|
|
|
214
233
|
if args.show_common_queries:
|
|
215
234
|
common_queries = query_loader.get_common_queries()
|
|
216
235
|
if common_queries:
|
|
217
|
-
output_list("
|
|
236
|
+
output_list("Common queries across multiple languages:")
|
|
218
237
|
for query in common_queries:
|
|
219
238
|
output_list(f" {query}")
|
|
220
239
|
else:
|
|
221
|
-
output_info("
|
|
240
|
+
output_info("No common queries found.")
|
|
222
241
|
return 0
|
|
223
242
|
|
|
224
243
|
return None
|
|
@@ -262,7 +281,7 @@ def main() -> None:
|
|
|
262
281
|
if not args.file_path:
|
|
263
282
|
output_error("ERROR: File path not specified.")
|
|
264
283
|
else:
|
|
265
|
-
output_error("ERROR:
|
|
284
|
+
output_error("ERROR: No executable command specified.")
|
|
266
285
|
parser.print_help()
|
|
267
286
|
sys.exit(1)
|
|
268
287
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
|
|
3
|
+
Unified Analysis Engine - Common Analysis System for CLI and MCP (Fixed Version)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
CLI
|
|
5
|
+
This module provides a unified engine that serves as the center of all analysis processing.
|
|
6
|
+
It is commonly used by CLI, MCP, and other interfaces.
|
|
7
7
|
|
|
8
|
-
Roo Code
|
|
9
|
-
-
|
|
10
|
-
- MCP
|
|
8
|
+
Roo Code compliance:
|
|
9
|
+
- Type hints: Required for all functions
|
|
10
|
+
- MCP logging: Log output at each step
|
|
11
11
|
- docstring: Google Style docstring
|
|
12
|
-
-
|
|
12
|
+
- Performance-focused: Singleton pattern and cache sharing
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import hashlib
|
|
@@ -25,31 +25,31 @@ from .cache_service import CacheService
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class UnsupportedLanguageError(Exception):
|
|
28
|
-
"""
|
|
28
|
+
"""Unsupported language error"""
|
|
29
29
|
|
|
30
30
|
pass
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class PluginRegistry(Protocol):
|
|
34
|
-
"""
|
|
34
|
+
"""Protocol for plugin registration management"""
|
|
35
35
|
|
|
36
36
|
def get_plugin(self, language: str) -> Optional["LanguagePlugin"]:
|
|
37
|
-
"""
|
|
37
|
+
"""Get language plugin"""
|
|
38
38
|
...
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class LanguagePlugin(Protocol):
|
|
42
|
-
"""
|
|
42
|
+
"""Language plugin protocol"""
|
|
43
43
|
|
|
44
44
|
async def analyze_file(
|
|
45
45
|
self, file_path: str, request: "AnalysisRequest"
|
|
46
46
|
) -> AnalysisResult:
|
|
47
|
-
"""
|
|
47
|
+
"""File analysis"""
|
|
48
48
|
...
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
class PerformanceMonitor:
|
|
52
|
-
"""
|
|
52
|
+
"""Performance monitoring (simplified version)"""
|
|
53
53
|
|
|
54
54
|
def __init__(self) -> None:
|
|
55
55
|
self._last_duration: float = 0.0
|
|
@@ -58,33 +58,33 @@ class PerformanceMonitor:
|
|
|
58
58
|
self._total_operations: int = 0
|
|
59
59
|
|
|
60
60
|
def measure_operation(self, operation_name: str) -> "PerformanceContext":
|
|
61
|
-
"""
|
|
61
|
+
"""Return measurement context for operation"""
|
|
62
62
|
return PerformanceContext(operation_name, self)
|
|
63
63
|
|
|
64
64
|
def get_last_duration(self) -> float:
|
|
65
|
-
"""
|
|
65
|
+
"""Get last operation time"""
|
|
66
66
|
return self._last_duration
|
|
67
67
|
|
|
68
68
|
def _set_duration(self, duration: float) -> None:
|
|
69
|
-
"""
|
|
69
|
+
"""Set operation time (internal use)"""
|
|
70
70
|
self._last_duration = duration
|
|
71
71
|
|
|
72
72
|
def start_monitoring(self) -> None:
|
|
73
|
-
"""
|
|
73
|
+
"""Start performance monitoring"""
|
|
74
74
|
self._monitoring_active = True
|
|
75
75
|
log_info("Performance monitoring started")
|
|
76
76
|
|
|
77
77
|
def stop_monitoring(self) -> None:
|
|
78
|
-
"""
|
|
78
|
+
"""Stop performance monitoring"""
|
|
79
79
|
self._monitoring_active = False
|
|
80
80
|
log_info("Performance monitoring stopped")
|
|
81
81
|
|
|
82
82
|
def get_operation_stats(self) -> dict[str, Any]:
|
|
83
|
-
"""
|
|
83
|
+
"""Get operation statistics"""
|
|
84
84
|
return self._operation_stats.copy()
|
|
85
85
|
|
|
86
86
|
def get_performance_summary(self) -> dict[str, Any]:
|
|
87
|
-
"""
|
|
87
|
+
"""Get performance summary"""
|
|
88
88
|
return {
|
|
89
89
|
"total_operations": self._total_operations,
|
|
90
90
|
"monitoring_active": self._monitoring_active,
|
|
@@ -93,7 +93,7 @@ class PerformanceMonitor:
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
def record_operation(self, operation_name: str, duration: float) -> None:
|
|
96
|
-
"""
|
|
96
|
+
"""Record operation"""
|
|
97
97
|
if self._monitoring_active:
|
|
98
98
|
if operation_name not in self._operation_stats:
|
|
99
99
|
self._operation_stats[operation_name] = {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
|
|
3
|
+
Unified Cache Service - Common Cache System for CLI and MCP
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
L1
|
|
5
|
+
This module provides a memory-efficient hierarchical cache system.
|
|
6
|
+
Achieves optimal performance with a 3-tier structure: L1 (fast), L2 (medium-term), L3 (long-term).
|
|
7
7
|
|
|
8
|
-
Roo Code
|
|
9
|
-
-
|
|
10
|
-
- MCP
|
|
8
|
+
Roo Code compliance:
|
|
9
|
+
- Type hints: Required for all functions
|
|
10
|
+
- MCP logging: Log output at each step
|
|
11
11
|
- docstring: Google Style docstring
|
|
12
|
-
-
|
|
12
|
+
- Performance-focused: Optimization of memory efficiency and access speed
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import hashlib
|
|
@@ -26,15 +26,15 @@ from ..utils import log_debug, log_error, log_info
|
|
|
26
26
|
@dataclass(frozen=True)
|
|
27
27
|
class CacheEntry:
|
|
28
28
|
"""
|
|
29
|
-
|
|
29
|
+
Cache Entry
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Data class that holds cached values and metadata.
|
|
32
32
|
|
|
33
33
|
Attributes:
|
|
34
|
-
value:
|
|
35
|
-
created_at:
|
|
36
|
-
expires_at:
|
|
37
|
-
access_count:
|
|
34
|
+
value: Cached value
|
|
35
|
+
created_at: Creation timestamp
|
|
36
|
+
expires_at: Expiration time
|
|
37
|
+
access_count: Access count
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
40
|
value: Any
|
|
@@ -44,10 +44,10 @@ class CacheEntry:
|
|
|
44
44
|
|
|
45
45
|
def is_expired(self) -> bool:
|
|
46
46
|
"""
|
|
47
|
-
|
|
47
|
+
Expiration check
|
|
48
48
|
|
|
49
49
|
Returns:
|
|
50
|
-
bool:
|
|
50
|
+
bool: True if expired
|
|
51
51
|
"""
|
|
52
52
|
if self.expires_at is None:
|
|
53
53
|
return False
|
|
@@ -56,17 +56,17 @@ class CacheEntry:
|
|
|
56
56
|
|
|
57
57
|
class CacheService:
|
|
58
58
|
"""
|
|
59
|
-
|
|
59
|
+
Unified Cache Service
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
Provides hierarchical cache system and shares cache between CLI and MCP.
|
|
62
|
+
3-tier structure optimized for memory efficiency and access speed.
|
|
63
63
|
|
|
64
64
|
Attributes:
|
|
65
|
-
_l1_cache: L1
|
|
66
|
-
_l2_cache: L2
|
|
67
|
-
_l3_cache: L3
|
|
68
|
-
_lock:
|
|
69
|
-
_stats:
|
|
65
|
+
_l1_cache: L1 cache (for fast access)
|
|
66
|
+
_l2_cache: L2 cache (for medium-term storage)
|
|
67
|
+
_l3_cache: L3 cache (for long-term storage)
|
|
68
|
+
_lock: Lock for thread safety
|
|
69
|
+
_stats: Cache statistics
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
72
|
def __init__(
|
|
@@ -77,25 +77,25 @@ class CacheService:
|
|
|
77
77
|
ttl_seconds: int = 3600,
|
|
78
78
|
) -> None:
|
|
79
79
|
"""
|
|
80
|
-
|
|
80
|
+
Initialization
|
|
81
81
|
|
|
82
82
|
Args:
|
|
83
|
-
l1_maxsize: L1
|
|
84
|
-
l2_maxsize: L2
|
|
85
|
-
l3_maxsize: L3
|
|
86
|
-
ttl_seconds:
|
|
83
|
+
l1_maxsize: Maximum size of L1 cache
|
|
84
|
+
l2_maxsize: Maximum size of L2 cache
|
|
85
|
+
l3_maxsize: Maximum size of L3 cache
|
|
86
|
+
ttl_seconds: Default TTL (seconds)
|
|
87
87
|
"""
|
|
88
|
-
#
|
|
88
|
+
# Initialize hierarchical cache
|
|
89
89
|
self._l1_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l1_maxsize)
|
|
90
90
|
self._l2_cache: TTLCache[str, CacheEntry] = TTLCache(
|
|
91
91
|
maxsize=l2_maxsize, ttl=ttl_seconds
|
|
92
92
|
)
|
|
93
93
|
self._l3_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l3_maxsize)
|
|
94
94
|
|
|
95
|
-
#
|
|
95
|
+
# Lock for thread safety
|
|
96
96
|
self._lock = threading.RLock()
|
|
97
97
|
|
|
98
|
-
#
|
|
98
|
+
# Cache statistics
|
|
99
99
|
self._stats = {
|
|
100
100
|
"hits": 0,
|
|
101
101
|
"misses": 0,
|