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
|
@@ -63,7 +63,6 @@ class TestSpecialistAgent(SubAgent):
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
def _check_test_patterns(self, message: str) -> float:
|
|
66
|
-
# Map pattern strings back to SAFE_PATTERNS for safe usage
|
|
67
66
|
pattern_map = {
|
|
68
67
|
SAFE_PATTERNS[
|
|
69
68
|
"fixture_not_found_pattern"
|
|
@@ -191,7 +190,6 @@ class TestSpecialistAgent(SubAgent):
|
|
|
191
190
|
def _identify_failure_type(self, issue: Issue) -> str:
|
|
192
191
|
message = issue.message
|
|
193
192
|
|
|
194
|
-
# Map pattern strings back to SAFE_PATTERNS for safe usage
|
|
195
193
|
pattern_map = {
|
|
196
194
|
SAFE_PATTERNS[
|
|
197
195
|
"fixture_not_found_pattern"
|
|
@@ -218,12 +216,10 @@ class TestSpecialistAgent(SubAgent):
|
|
|
218
216
|
async def _fix_missing_fixtures(self, issue: Issue) -> list[str]:
|
|
219
217
|
fixes: list[str] = []
|
|
220
218
|
|
|
221
|
-
# Use safe pattern to test and extract fixture name
|
|
222
219
|
fixture_pattern = SAFE_PATTERNS["fixture_not_found_pattern"]
|
|
223
220
|
if not fixture_pattern.test(issue.message):
|
|
224
221
|
return fixes
|
|
225
222
|
|
|
226
|
-
# Extract fixture name using the safe pattern's search method
|
|
227
223
|
match = fixture_pattern.search(issue.message)
|
|
228
224
|
if not match:
|
|
229
225
|
return fixes
|
crackerjack/api.py
CHANGED
|
@@ -131,17 +131,6 @@ class CrackerjackAPI:
|
|
|
131
131
|
backup: bool = True,
|
|
132
132
|
safe_mode: bool = True,
|
|
133
133
|
) -> list[CleaningResult] | PackageCleaningResult:
|
|
134
|
-
"""Clean code with comprehensive backup protection.
|
|
135
|
-
|
|
136
|
-
Args:
|
|
137
|
-
target_dir: Directory to clean (defaults to package root)
|
|
138
|
-
backup: Whether to create backup (deprecated, always True for safety)
|
|
139
|
-
safe_mode: Use comprehensive backup system (default: True, recommended)
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
PackageCleaningResult with backup metadata if safe_mode=True,
|
|
143
|
-
otherwise list[CleaningResult] for legacy compatibility
|
|
144
|
-
"""
|
|
145
134
|
target_dir = target_dir or self._get_package_root()
|
|
146
135
|
self.logger.info(f"Cleaning code in {target_dir} (safe_mode={safe_mode})")
|
|
147
136
|
|
|
@@ -153,7 +142,6 @@ class CrackerjackAPI:
|
|
|
153
142
|
)
|
|
154
143
|
return self._execute_safe_code_cleaning(target_dir)
|
|
155
144
|
else:
|
|
156
|
-
# Note: Legacy mode still uses backup protection for safety
|
|
157
145
|
self.console.print(
|
|
158
146
|
"[yellow]ā ļø Legacy mode - backup protection still enabled for safety[/yellow]"
|
|
159
147
|
)
|
|
@@ -210,16 +198,13 @@ class CrackerjackAPI:
|
|
|
210
198
|
|
|
211
199
|
def _execute_code_cleaning(self, target_dir: Path) -> list[CleaningResult]:
|
|
212
200
|
try:
|
|
213
|
-
# Use backup protection by default for safety
|
|
214
201
|
results = self.code_cleaner.clean_files(target_dir, use_backup=True)
|
|
215
202
|
|
|
216
|
-
# Handle both return types (legacy compatibility)
|
|
217
203
|
if isinstance(results, list):
|
|
218
204
|
self._report_cleaning_results(results)
|
|
219
205
|
else:
|
|
220
|
-
# PackageCleaningResult from backup mode
|
|
221
206
|
self._report_safe_cleaning_results(results)
|
|
222
|
-
results = results.file_results
|
|
207
|
+
results = results.file_results
|
|
223
208
|
|
|
224
209
|
return results
|
|
225
210
|
except Exception as e:
|
|
@@ -415,7 +400,7 @@ class CrackerjackAPI:
|
|
|
415
400
|
git_dir = self.project_path / ".git"
|
|
416
401
|
is_git_repo = git_dir.exists()
|
|
417
402
|
|
|
418
|
-
python_files = list(self.project_path.rglob("*.py"))
|
|
403
|
+
python_files = list[t.Any](self.project_path.rglob("*.py"))
|
|
419
404
|
|
|
420
405
|
return {
|
|
421
406
|
"project_path": str(self.project_path),
|
|
@@ -494,13 +479,13 @@ class CrackerjackAPI:
|
|
|
494
479
|
data = tomllib.load(f)
|
|
495
480
|
|
|
496
481
|
if "project" in data and "version" in data["project"]:
|
|
497
|
-
return data["project"]["version"]
|
|
482
|
+
return str(data["project"]["version"])
|
|
498
483
|
if (
|
|
499
484
|
"tool" in data
|
|
500
485
|
and "poetry" in data["tool"]
|
|
501
486
|
and "version" in data["tool"]["poetry"]
|
|
502
487
|
):
|
|
503
|
-
return data["tool"]["poetry"]["version"]
|
|
488
|
+
return str(data["tool"]["poetry"]["version"])
|
|
504
489
|
|
|
505
490
|
import importlib.metadata
|
|
506
491
|
|
|
@@ -514,7 +499,6 @@ class CrackerjackAPI:
|
|
|
514
499
|
return "unknown"
|
|
515
500
|
|
|
516
501
|
def _check_for_todos(self, target_dir: Path) -> list[tuple[Path, int, str]]:
|
|
517
|
-
# Use SAFE_PATTERNS for TODO detection
|
|
518
502
|
task_pattern = SAFE_PATTERNS["todo_pattern"]
|
|
519
503
|
python_files = self._get_python_files_for_todo_check(target_dir)
|
|
520
504
|
return self._scan_files_for_todos(python_files, task_pattern)
|
|
@@ -574,12 +558,9 @@ class CrackerjackAPI:
|
|
|
574
558
|
with suppress(UnicodeDecodeError, PermissionError):
|
|
575
559
|
with file_path.open() as f:
|
|
576
560
|
for line_no, line in enumerate(f, 1):
|
|
577
|
-
# For ValidatedPattern, check if applying it changes the line
|
|
578
|
-
# If it doesn't change, then it didn't match (identity replacement for match-only patterns)
|
|
579
561
|
original = line.strip()
|
|
580
562
|
processed = todo_pattern.apply(original)
|
|
581
|
-
|
|
582
|
-
# But we need to check if it actually contains TODO
|
|
563
|
+
|
|
583
564
|
if "todo" in original.lower() and original == processed:
|
|
584
565
|
todos.append((file_path, line_no, line))
|
|
585
566
|
|
|
@@ -609,7 +590,7 @@ class CrackerjackAPI:
|
|
|
609
590
|
data = tomllib.load(f)
|
|
610
591
|
|
|
611
592
|
if "project" in data and "name" in data["project"]:
|
|
612
|
-
return data["project"]["name"]
|
|
593
|
+
return str(data["project"]["name"])
|
|
613
594
|
|
|
614
595
|
return None
|
|
615
596
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
|
|
8
|
+
from crackerjack.services.cache import CrackerjackCache
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def handle_clear_cache(console: Console) -> None:
|
|
12
|
+
"""Clear all caches and display results."""
|
|
13
|
+
try:
|
|
14
|
+
cache = CrackerjackCache()
|
|
15
|
+
|
|
16
|
+
# Clear memory caches and get cleanup stats
|
|
17
|
+
cleanup_results = cache.cleanup_all()
|
|
18
|
+
|
|
19
|
+
# Clear disk cache completely
|
|
20
|
+
if cache.enable_disk_cache and cache.cache_dir:
|
|
21
|
+
cache.disk_cache.clear()
|
|
22
|
+
|
|
23
|
+
# Calculate total items cleared
|
|
24
|
+
total_cleared = sum(cleanup_results.values())
|
|
25
|
+
|
|
26
|
+
# Create results table
|
|
27
|
+
table = Table(
|
|
28
|
+
title="Cache Cleared", show_header=True, header_style="bold green"
|
|
29
|
+
)
|
|
30
|
+
table.add_column("Cache Type", style="cyan", no_wrap=True)
|
|
31
|
+
table.add_column("Items Cleared", justify="right", style="yellow")
|
|
32
|
+
|
|
33
|
+
for cache_type, count in cleanup_results.items():
|
|
34
|
+
table.add_row(cache_type.replace("_", " ").title(), str(count))
|
|
35
|
+
|
|
36
|
+
table.add_row("", "", end_section=True)
|
|
37
|
+
table.add_row("Total", str(total_cleared), style="bold green")
|
|
38
|
+
|
|
39
|
+
console.print()
|
|
40
|
+
console.print(table)
|
|
41
|
+
console.print(f"\nā
Successfully cleared {total_cleared} cache entries")
|
|
42
|
+
|
|
43
|
+
except Exception as e:
|
|
44
|
+
console.print(f"\nā Error clearing cache: {e}", style="bold red")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def handle_cache_stats(console: Console) -> None:
|
|
48
|
+
"""Display detailed cache statistics."""
|
|
49
|
+
try:
|
|
50
|
+
cache = CrackerjackCache()
|
|
51
|
+
stats = cache.get_cache_stats()
|
|
52
|
+
|
|
53
|
+
main_table = _create_cache_stats_table()
|
|
54
|
+
totals = _populate_cache_stats_table(main_table, stats)
|
|
55
|
+
_add_cache_totals_row(main_table, totals)
|
|
56
|
+
|
|
57
|
+
console.print()
|
|
58
|
+
console.print(main_table)
|
|
59
|
+
|
|
60
|
+
_display_performance_insights(console, totals)
|
|
61
|
+
_display_cache_directory_info(console, cache)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
console.print(f"\nā Error retrieving cache stats: {e}", style="bold red")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _create_cache_stats_table() -> Table:
|
|
68
|
+
"""Create and configure the main cache statistics table."""
|
|
69
|
+
table = Table(title="Cache Statistics", show_header=True, header_style="bold blue")
|
|
70
|
+
table.add_column("Cache Layer", style="cyan", no_wrap=True)
|
|
71
|
+
table.add_column("Hit Rate %", justify="right", style="green")
|
|
72
|
+
table.add_column("Hits", justify="right", style="yellow")
|
|
73
|
+
table.add_column("Misses", justify="right", style="red")
|
|
74
|
+
table.add_column("Entries", justify="right", style="magenta")
|
|
75
|
+
table.add_column("Size (MB)", justify="right", style="blue")
|
|
76
|
+
return table
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _populate_cache_stats_table(
|
|
80
|
+
table: Table, stats: dict[str, t.Any]
|
|
81
|
+
) -> dict[str, t.Any]:
|
|
82
|
+
"""Populate table with cache statistics and return totals."""
|
|
83
|
+
totals = {"hits": 0, "misses": 0, "entries": 0, "size": 0.0}
|
|
84
|
+
|
|
85
|
+
for cache_name, cache_stats in stats.items():
|
|
86
|
+
hit_rate = cache_stats.get("hit_rate_percent", 0.0)
|
|
87
|
+
hits = cache_stats.get("hits", 0)
|
|
88
|
+
misses = cache_stats.get("misses", 0)
|
|
89
|
+
entries = cache_stats.get("total_entries", 0)
|
|
90
|
+
size_mb = cache_stats.get("total_size_mb", 0.0)
|
|
91
|
+
|
|
92
|
+
hit_rate_style = _get_hit_rate_style(hit_rate)
|
|
93
|
+
|
|
94
|
+
table.add_row(
|
|
95
|
+
cache_name.replace("_", " ").title(),
|
|
96
|
+
Text(f"{hit_rate:.1f}", style=hit_rate_style),
|
|
97
|
+
str(hits),
|
|
98
|
+
str(misses),
|
|
99
|
+
str(entries),
|
|
100
|
+
f"{size_mb:.2f}",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
totals["hits"] += hits
|
|
104
|
+
totals["misses"] += misses
|
|
105
|
+
totals["entries"] += entries
|
|
106
|
+
totals["size"] += size_mb
|
|
107
|
+
|
|
108
|
+
return totals
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _get_hit_rate_style(hit_rate: float) -> str:
|
|
112
|
+
"""Get color style for hit rate based on performance."""
|
|
113
|
+
return "green" if hit_rate > 70 else "yellow" if hit_rate > 40 else "red"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _add_cache_totals_row(table: Table, totals: dict[str, t.Any]) -> None:
|
|
117
|
+
"""Add totals row to cache statistics table."""
|
|
118
|
+
overall_hit_rate = (
|
|
119
|
+
(totals["hits"] / (totals["hits"] + totals["misses"]) * 100)
|
|
120
|
+
if (totals["hits"] + totals["misses"]) > 0
|
|
121
|
+
else 0
|
|
122
|
+
)
|
|
123
|
+
overall_style = _get_hit_rate_style(overall_hit_rate)
|
|
124
|
+
|
|
125
|
+
table.add_row("", "", "", "", "", "", end_section=True)
|
|
126
|
+
table.add_row(
|
|
127
|
+
"Overall",
|
|
128
|
+
Text(f"{overall_hit_rate:.1f}", style=f"bold {overall_style}"),
|
|
129
|
+
str(totals["hits"]),
|
|
130
|
+
str(totals["misses"]),
|
|
131
|
+
str(totals["entries"]),
|
|
132
|
+
f"{totals['size']:.2f}",
|
|
133
|
+
style="bold",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _display_performance_insights(console: Console, totals: dict[str, t.Any]) -> None:
|
|
138
|
+
"""Display performance insights panel based on cache statistics."""
|
|
139
|
+
overall_hit_rate = (
|
|
140
|
+
(totals["hits"] / (totals["hits"] + totals["misses"]) * 100)
|
|
141
|
+
if (totals["hits"] + totals["misses"]) > 0
|
|
142
|
+
else 0
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
insights = _generate_performance_insights(overall_hit_rate, totals["size"])
|
|
146
|
+
|
|
147
|
+
if insights:
|
|
148
|
+
insights_text = "\n".join(insights)
|
|
149
|
+
insights_panel = Panel(
|
|
150
|
+
insights_text,
|
|
151
|
+
title="Performance Insights",
|
|
152
|
+
border_style="blue",
|
|
153
|
+
padding=(1, 2),
|
|
154
|
+
)
|
|
155
|
+
console.print()
|
|
156
|
+
console.print(insights_panel)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _generate_performance_insights(hit_rate: float, total_size: float) -> list[str]:
|
|
160
|
+
"""Generate performance insights based on cache metrics."""
|
|
161
|
+
insights = []
|
|
162
|
+
|
|
163
|
+
if hit_rate > 80:
|
|
164
|
+
insights.append("š Excellent cache performance!")
|
|
165
|
+
elif hit_rate > 60:
|
|
166
|
+
insights.append("ā
Good cache performance")
|
|
167
|
+
elif hit_rate > 30:
|
|
168
|
+
insights.append("ā ļø Moderate cache performance - consider cache warming")
|
|
169
|
+
else:
|
|
170
|
+
insights.append("ā Poor cache performance - check cache configuration")
|
|
171
|
+
|
|
172
|
+
if total_size > 100:
|
|
173
|
+
insights.append(f"š¾ Large cache size ({total_size:.1f}MB) - consider cleanup")
|
|
174
|
+
|
|
175
|
+
return insights
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _display_cache_directory_info(console: Console, cache: CrackerjackCache) -> None:
|
|
179
|
+
"""Display cache directory information."""
|
|
180
|
+
if not (cache.enable_disk_cache and cache.cache_dir):
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
cache_dir_info = f"š Cache Directory: {cache.cache_dir}"
|
|
184
|
+
if cache.cache_dir.exists():
|
|
185
|
+
disk_files = len(list[t.Any](cache.cache_dir.rglob("*.cache")))
|
|
186
|
+
cache_dir_info += f" ({disk_files} files)"
|
|
187
|
+
|
|
188
|
+
console.print()
|
|
189
|
+
console.print(cache_dir_info)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _handle_cache_commands(
|
|
193
|
+
clear_cache: bool, cache_stats: bool, console: Console
|
|
194
|
+
) -> bool:
|
|
195
|
+
"""Handle cache management commands. Returns True if a cache command was executed."""
|
|
196
|
+
if clear_cache:
|
|
197
|
+
handle_clear_cache(console)
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
if cache_stats:
|
|
201
|
+
handle_cache_stats(console)
|
|
202
|
+
return True
|
|
203
|
+
|
|
204
|
+
return False
|