crackerjack 0.33.0__py3-none-any.whl → 0.33.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 crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +196 -25
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""Performance-optimized caching layer for expensive operations.
|
|
2
|
-
|
|
3
|
-
This module provides intelligent caching for git operations, file system checks,
|
|
4
|
-
and command results to significantly improve workflow performance.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
1
|
import asyncio
|
|
8
2
|
import builtins
|
|
9
3
|
import hashlib
|
|
@@ -19,23 +13,19 @@ from crackerjack.services.logging import get_logger
|
|
|
19
13
|
|
|
20
14
|
@dataclass
|
|
21
15
|
class CacheEntry:
|
|
22
|
-
"""Represents a cached value with metadata."""
|
|
23
|
-
|
|
24
16
|
value: t.Any
|
|
25
17
|
created_at: datetime
|
|
26
18
|
access_count: int = 0
|
|
27
19
|
last_accessed: datetime = field(default_factory=datetime.now)
|
|
28
|
-
ttl_seconds: int = 300
|
|
20
|
+
ttl_seconds: int = 300
|
|
29
21
|
invalidation_keys: set[str] = field(default_factory=set)
|
|
30
22
|
|
|
31
23
|
def is_expired(self) -> bool:
|
|
32
|
-
|
|
33
|
-
if self.ttl_seconds <= 0: # Permanent cache
|
|
24
|
+
if self.ttl_seconds <= 0:
|
|
34
25
|
return False
|
|
35
26
|
return datetime.now() > self.created_at + timedelta(seconds=self.ttl_seconds)
|
|
36
27
|
|
|
37
28
|
def access(self) -> t.Any:
|
|
38
|
-
"""Mark entry as accessed and return value."""
|
|
39
29
|
self.access_count += 1
|
|
40
30
|
self.last_accessed = datetime.now()
|
|
41
31
|
return self.value
|
|
@@ -43,8 +33,6 @@ class CacheEntry:
|
|
|
43
33
|
|
|
44
34
|
@dataclass
|
|
45
35
|
class CacheStats:
|
|
46
|
-
"""Cache performance statistics."""
|
|
47
|
-
|
|
48
36
|
hits: int = 0
|
|
49
37
|
misses: int = 0
|
|
50
38
|
evictions: int = 0
|
|
@@ -52,14 +40,11 @@ class CacheStats:
|
|
|
52
40
|
|
|
53
41
|
@property
|
|
54
42
|
def hit_ratio(self) -> float:
|
|
55
|
-
"""Calculate cache hit ratio."""
|
|
56
43
|
total = self.hits + self.misses
|
|
57
44
|
return self.hits / total if total > 0 else 0.0
|
|
58
45
|
|
|
59
46
|
|
|
60
47
|
class PerformanceCache:
|
|
61
|
-
"""High-performance async cache with intelligent invalidation."""
|
|
62
|
-
|
|
63
48
|
def __init__(
|
|
64
49
|
self,
|
|
65
50
|
max_memory_mb: int = 50,
|
|
@@ -77,17 +62,14 @@ class PerformanceCache:
|
|
|
77
62
|
self._cleanup_task: asyncio.Task[None] | None = None
|
|
78
63
|
self._invalidation_map: dict[str, set[str]] = {}
|
|
79
64
|
|
|
80
|
-
# Weak reference cache for heavy objects
|
|
81
65
|
self._weak_cache: WeakValueDictionary[str, t.Any] = WeakValueDictionary()
|
|
82
66
|
|
|
83
67
|
async def start(self) -> None:
|
|
84
|
-
"""Start background cleanup task."""
|
|
85
68
|
if self._cleanup_task is None:
|
|
86
69
|
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
|
87
70
|
self._logger.info("Performance cache started")
|
|
88
71
|
|
|
89
72
|
async def stop(self) -> None:
|
|
90
|
-
"""Stop background cleanup task."""
|
|
91
73
|
if self._cleanup_task:
|
|
92
74
|
self._cleanup_task.cancel()
|
|
93
75
|
try:
|
|
@@ -102,7 +84,6 @@ class PerformanceCache:
|
|
|
102
84
|
key: str,
|
|
103
85
|
default: t.Any = None,
|
|
104
86
|
) -> t.Any:
|
|
105
|
-
"""Get value from cache."""
|
|
106
87
|
with self._lock:
|
|
107
88
|
if key not in self._cache:
|
|
108
89
|
self._stats.misses += 1
|
|
@@ -123,7 +104,6 @@ class PerformanceCache:
|
|
|
123
104
|
key: str,
|
|
124
105
|
default: t.Any = None,
|
|
125
106
|
) -> t.Any:
|
|
126
|
-
"""Async version of get for compatibility."""
|
|
127
107
|
return self.get(key, default)
|
|
128
108
|
|
|
129
109
|
def set(
|
|
@@ -133,7 +113,6 @@ class PerformanceCache:
|
|
|
133
113
|
ttl_seconds: int | None = None,
|
|
134
114
|
invalidation_keys: set[str] | None = None,
|
|
135
115
|
) -> None:
|
|
136
|
-
"""Set value in cache."""
|
|
137
116
|
ttl = ttl_seconds if ttl_seconds is not None else self.default_ttl_seconds
|
|
138
117
|
inv_keys = invalidation_keys or set()
|
|
139
118
|
|
|
@@ -147,13 +126,11 @@ class PerformanceCache:
|
|
|
147
126
|
with self._lock:
|
|
148
127
|
self._cache[key] = entry
|
|
149
128
|
|
|
150
|
-
# Update invalidation map
|
|
151
129
|
for inv_key in inv_keys:
|
|
152
130
|
if inv_key not in self._invalidation_map:
|
|
153
131
|
self._invalidation_map[inv_key] = set()
|
|
154
132
|
self._invalidation_map[inv_key].add(key)
|
|
155
133
|
|
|
156
|
-
# Check memory usage and evict if needed
|
|
157
134
|
self._check_memory_pressure()
|
|
158
135
|
|
|
159
136
|
async def set_async(
|
|
@@ -163,11 +140,9 @@ class PerformanceCache:
|
|
|
163
140
|
ttl_seconds: int | None = None,
|
|
164
141
|
invalidation_keys: builtins.set[str] | None = None,
|
|
165
142
|
) -> None:
|
|
166
|
-
|
|
167
|
-
self.set(key, value, ttl_seconds, invalidation_keys)
|
|
143
|
+
self.set[t.Any](key, value, ttl_seconds, invalidation_keys)
|
|
168
144
|
|
|
169
145
|
def invalidate(self, invalidation_key: str) -> int:
|
|
170
|
-
"""Invalidate all entries with given invalidation key."""
|
|
171
146
|
with self._lock:
|
|
172
147
|
if invalidation_key not in self._invalidation_map:
|
|
173
148
|
return 0
|
|
@@ -181,7 +156,6 @@ class PerformanceCache:
|
|
|
181
156
|
count += 1
|
|
182
157
|
self._stats.evictions += 1
|
|
183
158
|
|
|
184
|
-
# Clean up invalidation map
|
|
185
159
|
del self._invalidation_map[invalidation_key]
|
|
186
160
|
|
|
187
161
|
self._logger.debug(
|
|
@@ -190,7 +164,6 @@ class PerformanceCache:
|
|
|
190
164
|
return count
|
|
191
165
|
|
|
192
166
|
def clear(self) -> None:
|
|
193
|
-
"""Clear all cache entries."""
|
|
194
167
|
with self._lock:
|
|
195
168
|
count = len(self._cache)
|
|
196
169
|
self._cache.clear()
|
|
@@ -199,7 +172,6 @@ class PerformanceCache:
|
|
|
199
172
|
self._logger.info(f"Cleared {count} cache entries")
|
|
200
173
|
|
|
201
174
|
def get_stats(self) -> CacheStats:
|
|
202
|
-
"""Get cache performance statistics."""
|
|
203
175
|
with self._lock:
|
|
204
176
|
stats = CacheStats(
|
|
205
177
|
hits=self._stats.hits,
|
|
@@ -210,38 +182,32 @@ class PerformanceCache:
|
|
|
210
182
|
return stats
|
|
211
183
|
|
|
212
184
|
def _estimate_memory_usage(self) -> int:
|
|
213
|
-
"""Estimate memory usage of cache entries."""
|
|
214
|
-
# Simplified estimation - in production would use more sophisticated method
|
|
215
185
|
total_size = 0
|
|
216
186
|
for entry in self._cache.values():
|
|
217
187
|
if isinstance(entry.value, str | bytes):
|
|
218
188
|
total_size += len(entry.value)
|
|
219
189
|
elif isinstance(entry.value, list | tuple):
|
|
220
|
-
total_size += len(entry.value) * 100
|
|
190
|
+
total_size += len(entry.value) * 100
|
|
221
191
|
elif isinstance(entry.value, dict):
|
|
222
|
-
total_size += len(entry.value) * 200
|
|
192
|
+
total_size += len(entry.value) * 200
|
|
223
193
|
else:
|
|
224
|
-
total_size += 1000
|
|
194
|
+
total_size += 1000
|
|
225
195
|
|
|
226
196
|
return total_size
|
|
227
197
|
|
|
228
198
|
def _check_memory_pressure(self) -> None:
|
|
229
|
-
"""Check memory usage and evict entries if needed."""
|
|
230
199
|
if self._estimate_memory_usage() > self.max_memory_bytes:
|
|
231
200
|
self._evict_lru_entries()
|
|
232
201
|
|
|
233
202
|
def _evict_lru_entries(self) -> None:
|
|
234
|
-
"""Evict least recently used entries to free memory."""
|
|
235
203
|
if not self._cache:
|
|
236
204
|
return
|
|
237
205
|
|
|
238
|
-
# Sort entries by last access time (oldest first)
|
|
239
206
|
entries_by_access = sorted(
|
|
240
207
|
self._cache.items(),
|
|
241
208
|
key=lambda x: x[1].last_accessed,
|
|
242
209
|
)
|
|
243
210
|
|
|
244
|
-
# Remove oldest 25% of entries
|
|
245
211
|
evict_count = max(1, len(entries_by_access) // 4)
|
|
246
212
|
|
|
247
213
|
for key, _ in entries_by_access[:evict_count]:
|
|
@@ -251,7 +217,6 @@ class PerformanceCache:
|
|
|
251
217
|
self._logger.debug(f"Evicted {evict_count} LRU cache entries")
|
|
252
218
|
|
|
253
219
|
async def _cleanup_loop(self) -> None:
|
|
254
|
-
"""Background cleanup loop for expired entries."""
|
|
255
220
|
while True:
|
|
256
221
|
try:
|
|
257
222
|
await asyncio.sleep(self.cleanup_interval_seconds)
|
|
@@ -262,7 +227,6 @@ class PerformanceCache:
|
|
|
262
227
|
self._logger.error(f"Error in cache cleanup loop: {e}")
|
|
263
228
|
|
|
264
229
|
def _cleanup_expired_entries(self) -> None:
|
|
265
|
-
"""Clean up expired cache entries."""
|
|
266
230
|
with self._lock:
|
|
267
231
|
expired_keys = [
|
|
268
232
|
key for key, entry in self._cache.items() if entry.is_expired()
|
|
@@ -277,14 +241,11 @@ class PerformanceCache:
|
|
|
277
241
|
|
|
278
242
|
|
|
279
243
|
class GitOperationCache:
|
|
280
|
-
"""Specialized cache for Git operations."""
|
|
281
|
-
|
|
282
244
|
def __init__(self, cache: PerformanceCache):
|
|
283
245
|
self.cache = cache
|
|
284
246
|
self._logger = get_logger("crackerjack.git_cache")
|
|
285
247
|
|
|
286
248
|
def _make_repo_key(self, repo_path: Path, operation: str, params: str = "") -> str:
|
|
287
|
-
"""Create cache key for git operation."""
|
|
288
249
|
repo_hash = hashlib.md5(
|
|
289
250
|
str(repo_path).encode(), usedforsecurity=False
|
|
290
251
|
).hexdigest()[:8]
|
|
@@ -293,10 +254,9 @@ class GitOperationCache:
|
|
|
293
254
|
if params
|
|
294
255
|
else ""
|
|
295
256
|
)
|
|
296
|
-
return f"git:{repo_hash}:{operation}:{param_hash}"
|
|
257
|
+
return f"git: {repo_hash}: {operation}: {param_hash}"
|
|
297
258
|
|
|
298
259
|
def get_branch_info(self, repo_path: Path) -> t.Any:
|
|
299
|
-
"""Get cached branch information."""
|
|
300
260
|
key = self._make_repo_key(repo_path, "branch_info")
|
|
301
261
|
return self.cache.get(key)
|
|
302
262
|
|
|
@@ -306,13 +266,11 @@ class GitOperationCache:
|
|
|
306
266
|
branch_info: t.Any,
|
|
307
267
|
ttl_seconds: int = 60,
|
|
308
268
|
) -> None:
|
|
309
|
-
"""Cache branch information."""
|
|
310
269
|
key = self._make_repo_key(repo_path, "branch_info")
|
|
311
|
-
invalidation_keys = {f"git_repo:{repo_path}"}
|
|
312
|
-
self.cache.set(key, branch_info, ttl_seconds, invalidation_keys)
|
|
270
|
+
invalidation_keys = {f"git_repo: {repo_path}"}
|
|
271
|
+
self.cache.set[t.Any](key, branch_info, ttl_seconds, invalidation_keys)
|
|
313
272
|
|
|
314
273
|
def get_file_status(self, repo_path: Path) -> t.Any:
|
|
315
|
-
"""Get cached file status."""
|
|
316
274
|
key = self._make_repo_key(repo_path, "file_status")
|
|
317
275
|
return self.cache.get(key)
|
|
318
276
|
|
|
@@ -322,33 +280,27 @@ class GitOperationCache:
|
|
|
322
280
|
file_status: t.Any,
|
|
323
281
|
ttl_seconds: int = 30,
|
|
324
282
|
) -> None:
|
|
325
|
-
"""Cache file status."""
|
|
326
283
|
key = self._make_repo_key(repo_path, "file_status")
|
|
327
|
-
invalidation_keys = {f"git_repo:{repo_path}", "git_files"}
|
|
328
|
-
self.cache.set(key, file_status, ttl_seconds, invalidation_keys)
|
|
284
|
+
invalidation_keys = {f"git_repo: {repo_path}", "git_files"}
|
|
285
|
+
self.cache.set[t.Any](key, file_status, ttl_seconds, invalidation_keys)
|
|
329
286
|
|
|
330
287
|
def invalidate_repo(self, repo_path: Path) -> None:
|
|
331
|
-
|
|
332
|
-
self.cache.invalidate(f"git_repo:{repo_path}")
|
|
288
|
+
self.cache.invalidate(f"git_repo: {repo_path}")
|
|
333
289
|
self._logger.debug(f"Invalidated git cache for repo: {repo_path}")
|
|
334
290
|
|
|
335
291
|
|
|
336
292
|
class FileSystemCache:
|
|
337
|
-
"""Specialized cache for file system operations."""
|
|
338
|
-
|
|
339
293
|
def __init__(self, cache: PerformanceCache):
|
|
340
294
|
self.cache = cache
|
|
341
295
|
self._logger = get_logger("crackerjack.filesystem_cache")
|
|
342
296
|
|
|
343
297
|
def _make_file_key(self, file_path: Path, operation: str) -> str:
|
|
344
|
-
"""Create cache key for file operation."""
|
|
345
298
|
file_hash = hashlib.md5(
|
|
346
299
|
str(file_path).encode(), usedforsecurity=False
|
|
347
300
|
).hexdigest()[:8]
|
|
348
|
-
return f"fs:{file_hash}:{operation}"
|
|
301
|
+
return f"fs: {file_hash}: {operation}"
|
|
349
302
|
|
|
350
303
|
def get_file_stats(self, file_path: Path) -> t.Any:
|
|
351
|
-
"""Get cached file statistics."""
|
|
352
304
|
key = self._make_file_key(file_path, "stats")
|
|
353
305
|
return self.cache.get(key)
|
|
354
306
|
|
|
@@ -358,39 +310,33 @@ class FileSystemCache:
|
|
|
358
310
|
stats: t.Any,
|
|
359
311
|
ttl_seconds: int = 60,
|
|
360
312
|
) -> None:
|
|
361
|
-
"""Cache file statistics."""
|
|
362
313
|
key = self._make_file_key(file_path, "stats")
|
|
363
|
-
invalidation_keys = {f"file:{file_path}"}
|
|
364
|
-
self.cache.set(key, stats, ttl_seconds, invalidation_keys)
|
|
314
|
+
invalidation_keys = {f"file: {file_path}"}
|
|
315
|
+
self.cache.set[t.Any](key, stats, ttl_seconds, invalidation_keys)
|
|
365
316
|
|
|
366
317
|
def invalidate_file(self, file_path: Path) -> None:
|
|
367
|
-
|
|
368
|
-
self.cache.invalidate(f"file:{file_path}")
|
|
318
|
+
self.cache.invalidate(f"file: {file_path}")
|
|
369
319
|
|
|
370
320
|
|
|
371
321
|
class CommandResultCache:
|
|
372
|
-
"""Specialized cache for command execution results."""
|
|
373
|
-
|
|
374
322
|
def __init__(self, cache: PerformanceCache):
|
|
375
323
|
self.cache = cache
|
|
376
324
|
self._logger = get_logger("crackerjack.command_cache")
|
|
377
325
|
|
|
378
326
|
def _make_command_key(self, command: list[str], cwd: Path | None = None) -> str:
|
|
379
|
-
"""Create cache key for command execution."""
|
|
380
327
|
cmd_str = " ".join(command)
|
|
381
328
|
cwd_str = str(cwd) if cwd else ""
|
|
382
|
-
combined = f"{cmd_str}:{cwd_str}"
|
|
329
|
+
combined = f"{cmd_str}: {cwd_str}"
|
|
383
330
|
cmd_hash = hashlib.md5(combined.encode(), usedforsecurity=False).hexdigest()[
|
|
384
331
|
:12
|
|
385
332
|
]
|
|
386
|
-
return f"cmd:{cmd_hash}"
|
|
333
|
+
return f"cmd: {cmd_hash}"
|
|
387
334
|
|
|
388
335
|
def get_command_result(
|
|
389
336
|
self,
|
|
390
337
|
command: list[str],
|
|
391
338
|
cwd: Path | None = None,
|
|
392
339
|
) -> t.Any:
|
|
393
|
-
"""Get cached command result."""
|
|
394
340
|
key = self._make_command_key(command, cwd)
|
|
395
341
|
return self.cache.get(key)
|
|
396
342
|
|
|
@@ -401,26 +347,22 @@ class CommandResultCache:
|
|
|
401
347
|
cwd: Path | None = None,
|
|
402
348
|
ttl_seconds: int = 120,
|
|
403
349
|
) -> None:
|
|
404
|
-
"""Cache command execution result."""
|
|
405
350
|
key = self._make_command_key(command, cwd)
|
|
406
351
|
invalidation_keys = {"commands"}
|
|
407
352
|
if cwd:
|
|
408
|
-
invalidation_keys.add(f"cwd:{cwd}")
|
|
353
|
+
invalidation_keys.add(f"cwd: {cwd}")
|
|
409
354
|
|
|
410
|
-
self.cache.set(key, result, ttl_seconds, invalidation_keys)
|
|
355
|
+
self.cache.set[t.Any](key, result, ttl_seconds, invalidation_keys)
|
|
411
356
|
|
|
412
357
|
def invalidate_commands(self) -> None:
|
|
413
|
-
"""Invalidate all cached command results."""
|
|
414
358
|
self.cache.invalidate("commands")
|
|
415
359
|
|
|
416
360
|
|
|
417
|
-
# Global cache instance for the application
|
|
418
361
|
_global_cache: PerformanceCache | None = None
|
|
419
362
|
_cache_lock = Lock()
|
|
420
363
|
|
|
421
364
|
|
|
422
365
|
def get_performance_cache() -> PerformanceCache:
|
|
423
|
-
"""Get global performance cache instance."""
|
|
424
366
|
global _global_cache
|
|
425
367
|
with _cache_lock:
|
|
426
368
|
if _global_cache is None:
|
|
@@ -429,15 +371,12 @@ def get_performance_cache() -> PerformanceCache:
|
|
|
429
371
|
|
|
430
372
|
|
|
431
373
|
def get_git_cache() -> GitOperationCache:
|
|
432
|
-
"""Get Git operation cache instance."""
|
|
433
374
|
return GitOperationCache(get_performance_cache())
|
|
434
375
|
|
|
435
376
|
|
|
436
377
|
def get_filesystem_cache() -> FileSystemCache:
|
|
437
|
-
"""Get file system cache instance."""
|
|
438
378
|
return FileSystemCache(get_performance_cache())
|
|
439
379
|
|
|
440
380
|
|
|
441
381
|
def get_command_cache() -> CommandResultCache:
|
|
442
|
-
"""Get command result cache instance."""
|
|
443
382
|
return CommandResultCache(get_performance_cache())
|