tree-sitter-analyzer 0.6.2__py3-none-any.whl → 0.8.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 (69) hide show
  1. tree_sitter_analyzer/__init__.py +132 -132
  2. tree_sitter_analyzer/__main__.py +11 -11
  3. tree_sitter_analyzer/api.py +533 -533
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -12
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -26
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -160
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -18
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -141
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -81
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
  14. tree_sitter_analyzer/cli/commands/table_command.py +235 -235
  15. tree_sitter_analyzer/cli/info_commands.py +121 -121
  16. tree_sitter_analyzer/cli_main.py +297 -297
  17. tree_sitter_analyzer/core/__init__.py +15 -15
  18. tree_sitter_analyzer/core/analysis_engine.py +555 -555
  19. tree_sitter_analyzer/core/cache_service.py +320 -320
  20. tree_sitter_analyzer/core/engine.py +566 -566
  21. tree_sitter_analyzer/core/parser.py +293 -293
  22. tree_sitter_analyzer/encoding_utils.py +459 -459
  23. tree_sitter_analyzer/exceptions.py +406 -337
  24. tree_sitter_analyzer/file_handler.py +210 -210
  25. tree_sitter_analyzer/formatters/__init__.py +1 -1
  26. tree_sitter_analyzer/formatters/base_formatter.py +167 -167
  27. tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
  28. tree_sitter_analyzer/interfaces/__init__.py +9 -9
  29. tree_sitter_analyzer/interfaces/cli.py +528 -528
  30. tree_sitter_analyzer/interfaces/cli_adapter.py +343 -343
  31. tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
  32. tree_sitter_analyzer/interfaces/mcp_server.py +425 -405
  33. tree_sitter_analyzer/languages/__init__.py +10 -10
  34. tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
  35. tree_sitter_analyzer/languages/python_plugin.py +755 -755
  36. tree_sitter_analyzer/mcp/__init__.py +31 -31
  37. tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
  38. tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
  39. tree_sitter_analyzer/mcp/server.py +346 -333
  40. tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
  41. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -654
  42. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -247
  43. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
  44. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -300
  45. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -362
  46. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -543
  47. tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
  48. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  49. tree_sitter_analyzer/output_manager.py +253 -253
  50. tree_sitter_analyzer/plugins/__init__.py +280 -280
  51. tree_sitter_analyzer/plugins/base.py +529 -529
  52. tree_sitter_analyzer/plugins/manager.py +379 -379
  53. tree_sitter_analyzer/queries/__init__.py +26 -26
  54. tree_sitter_analyzer/queries/java.py +391 -391
  55. tree_sitter_analyzer/queries/javascript.py +148 -148
  56. tree_sitter_analyzer/queries/python.py +285 -285
  57. tree_sitter_analyzer/queries/typescript.py +229 -229
  58. tree_sitter_analyzer/query_loader.py +257 -257
  59. tree_sitter_analyzer/security/__init__.py +22 -0
  60. tree_sitter_analyzer/security/boundary_manager.py +237 -0
  61. tree_sitter_analyzer/security/regex_checker.py +292 -0
  62. tree_sitter_analyzer/security/validator.py +224 -0
  63. tree_sitter_analyzer/table_formatter.py +652 -473
  64. tree_sitter_analyzer/utils.py +277 -277
  65. {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/METADATA +4 -1
  66. tree_sitter_analyzer-0.8.0.dist-info/RECORD +76 -0
  67. tree_sitter_analyzer-0.6.2.dist-info/RECORD +0 -72
  68. {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/WHEEL +0 -0
  69. {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -1,320 +1,320 @@
1
- #!/usr/bin/env python3
2
- """
3
- Unified Cache Service - Common Cache System for CLI and MCP
4
-
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
-
8
- Roo Code compliance:
9
- - Type hints: Required for all functions
10
- - MCP logging: Log output at each step
11
- - docstring: Google Style docstring
12
- - Performance-focused: Optimization of memory efficiency and access speed
13
- """
14
-
15
- import hashlib
16
- import threading
17
- from dataclasses import dataclass
18
- from datetime import datetime, timedelta
19
- from typing import Any
20
-
21
- from cachetools import LRUCache, TTLCache
22
-
23
- from ..utils import log_debug, log_error, log_info
24
-
25
-
26
- @dataclass(frozen=True)
27
- class CacheEntry:
28
- """
29
- Cache Entry
30
-
31
- Data class that holds cached values and metadata.
32
-
33
- Attributes:
34
- value: Cached value
35
- created_at: Creation timestamp
36
- expires_at: Expiration time
37
- access_count: Access count
38
- """
39
-
40
- value: Any
41
- created_at: datetime
42
- expires_at: datetime | None = None
43
- access_count: int = 0
44
-
45
- def is_expired(self) -> bool:
46
- """
47
- Expiration check
48
-
49
- Returns:
50
- bool: True if expired
51
- """
52
- if self.expires_at is None:
53
- return False
54
- return datetime.now() > self.expires_at
55
-
56
-
57
- class CacheService:
58
- """
59
- Unified Cache Service
60
-
61
- Provides hierarchical cache system and shares cache between CLI and MCP.
62
- 3-tier structure optimized for memory efficiency and access speed.
63
-
64
- Attributes:
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
- """
71
-
72
- def __init__(
73
- self,
74
- l1_maxsize: int = 100,
75
- l2_maxsize: int = 1000,
76
- l3_maxsize: int = 10000,
77
- ttl_seconds: int = 3600,
78
- ) -> None:
79
- """
80
- Initialization
81
-
82
- Args:
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
- """
88
- # Initialize hierarchical cache
89
- self._l1_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l1_maxsize)
90
- self._l2_cache: TTLCache[str, CacheEntry] = TTLCache(
91
- maxsize=l2_maxsize, ttl=ttl_seconds
92
- )
93
- self._l3_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l3_maxsize)
94
-
95
- # Lock for thread safety
96
- self._lock = threading.RLock()
97
-
98
- # Cache statistics
99
- self._stats = {
100
- "hits": 0,
101
- "misses": 0,
102
- "l1_hits": 0,
103
- "l2_hits": 0,
104
- "l3_hits": 0,
105
- "sets": 0,
106
- "evictions": 0,
107
- }
108
-
109
- # デフォルト設定
110
- self._default_ttl = ttl_seconds
111
-
112
- log_info(
113
- f"CacheService initialized: L1={l1_maxsize}, L2={l2_maxsize}, "
114
- f"L3={l3_maxsize}, TTL={ttl_seconds}s"
115
- )
116
-
117
- async def get(self, key: str) -> Any | None:
118
- """
119
- キャッシュから値を取得
120
-
121
- 階層キャッシュを順番にチェックし、見つかった場合は
122
- 上位キャッシュに昇格させる。
123
-
124
- Args:
125
- key: キャッシュキー
126
-
127
- Returns:
128
- キャッシュされた値、見つからない場合はNone
129
-
130
- Raises:
131
- ValueError: 無効なキーの場合
132
- """
133
- if not key or key is None:
134
- raise ValueError("Cache key cannot be empty or None")
135
-
136
- with self._lock:
137
- # L1キャッシュをチェック
138
- entry = self._l1_cache.get(key)
139
- if entry and not entry.is_expired():
140
- self._stats["hits"] += 1
141
- self._stats["l1_hits"] += 1
142
- log_debug(f"Cache L1 hit: {key}")
143
- return entry.value
144
-
145
- # L2キャッシュをチェック
146
- entry = self._l2_cache.get(key)
147
- if entry and not entry.is_expired():
148
- self._stats["hits"] += 1
149
- self._stats["l2_hits"] += 1
150
- # L1に昇格
151
- self._l1_cache[key] = entry
152
- log_debug(f"Cache L2 hit: {key} (promoted to L1)")
153
- return entry.value
154
-
155
- # L3キャッシュをチェック
156
- entry = self._l3_cache.get(key)
157
- if entry and not entry.is_expired():
158
- self._stats["hits"] += 1
159
- self._stats["l3_hits"] += 1
160
- # L2とL1に昇格
161
- self._l2_cache[key] = entry
162
- self._l1_cache[key] = entry
163
- log_debug(f"Cache L3 hit: {key} (promoted to L1/L2)")
164
- return entry.value
165
-
166
- # キャッシュミス
167
- self._stats["misses"] += 1
168
- log_debug(f"Cache miss: {key}")
169
- return None
170
-
171
- async def set(self, key: str, value: Any, ttl_seconds: int | None = None) -> None:
172
- """
173
- キャッシュに値を設定
174
-
175
- Args:
176
- key: キャッシュキー
177
- value: キャッシュする値
178
- ttl_seconds: TTL(秒)、Noneの場合はデフォルト値
179
-
180
- Raises:
181
- ValueError: 無効なキーの場合
182
- TypeError: シリアライズできない値の場合
183
- """
184
- if not key or key is None:
185
- raise ValueError("Cache key cannot be empty or None")
186
-
187
- # シリアライズ可能性チェック
188
- try:
189
- import pickle
190
-
191
- pickle.dumps(value)
192
- except (pickle.PicklingError, TypeError) as e:
193
- raise TypeError(f"Value is not serializable: {e}") from e
194
-
195
- ttl = ttl_seconds or self._default_ttl
196
- expires_at = datetime.now() + timedelta(seconds=ttl)
197
-
198
- entry = CacheEntry(
199
- value=value,
200
- created_at=datetime.now(),
201
- expires_at=expires_at,
202
- access_count=0,
203
- )
204
-
205
- with self._lock:
206
- # 全階層に設定
207
- self._l1_cache[key] = entry
208
- self._l2_cache[key] = entry
209
- self._l3_cache[key] = entry
210
-
211
- self._stats["sets"] += 1
212
- log_debug(f"Cache set: {key} (TTL={ttl}s)")
213
-
214
- def clear(self) -> None:
215
- """
216
- 全キャッシュをクリア
217
- """
218
- with self._lock:
219
- self._l1_cache.clear()
220
- self._l2_cache.clear()
221
- self._l3_cache.clear()
222
-
223
- # 統計をリセット
224
- for key in self._stats:
225
- self._stats[key] = 0
226
-
227
- log_info("All caches cleared")
228
-
229
- def size(self) -> int:
230
- """
231
- キャッシュサイズを取得
232
-
233
- Returns:
234
- L1キャッシュのサイズ(最も頻繁にアクセスされるアイテム数)
235
- """
236
- with self._lock:
237
- return len(self._l1_cache)
238
-
239
- def get_stats(self) -> dict[str, Any]:
240
- """
241
- キャッシュ統計を取得
242
-
243
- Returns:
244
- 統計情報辞書
245
- """
246
- with self._lock:
247
- total_requests = self._stats["hits"] + self._stats["misses"]
248
- hit_rate = (
249
- self._stats["hits"] / total_requests if total_requests > 0 else 0.0
250
- )
251
-
252
- return {
253
- **self._stats,
254
- "hit_rate": hit_rate,
255
- "total_requests": total_requests,
256
- "l1_size": len(self._l1_cache),
257
- "l2_size": len(self._l2_cache),
258
- "l3_size": len(self._l3_cache),
259
- }
260
-
261
- def generate_cache_key(
262
- self, file_path: str, language: str, options: dict[str, Any]
263
- ) -> str:
264
- """
265
- キャッシュキーを生成
266
-
267
- Args:
268
- file_path: ファイルパス
269
- language: プログラミング言語
270
- options: 解析オプション
271
-
272
- Returns:
273
- ハッシュ化されたキャッシュキー
274
- """
275
- # 一意なキーを生成するための文字列を構築
276
- key_components = [
277
- file_path,
278
- language,
279
- str(sorted(options.items())), # 辞書を安定した文字列に変換
280
- ]
281
-
282
- key_string = ":".join(key_components)
283
-
284
- # SHA256でハッシュ化
285
- return hashlib.sha256(key_string.encode("utf-8")).hexdigest()
286
-
287
- async def invalidate_pattern(self, pattern: str) -> int:
288
- """
289
- パターンに一致するキーを無効化
290
-
291
- Args:
292
- pattern: 無効化するキーのパターン
293
-
294
- Returns:
295
- 無効化されたキー数
296
- """
297
- invalidated_count = 0
298
-
299
- with self._lock:
300
- # 各階層からパターンに一致するキーを削除
301
- for cache in [self._l1_cache, self._l2_cache, self._l3_cache]:
302
- keys_to_remove = [key for key in cache.keys() if pattern in key]
303
-
304
- for key in keys_to_remove:
305
- if key in cache:
306
- del cache[key]
307
- invalidated_count += 1
308
-
309
- log_info(
310
- f"Invalidated {invalidated_count} cache entries matching pattern: {pattern}"
311
- )
312
- return invalidated_count
313
-
314
- def __del__(self) -> None:
315
- """デストラクタ - リソースクリーンアップ"""
316
- try:
317
- self.clear()
318
- log_debug("CacheService destroyed and cleaned up")
319
- except Exception as e:
320
- log_error(f"Error during CacheService cleanup: {e}")
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unified Cache Service - Common Cache System for CLI and MCP
4
+
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
+
8
+ Roo Code compliance:
9
+ - Type hints: Required for all functions
10
+ - MCP logging: Log output at each step
11
+ - docstring: Google Style docstring
12
+ - Performance-focused: Optimization of memory efficiency and access speed
13
+ """
14
+
15
+ import hashlib
16
+ import threading
17
+ from dataclasses import dataclass
18
+ from datetime import datetime, timedelta
19
+ from typing import Any
20
+
21
+ from cachetools import LRUCache, TTLCache
22
+
23
+ from ..utils import log_debug, log_error, log_info
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class CacheEntry:
28
+ """
29
+ Cache Entry
30
+
31
+ Data class that holds cached values and metadata.
32
+
33
+ Attributes:
34
+ value: Cached value
35
+ created_at: Creation timestamp
36
+ expires_at: Expiration time
37
+ access_count: Access count
38
+ """
39
+
40
+ value: Any
41
+ created_at: datetime
42
+ expires_at: datetime | None = None
43
+ access_count: int = 0
44
+
45
+ def is_expired(self) -> bool:
46
+ """
47
+ Expiration check
48
+
49
+ Returns:
50
+ bool: True if expired
51
+ """
52
+ if self.expires_at is None:
53
+ return False
54
+ return datetime.now() > self.expires_at
55
+
56
+
57
+ class CacheService:
58
+ """
59
+ Unified Cache Service
60
+
61
+ Provides hierarchical cache system and shares cache between CLI and MCP.
62
+ 3-tier structure optimized for memory efficiency and access speed.
63
+
64
+ Attributes:
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
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ l1_maxsize: int = 100,
75
+ l2_maxsize: int = 1000,
76
+ l3_maxsize: int = 10000,
77
+ ttl_seconds: int = 3600,
78
+ ) -> None:
79
+ """
80
+ Initialization
81
+
82
+ Args:
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
+ """
88
+ # Initialize hierarchical cache
89
+ self._l1_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l1_maxsize)
90
+ self._l2_cache: TTLCache[str, CacheEntry] = TTLCache(
91
+ maxsize=l2_maxsize, ttl=ttl_seconds
92
+ )
93
+ self._l3_cache: LRUCache[str, CacheEntry] = LRUCache(maxsize=l3_maxsize)
94
+
95
+ # Lock for thread safety
96
+ self._lock = threading.RLock()
97
+
98
+ # Cache statistics
99
+ self._stats = {
100
+ "hits": 0,
101
+ "misses": 0,
102
+ "l1_hits": 0,
103
+ "l2_hits": 0,
104
+ "l3_hits": 0,
105
+ "sets": 0,
106
+ "evictions": 0,
107
+ }
108
+
109
+ # デフォルト設定
110
+ self._default_ttl = ttl_seconds
111
+
112
+ log_info(
113
+ f"CacheService initialized: L1={l1_maxsize}, L2={l2_maxsize}, "
114
+ f"L3={l3_maxsize}, TTL={ttl_seconds}s"
115
+ )
116
+
117
+ async def get(self, key: str) -> Any | None:
118
+ """
119
+ キャッシュから値を取得
120
+
121
+ 階層キャッシュを順番にチェックし、見つかった場合は
122
+ 上位キャッシュに昇格させる。
123
+
124
+ Args:
125
+ key: キャッシュキー
126
+
127
+ Returns:
128
+ キャッシュされた値、見つからない場合はNone
129
+
130
+ Raises:
131
+ ValueError: 無効なキーの場合
132
+ """
133
+ if not key or key is None:
134
+ raise ValueError("Cache key cannot be empty or None")
135
+
136
+ with self._lock:
137
+ # L1キャッシュをチェック
138
+ entry = self._l1_cache.get(key)
139
+ if entry and not entry.is_expired():
140
+ self._stats["hits"] += 1
141
+ self._stats["l1_hits"] += 1
142
+ log_debug(f"Cache L1 hit: {key}")
143
+ return entry.value
144
+
145
+ # L2キャッシュをチェック
146
+ entry = self._l2_cache.get(key)
147
+ if entry and not entry.is_expired():
148
+ self._stats["hits"] += 1
149
+ self._stats["l2_hits"] += 1
150
+ # L1に昇格
151
+ self._l1_cache[key] = entry
152
+ log_debug(f"Cache L2 hit: {key} (promoted to L1)")
153
+ return entry.value
154
+
155
+ # L3キャッシュをチェック
156
+ entry = self._l3_cache.get(key)
157
+ if entry and not entry.is_expired():
158
+ self._stats["hits"] += 1
159
+ self._stats["l3_hits"] += 1
160
+ # L2とL1に昇格
161
+ self._l2_cache[key] = entry
162
+ self._l1_cache[key] = entry
163
+ log_debug(f"Cache L3 hit: {key} (promoted to L1/L2)")
164
+ return entry.value
165
+
166
+ # キャッシュミス
167
+ self._stats["misses"] += 1
168
+ log_debug(f"Cache miss: {key}")
169
+ return None
170
+
171
+ async def set(self, key: str, value: Any, ttl_seconds: int | None = None) -> None:
172
+ """
173
+ キャッシュに値を設定
174
+
175
+ Args:
176
+ key: キャッシュキー
177
+ value: キャッシュする値
178
+ ttl_seconds: TTL(秒)、Noneの場合はデフォルト値
179
+
180
+ Raises:
181
+ ValueError: 無効なキーの場合
182
+ TypeError: シリアライズできない値の場合
183
+ """
184
+ if not key or key is None:
185
+ raise ValueError("Cache key cannot be empty or None")
186
+
187
+ # シリアライズ可能性チェック
188
+ try:
189
+ import pickle
190
+
191
+ pickle.dumps(value)
192
+ except (pickle.PicklingError, TypeError) as e:
193
+ raise TypeError(f"Value is not serializable: {e}") from e
194
+
195
+ ttl = ttl_seconds or self._default_ttl
196
+ expires_at = datetime.now() + timedelta(seconds=ttl)
197
+
198
+ entry = CacheEntry(
199
+ value=value,
200
+ created_at=datetime.now(),
201
+ expires_at=expires_at,
202
+ access_count=0,
203
+ )
204
+
205
+ with self._lock:
206
+ # 全階層に設定
207
+ self._l1_cache[key] = entry
208
+ self._l2_cache[key] = entry
209
+ self._l3_cache[key] = entry
210
+
211
+ self._stats["sets"] += 1
212
+ log_debug(f"Cache set: {key} (TTL={ttl}s)")
213
+
214
+ def clear(self) -> None:
215
+ """
216
+ 全キャッシュをクリア
217
+ """
218
+ with self._lock:
219
+ self._l1_cache.clear()
220
+ self._l2_cache.clear()
221
+ self._l3_cache.clear()
222
+
223
+ # 統計をリセット
224
+ for key in self._stats:
225
+ self._stats[key] = 0
226
+
227
+ log_info("All caches cleared")
228
+
229
+ def size(self) -> int:
230
+ """
231
+ キャッシュサイズを取得
232
+
233
+ Returns:
234
+ L1キャッシュのサイズ(最も頻繁にアクセスされるアイテム数)
235
+ """
236
+ with self._lock:
237
+ return len(self._l1_cache)
238
+
239
+ def get_stats(self) -> dict[str, Any]:
240
+ """
241
+ キャッシュ統計を取得
242
+
243
+ Returns:
244
+ 統計情報辞書
245
+ """
246
+ with self._lock:
247
+ total_requests = self._stats["hits"] + self._stats["misses"]
248
+ hit_rate = (
249
+ self._stats["hits"] / total_requests if total_requests > 0 else 0.0
250
+ )
251
+
252
+ return {
253
+ **self._stats,
254
+ "hit_rate": hit_rate,
255
+ "total_requests": total_requests,
256
+ "l1_size": len(self._l1_cache),
257
+ "l2_size": len(self._l2_cache),
258
+ "l3_size": len(self._l3_cache),
259
+ }
260
+
261
+ def generate_cache_key(
262
+ self, file_path: str, language: str, options: dict[str, Any]
263
+ ) -> str:
264
+ """
265
+ キャッシュキーを生成
266
+
267
+ Args:
268
+ file_path: ファイルパス
269
+ language: プログラミング言語
270
+ options: 解析オプション
271
+
272
+ Returns:
273
+ ハッシュ化されたキャッシュキー
274
+ """
275
+ # 一意なキーを生成するための文字列を構築
276
+ key_components = [
277
+ file_path,
278
+ language,
279
+ str(sorted(options.items())), # 辞書を安定した文字列に変換
280
+ ]
281
+
282
+ key_string = ":".join(key_components)
283
+
284
+ # SHA256でハッシュ化
285
+ return hashlib.sha256(key_string.encode("utf-8")).hexdigest()
286
+
287
+ async def invalidate_pattern(self, pattern: str) -> int:
288
+ """
289
+ パターンに一致するキーを無効化
290
+
291
+ Args:
292
+ pattern: 無効化するキーのパターン
293
+
294
+ Returns:
295
+ 無効化されたキー数
296
+ """
297
+ invalidated_count = 0
298
+
299
+ with self._lock:
300
+ # 各階層からパターンに一致するキーを削除
301
+ for cache in [self._l1_cache, self._l2_cache, self._l3_cache]:
302
+ keys_to_remove = [key for key in cache.keys() if pattern in key]
303
+
304
+ for key in keys_to_remove:
305
+ if key in cache:
306
+ del cache[key]
307
+ invalidated_count += 1
308
+
309
+ log_info(
310
+ f"Invalidated {invalidated_count} cache entries matching pattern: {pattern}"
311
+ )
312
+ return invalidated_count
313
+
314
+ def __del__(self) -> None:
315
+ """デストラクタ - リソースクリーンアップ"""
316
+ try:
317
+ self.clear()
318
+ log_debug("CacheService destroyed and cleaned up")
319
+ except Exception as e:
320
+ log_error(f"Error during CacheService cleanup: {e}")