crackerjack 0.37.9__py3-none-any.whl → 0.45.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +6 -8
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +1247 -953
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +21 -8
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,9 +3,10 @@ from collections.abc import Iterator
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from crackerjack.errors import ErrorCode, FileError, ResourceError
|
|
6
|
+
from crackerjack.models.protocols import FileSystemInterface
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class FileSystemService:
|
|
9
|
+
class FileSystemService(FileSystemInterface):
|
|
9
10
|
@staticmethod
|
|
10
11
|
def clean_trailing_whitespace_and_newlines(content: str) -> str:
|
|
11
12
|
lines = content.splitlines()
|
|
@@ -17,37 +18,79 @@ class FileSystemService:
|
|
|
17
18
|
|
|
18
19
|
return result
|
|
19
20
|
|
|
21
|
+
def _validate_path_exists(self, path_obj: Path, operation: str) -> None:
|
|
22
|
+
"""Validate that a path exists."""
|
|
23
|
+
if not path_obj.exists():
|
|
24
|
+
raise FileError(
|
|
25
|
+
message=f"File does not exist: {path_obj}",
|
|
26
|
+
details=f"Attempted to {operation} file at {path_obj.absolute()}",
|
|
27
|
+
recovery="Check file path and ensure file exists",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def _handle_permission_error(
|
|
31
|
+
self, error: PermissionError, path: str | Path, operation: str
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Handle permission errors."""
|
|
34
|
+
raise FileError(
|
|
35
|
+
message=f"Permission denied {operation}: {path}",
|
|
36
|
+
error_code=ErrorCode.PERMISSION_ERROR,
|
|
37
|
+
details=str(error),
|
|
38
|
+
recovery="Check file permissions and user access rights",
|
|
39
|
+
) from error
|
|
40
|
+
|
|
41
|
+
def _handle_unicode_error(
|
|
42
|
+
self, error: UnicodeDecodeError, path: str | Path
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Handle unicode decode errors."""
|
|
45
|
+
raise FileError(
|
|
46
|
+
message=f"Unable to decode file as UTF-8: {path}",
|
|
47
|
+
error_code=ErrorCode.FILE_READ_ERROR,
|
|
48
|
+
details=str(error),
|
|
49
|
+
recovery="Ensure file is text - based and UTF-8 encoded",
|
|
50
|
+
) from error
|
|
51
|
+
|
|
52
|
+
def _handle_os_error(
|
|
53
|
+
self, error: OSError, path: str | Path, operation: str
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Handle OS errors."""
|
|
56
|
+
raise FileError(
|
|
57
|
+
message=f"System error {operation}: {path}",
|
|
58
|
+
error_code=ErrorCode.FILE_READ_ERROR,
|
|
59
|
+
details=str(error),
|
|
60
|
+
recovery="Check disk space and file system integrity",
|
|
61
|
+
) from error
|
|
62
|
+
|
|
63
|
+
def _handle_disk_space_error(
|
|
64
|
+
self, error: OSError, path: str | Path, operation: str
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Handle disk space errors."""
|
|
67
|
+
if "No space left on device" in str(error):
|
|
68
|
+
raise ResourceError(
|
|
69
|
+
message=f"Insufficient disk space to {operation}: {path}",
|
|
70
|
+
details=str(error),
|
|
71
|
+
recovery="Free up disk space and try again",
|
|
72
|
+
) from error
|
|
73
|
+
raise FileError(
|
|
74
|
+
message=f"System error {operation}: {path}",
|
|
75
|
+
error_code=ErrorCode.FILE_WRITE_ERROR,
|
|
76
|
+
details=str(error),
|
|
77
|
+
recovery="Check disk space and file system integrity",
|
|
78
|
+
) from error
|
|
79
|
+
|
|
20
80
|
def read_file(self, path: str | Path) -> str:
|
|
21
81
|
try:
|
|
22
82
|
path_obj = Path(path) if isinstance(path, str) else path
|
|
23
|
-
|
|
24
|
-
raise FileError(
|
|
25
|
-
message=f"File does not exist: {path_obj}",
|
|
26
|
-
details=f"Attempted to read file at {path_obj.absolute()}",
|
|
27
|
-
recovery="Check file path and ensure file exists",
|
|
28
|
-
)
|
|
83
|
+
self._validate_path_exists(path_obj, "read")
|
|
29
84
|
return path_obj.read_text(encoding="utf-8")
|
|
30
85
|
except PermissionError as e:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
error_code=ErrorCode.PERMISSION_ERROR,
|
|
34
|
-
details=str(e),
|
|
35
|
-
recovery="Check file permissions and user access rights",
|
|
36
|
-
) from e
|
|
86
|
+
self._handle_permission_error(e, path, "reading file")
|
|
87
|
+
raise # Ensure type checker knows this doesn't return
|
|
37
88
|
except UnicodeDecodeError as e:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
41
|
-
details=str(e),
|
|
42
|
-
recovery="Ensure file is text - based and UTF-8 encoded",
|
|
43
|
-
) from e
|
|
89
|
+
self._handle_unicode_error(e, path)
|
|
90
|
+
raise # Ensure type checker knows this doesn't return
|
|
44
91
|
except OSError as e:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
48
|
-
details=str(e),
|
|
49
|
-
recovery="Check disk space and file system integrity",
|
|
50
|
-
) from e
|
|
92
|
+
self._handle_os_error(e, path, "reading file")
|
|
93
|
+
raise # Ensure type checker knows this doesn't return
|
|
51
94
|
|
|
52
95
|
def write_file(self, path: str | Path, content: str) -> None:
|
|
53
96
|
try:
|
|
@@ -99,12 +142,7 @@ class FileSystemService:
|
|
|
99
142
|
path_obj = Path(path) if isinstance(path, str) else path
|
|
100
143
|
path_obj.mkdir(parents=parents, exist_ok=True)
|
|
101
144
|
except PermissionError as e:
|
|
102
|
-
|
|
103
|
-
message=f"Permission denied creating directory: {path}",
|
|
104
|
-
error_code=ErrorCode.PERMISSION_ERROR,
|
|
105
|
-
details=str(e),
|
|
106
|
-
recovery="Check parent directory permissions",
|
|
107
|
-
) from e
|
|
145
|
+
self._handle_permission_error(e, path, "creating directory")
|
|
108
146
|
except FileExistsError as e:
|
|
109
147
|
if not parents:
|
|
110
148
|
raise FileError(
|
|
@@ -113,18 +151,7 @@ class FileSystemService:
|
|
|
113
151
|
recovery="Use exist_ok=True or check if directory exists first",
|
|
114
152
|
) from e
|
|
115
153
|
except OSError as e:
|
|
116
|
-
|
|
117
|
-
raise ResourceError(
|
|
118
|
-
message=f"Insufficient disk space to create directory: {path}",
|
|
119
|
-
details=str(e),
|
|
120
|
-
recovery="Free up disk space and try again",
|
|
121
|
-
) from e
|
|
122
|
-
raise FileError(
|
|
123
|
-
message=f"System error creating directory: {path}",
|
|
124
|
-
error_code=ErrorCode.FILE_WRITE_ERROR,
|
|
125
|
-
details=str(e),
|
|
126
|
-
recovery="Check disk space and file system integrity",
|
|
127
|
-
) from e
|
|
154
|
+
self._handle_disk_space_error(e, path, "create directory")
|
|
128
155
|
|
|
129
156
|
def glob(self, pattern: str, path: str | Path | None = None) -> list[Path]:
|
|
130
157
|
base_path = Path(path) if path else Path.cwd()
|
|
@@ -220,12 +247,7 @@ class FileSystemService:
|
|
|
220
247
|
try:
|
|
221
248
|
shutil.copy2(src_path, dst_path)
|
|
222
249
|
except PermissionError as e:
|
|
223
|
-
|
|
224
|
-
message=f"Permission denied copying file: {src} -> {dst}",
|
|
225
|
-
error_code=ErrorCode.PERMISSION_ERROR,
|
|
226
|
-
details=str(e),
|
|
227
|
-
recovery="Check file and directory permissions",
|
|
228
|
-
) from e
|
|
250
|
+
self._handle_permission_error(e, f"{src} -> {dst}", "copying file")
|
|
229
251
|
except shutil.SameFileError as e:
|
|
230
252
|
raise FileError(
|
|
231
253
|
message=f"Source and destination are the same file: {src}",
|
|
@@ -234,18 +256,7 @@ class FileSystemService:
|
|
|
234
256
|
recovery="Ensure source and destination paths are different",
|
|
235
257
|
) from e
|
|
236
258
|
except OSError as e:
|
|
237
|
-
|
|
238
|
-
raise ResourceError(
|
|
239
|
-
message=f"Insufficient disk space to copy file: {src} -> {dst}",
|
|
240
|
-
details=str(e),
|
|
241
|
-
recovery="Free up disk space and try again",
|
|
242
|
-
) from e
|
|
243
|
-
raise FileError(
|
|
244
|
-
message=f"System error copying file: {src} -> {dst}",
|
|
245
|
-
error_code=ErrorCode.FILE_WRITE_ERROR,
|
|
246
|
-
details=str(e),
|
|
247
|
-
recovery="Check disk space and file system integrity",
|
|
248
|
-
) from e
|
|
259
|
+
self._handle_disk_space_error(e, f"{src} -> {dst}", "copy file")
|
|
249
260
|
|
|
250
261
|
def remove_file(self, path: str | Path) -> None:
|
|
251
262
|
try:
|
|
@@ -342,69 +353,29 @@ class FileSystemService:
|
|
|
342
353
|
) -> Iterator[str]:
|
|
343
354
|
try:
|
|
344
355
|
path_obj = Path(path) if isinstance(path, str) else path
|
|
345
|
-
|
|
346
|
-
raise FileError(
|
|
347
|
-
message=f"File does not exist: {path_obj}",
|
|
348
|
-
details=f"Attempted to read file at {path_obj.absolute()}",
|
|
349
|
-
recovery="Check file path and ensure file exists",
|
|
350
|
-
)
|
|
356
|
+
self._validate_path_exists(path_obj, "read")
|
|
351
357
|
|
|
352
358
|
with path_obj.open(encoding="utf-8") as file:
|
|
353
359
|
while chunk := file.read(chunk_size):
|
|
354
360
|
yield chunk
|
|
355
361
|
|
|
356
362
|
except PermissionError as e:
|
|
357
|
-
|
|
358
|
-
message=f"Permission denied reading file: {path}",
|
|
359
|
-
error_code=ErrorCode.PERMISSION_ERROR,
|
|
360
|
-
details=str(e),
|
|
361
|
-
recovery="Check file permissions",
|
|
362
|
-
) from e
|
|
363
|
+
self._handle_permission_error(e, path, "reading file")
|
|
363
364
|
except UnicodeDecodeError as e:
|
|
364
|
-
|
|
365
|
-
message=f"File encoding error: {path}",
|
|
366
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
367
|
-
details=str(e),
|
|
368
|
-
recovery="Ensure file is encoded in UTF-8",
|
|
369
|
-
) from e
|
|
365
|
+
self._handle_unicode_error(e, path)
|
|
370
366
|
except OSError as e:
|
|
371
|
-
|
|
372
|
-
message=f"System error reading file: {path}",
|
|
373
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
374
|
-
details=str(e),
|
|
375
|
-
recovery="Check file system integrity",
|
|
376
|
-
) from e
|
|
367
|
+
self._handle_os_error(e, path, "reading file")
|
|
377
368
|
|
|
378
369
|
def read_lines_streaming(self, path: str | Path) -> Iterator[str]:
|
|
379
370
|
try:
|
|
380
371
|
path_obj = Path(path) if isinstance(path, str) else path
|
|
381
|
-
|
|
382
|
-
raise FileError(
|
|
383
|
-
message=f"File does not exist: {path_obj}",
|
|
384
|
-
details=f"Attempted to read file at {path_obj.absolute()}",
|
|
385
|
-
recovery="Check file path and ensure file exists",
|
|
386
|
-
)
|
|
372
|
+
self._validate_path_exists(path_obj, "read")
|
|
387
373
|
with path_obj.open(encoding="utf-8") as file:
|
|
388
374
|
for line in file:
|
|
389
375
|
yield line.rstrip("\n\r")
|
|
390
376
|
except PermissionError as e:
|
|
391
|
-
|
|
392
|
-
message=f"Permission denied reading file: {path}",
|
|
393
|
-
error_code=ErrorCode.PERMISSION_ERROR,
|
|
394
|
-
details=str(e),
|
|
395
|
-
recovery="Check file permissions",
|
|
396
|
-
) from e
|
|
377
|
+
self._handle_permission_error(e, path, "reading file")
|
|
397
378
|
except UnicodeDecodeError as e:
|
|
398
|
-
|
|
399
|
-
message=f"File encoding error: {path}",
|
|
400
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
401
|
-
details=str(e),
|
|
402
|
-
recovery="Ensure file is encoded in UTF-8",
|
|
403
|
-
) from e
|
|
379
|
+
self._handle_unicode_error(e, path)
|
|
404
380
|
except OSError as e:
|
|
405
|
-
|
|
406
|
-
message=f"System error reading file: {path}",
|
|
407
|
-
error_code=ErrorCode.FILE_READ_ERROR,
|
|
408
|
-
details=str(e),
|
|
409
|
-
recovery="Check file system integrity",
|
|
410
|
-
) from e
|
|
381
|
+
self._handle_os_error(e, path, "reading file")
|
crackerjack/services/git.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import subprocess
|
|
1
|
+
import subprocess # nosec B404
|
|
2
2
|
import typing as t
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from acb.console import Console
|
|
6
|
+
from acb.depends import Inject, depends
|
|
7
|
+
|
|
8
|
+
from crackerjack.models.protocols import GitInterface
|
|
6
9
|
|
|
7
10
|
from .secure_subprocess import execute_secure_subprocess
|
|
8
11
|
from .security_logger import get_security_logger
|
|
@@ -18,6 +21,7 @@ GIT_COMMANDS = {
|
|
|
18
21
|
"commit": ["commit", "-m"],
|
|
19
22
|
"add_updated": ["add", "-u"],
|
|
20
23
|
"push_porcelain": ["push", "--porcelain"],
|
|
24
|
+
"push_with_tags": ["push", "--porcelain", "--follow-tags"],
|
|
21
25
|
"current_branch": ["branch", "--show-current"],
|
|
22
26
|
"commits_ahead": ["rev-list", "--count", "@{u}..HEAD"],
|
|
23
27
|
}
|
|
@@ -31,8 +35,9 @@ class FailedGitResult:
|
|
|
31
35
|
self.stderr = f"Git security validation failed: {error}"
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
class GitService:
|
|
35
|
-
|
|
38
|
+
class GitService(GitInterface):
|
|
39
|
+
@depends.inject
|
|
40
|
+
def __init__(self, console: Inject[Console], pkg_path: Path | None = None) -> None:
|
|
36
41
|
self.console = console
|
|
37
42
|
self.pkg_path = pkg_path or Path.cwd()
|
|
38
43
|
|
|
@@ -156,7 +161,7 @@ class GitService:
|
|
|
156
161
|
|
|
157
162
|
def _retry_commit_after_restage(self, message: str) -> bool:
|
|
158
163
|
self.console.print(
|
|
159
|
-
"[yellow]🔄[/
|
|
164
|
+
"[yellow]🔄[/yellow] Pre-commit hooks modified files - attempting to re-stage and retry commit"
|
|
160
165
|
)
|
|
161
166
|
|
|
162
167
|
add_result = self._run_git_command(GIT_COMMANDS["add_updated"])
|
|
@@ -204,6 +209,19 @@ class GitService:
|
|
|
204
209
|
self.console.print(f"[red]❌[/ red] Error pushing: {e}")
|
|
205
210
|
return False
|
|
206
211
|
|
|
212
|
+
def push_with_tags(self) -> bool:
|
|
213
|
+
"""Push commits and any tags to remote using --follow-tags."""
|
|
214
|
+
try:
|
|
215
|
+
result = self._run_git_command(GIT_COMMANDS["push_with_tags"])
|
|
216
|
+
if result.returncode == 0:
|
|
217
|
+
self._display_push_success(result.stdout)
|
|
218
|
+
return True
|
|
219
|
+
self.console.print(f"[red]❌[/ red] Push failed: {result.stderr}")
|
|
220
|
+
return False
|
|
221
|
+
except Exception as e:
|
|
222
|
+
self.console.print(f"[red]❌[/ red] Error pushing: {e}")
|
|
223
|
+
return False
|
|
224
|
+
|
|
207
225
|
def _display_push_success(self, push_output: str) -> None:
|
|
208
226
|
lines = push_output.strip().split("\n") if push_output.strip() else []
|
|
209
227
|
|
|
@@ -326,3 +344,79 @@ class GitService:
|
|
|
326
344
|
if result.returncode == 0 and result.stdout.strip().isdigit():
|
|
327
345
|
return int(result.stdout.strip())
|
|
328
346
|
return 0
|
|
347
|
+
|
|
348
|
+
def get_changed_files_by_extension(
|
|
349
|
+
self,
|
|
350
|
+
extensions: list[str],
|
|
351
|
+
include_staged: bool = True,
|
|
352
|
+
include_unstaged: bool = True,
|
|
353
|
+
) -> list[Path]:
|
|
354
|
+
"""Get changed files filtered by file extensions.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
extensions: List of extensions to filter by (e.g., [".py", ".md"])
|
|
358
|
+
include_staged: Include staged files in results
|
|
359
|
+
include_unstaged: Include unstaged files in results
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
List of Path objects for changed files matching the extensions
|
|
363
|
+
|
|
364
|
+
Example:
|
|
365
|
+
>>> git_service.get_changed_files_by_extension([".py"])
|
|
366
|
+
[Path("crackerjack/services/git.py"), Path("tests/test_git.py")]
|
|
367
|
+
"""
|
|
368
|
+
try:
|
|
369
|
+
all_changed: set[str] = set()
|
|
370
|
+
|
|
371
|
+
if include_staged:
|
|
372
|
+
staged_result = self._run_git_command(GIT_COMMANDS["staged_files"])
|
|
373
|
+
if staged_result.stdout.strip():
|
|
374
|
+
all_changed.update(staged_result.stdout.strip().split("\n"))
|
|
375
|
+
|
|
376
|
+
if include_unstaged:
|
|
377
|
+
unstaged_result = self._run_git_command(GIT_COMMANDS["unstaged_files"])
|
|
378
|
+
if unstaged_result.stdout.strip():
|
|
379
|
+
all_changed.update(unstaged_result.stdout.strip().split("\n"))
|
|
380
|
+
|
|
381
|
+
# Filter by extensions
|
|
382
|
+
filtered = [
|
|
383
|
+
self.pkg_path / f
|
|
384
|
+
for f in all_changed
|
|
385
|
+
if f and any(f.endswith(ext) for ext in extensions)
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
# Only return files that actually exist
|
|
389
|
+
return [f for f in filtered if f.exists()]
|
|
390
|
+
|
|
391
|
+
except Exception as e:
|
|
392
|
+
self.console.print(
|
|
393
|
+
f"[yellow]⚠️[/yellow] Error getting changed files by extension: {e}"
|
|
394
|
+
)
|
|
395
|
+
return []
|
|
396
|
+
|
|
397
|
+
def get_current_commit_hash(self) -> str | None:
|
|
398
|
+
"""Get the hash of the current commit (HEAD)."""
|
|
399
|
+
try:
|
|
400
|
+
result = self._run_git_command(["rev-parse", "HEAD"])
|
|
401
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
402
|
+
return result.stdout.strip()
|
|
403
|
+
return None
|
|
404
|
+
except Exception:
|
|
405
|
+
self.console.print("[red]❌[/red] Error getting current commit hash")
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
def reset_hard(self, commit_hash: str) -> bool:
|
|
409
|
+
"""Reset the repository to a specific commit hash (hard reset)."""
|
|
410
|
+
try:
|
|
411
|
+
result = self._run_git_command(["reset", "--hard", commit_hash])
|
|
412
|
+
if result.returncode == 0:
|
|
413
|
+
self.console.print(
|
|
414
|
+
f"[green]✅[/green] Repository reset to {commit_hash[:8]}"
|
|
415
|
+
)
|
|
416
|
+
return True
|
|
417
|
+
else:
|
|
418
|
+
self.console.print(f"[red]❌[/red] Reset failed: {result.stderr}")
|
|
419
|
+
return False
|
|
420
|
+
except Exception as e:
|
|
421
|
+
self.console.print(f"[red]❌[/red] Error during reset: {e}")
|
|
422
|
+
return False
|
|
@@ -347,11 +347,11 @@ class HealthMetricsService:
|
|
|
347
347
|
try:
|
|
348
348
|
from urllib.parse import urlparse
|
|
349
349
|
|
|
350
|
-
url = f"https
|
|
350
|
+
url = f"https: //pypi.org/pypi/{package_name}/json"
|
|
351
351
|
|
|
352
352
|
parsed = urlparse(url)
|
|
353
353
|
if parsed.scheme != "https" or parsed.netloc != "pypi.org":
|
|
354
|
-
msg = f"Invalid URL: only https
|
|
354
|
+
msg = f"Invalid URL: only https: //pypi.org URLs are allowed, got {url}"
|
|
355
355
|
raise ValueError(msg)
|
|
356
356
|
|
|
357
357
|
if not parsed.path.startswith("/pypi/") or not parsed.path.endswith(
|
|
@@ -362,8 +362,7 @@ class HealthMetricsService:
|
|
|
362
362
|
|
|
363
363
|
response = requests.get(url, timeout=10, verify=True)
|
|
364
364
|
response.raise_for_status()
|
|
365
|
-
|
|
366
|
-
return t.cast(dict[str, t.Any] | None, json_result)
|
|
365
|
+
return response.json()
|
|
367
366
|
except Exception:
|
|
368
367
|
return None
|
|
369
368
|
|
|
@@ -379,8 +378,7 @@ class HealthMetricsService:
|
|
|
379
378
|
if not release_info:
|
|
380
379
|
return None
|
|
381
380
|
|
|
382
|
-
|
|
383
|
-
return t.cast(str | None, upload_time_raw)
|
|
381
|
+
return release_info[0].get("upload_time", "")
|
|
384
382
|
|
|
385
383
|
def _calculate_days_since_upload(self, upload_time: str) -> int | None:
|
|
386
384
|
try:
|
|
@@ -205,7 +205,11 @@ class HeatMapGenerator:
|
|
|
205
205
|
bucket_config: dict[str, t.Any],
|
|
206
206
|
) -> dict[str, t.Any]:
|
|
207
207
|
"""Build error count matrix for files and time buckets."""
|
|
208
|
-
|
|
208
|
+
from collections import defaultdict
|
|
209
|
+
|
|
210
|
+
error_matrix: dict[str, t.Any] = defaultdict(
|
|
211
|
+
lambda: defaultdict(int) # type: ignore[call-overload]
|
|
212
|
+
)
|
|
209
213
|
|
|
210
214
|
for file_path, errors in self.error_data.items():
|
|
211
215
|
for error in errors:
|
|
@@ -450,7 +454,7 @@ class HeatMapGenerator:
|
|
|
450
454
|
|
|
451
455
|
def _calculate_metric_max_values(self, metric_types: list[str]) -> dict[str, float]:
|
|
452
456
|
"""Calculate max values for normalization."""
|
|
453
|
-
max_values = {}
|
|
457
|
+
max_values: dict[str, float] = {}
|
|
454
458
|
for metric_type in metric_types:
|
|
455
459
|
values = [
|
|
456
460
|
data["metrics"][metric_type]
|
|
@@ -558,8 +562,13 @@ class HeatMapGenerator:
|
|
|
558
562
|
self, test_errors: list[dict[str, t.Any]]
|
|
559
563
|
) -> defaultdict[str, defaultdict[str, int]]:
|
|
560
564
|
"""Group test errors by file and error type."""
|
|
565
|
+
from collections import defaultdict as dd
|
|
566
|
+
|
|
567
|
+
def make_inner_defaultdict() -> defaultdict[str, int]:
|
|
568
|
+
return dd(int)
|
|
569
|
+
|
|
561
570
|
test_matrix: defaultdict[str, defaultdict[str, int]] = defaultdict(
|
|
562
|
-
|
|
571
|
+
make_inner_defaultdict
|
|
563
572
|
)
|
|
564
573
|
|
|
565
574
|
for error in test_errors:
|