crackerjack 0.18.2__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 +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- 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/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- 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 +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- 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 +230 -0
- 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/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- 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 +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- 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/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- 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 +2243 -0
- 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/INDEX.md +11 -0
- crackerjack/docs/README.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/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- 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 +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- 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 +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- 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 +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- 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 +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- 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/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- 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/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- 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 +523 -0
- 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/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- 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_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -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.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dependency Guard module to ensure proper dependency injection.
|
|
3
|
+
|
|
4
|
+
This module provides utilities to ensure that dependencies are properly
|
|
5
|
+
registered in the ACB dependency injection system and to handle cases
|
|
6
|
+
where dependencies might be registered as empty tuples or other invalid
|
|
7
|
+
values.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import typing
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from acb.depends import depends
|
|
15
|
+
from acb.logger import Logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _should_log_debug() -> bool:
|
|
19
|
+
"""Check if debug mode is active via CLI flags.
|
|
20
|
+
|
|
21
|
+
Uses sys.argv directly for early detection before flag parsing.
|
|
22
|
+
This is thread-safe as sys.argv is read-only during execution.
|
|
23
|
+
"""
|
|
24
|
+
return any(
|
|
25
|
+
arg in ("--debug", "-d", "--ai-debug") or arg.startswith("--debug=")
|
|
26
|
+
for arg in sys.argv[1:]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _log_dependency_issue(message: str, level: str = "WARNING") -> None:
|
|
31
|
+
"""Log dependency issues only in debug mode.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
message: The log message to emit
|
|
35
|
+
level: Log level (INFO, WARNING, ERROR)
|
|
36
|
+
"""
|
|
37
|
+
if not _should_log_debug():
|
|
38
|
+
return
|
|
39
|
+
# Use stderr for diagnostic messages (stdout reserved for user output)
|
|
40
|
+
print(f"[CRACKERJACK:{level}] {message}", file=sys.stderr)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def ensure_logger_dependency() -> None:
|
|
44
|
+
"""
|
|
45
|
+
Ensure that Logger and LoggerProtocol are properly registered in the DI container.
|
|
46
|
+
This prevents issues where empty tuples might get registered instead of logger instances.
|
|
47
|
+
"""
|
|
48
|
+
# Check if Logger is registered and has a valid instance
|
|
49
|
+
try:
|
|
50
|
+
logger_instance = depends.get_sync(Logger)
|
|
51
|
+
# If we get an empty tuple, string, or other invalid value, replace it
|
|
52
|
+
if isinstance(logger_instance, tuple) and len(logger_instance) == 0:
|
|
53
|
+
_log_dependency_issue(
|
|
54
|
+
"Logger dependency was registered as empty tuple, replacing with fresh instance"
|
|
55
|
+
)
|
|
56
|
+
# Create a new logger instance to replace the invalid one
|
|
57
|
+
from acb.logger import Logger as ACBLogger
|
|
58
|
+
|
|
59
|
+
fresh_logger = ACBLogger()
|
|
60
|
+
depends.set(Logger, fresh_logger)
|
|
61
|
+
elif isinstance(logger_instance, str):
|
|
62
|
+
_log_dependency_issue(
|
|
63
|
+
f"Logger dependency was registered as string ({logger_instance!r}), replacing with fresh instance"
|
|
64
|
+
)
|
|
65
|
+
# Create a new logger instance to replace the invalid one
|
|
66
|
+
from acb.logger import Logger as ACBLogger
|
|
67
|
+
|
|
68
|
+
fresh_logger = ACBLogger()
|
|
69
|
+
depends.set(Logger, fresh_logger)
|
|
70
|
+
except Exception:
|
|
71
|
+
# If there's no logger registered at all, create one
|
|
72
|
+
from acb.logger import Logger as ACBLogger
|
|
73
|
+
|
|
74
|
+
fresh_logger = ACBLogger()
|
|
75
|
+
depends.set(Logger, fresh_logger)
|
|
76
|
+
|
|
77
|
+
# Do the same check for LoggerProtocol if it exists
|
|
78
|
+
# Import once to avoid typing issues
|
|
79
|
+
try:
|
|
80
|
+
from crackerjack.models.protocols import LoggerProtocol as _LoggerProtocol
|
|
81
|
+
|
|
82
|
+
logger_proto_instance = depends.get_sync(_LoggerProtocol)
|
|
83
|
+
if isinstance(logger_proto_instance, tuple) and len(logger_proto_instance) == 0:
|
|
84
|
+
_log_dependency_issue(
|
|
85
|
+
"LoggerProtocol dependency was registered as empty tuple, replacing with fresh instance"
|
|
86
|
+
)
|
|
87
|
+
from acb.logger import Logger as ACBLogger
|
|
88
|
+
|
|
89
|
+
fresh_logger = ACBLogger()
|
|
90
|
+
depends.set(_LoggerProtocol, fresh_logger)
|
|
91
|
+
elif isinstance(logger_proto_instance, str):
|
|
92
|
+
_log_dependency_issue(
|
|
93
|
+
f"LoggerProtocol dependency was registered as string ({logger_proto_instance!r}), replacing with fresh instance"
|
|
94
|
+
)
|
|
95
|
+
from acb.logger import Logger as ACBLogger
|
|
96
|
+
|
|
97
|
+
fresh_logger = ACBLogger()
|
|
98
|
+
depends.set(_LoggerProtocol, fresh_logger)
|
|
99
|
+
except ImportError:
|
|
100
|
+
# LoggerProtocol doesn't exist, that's fine
|
|
101
|
+
pass
|
|
102
|
+
except Exception:
|
|
103
|
+
# If there's no LoggerProtocol registered, create one
|
|
104
|
+
try:
|
|
105
|
+
from acb.logger import Logger as ACBLogger
|
|
106
|
+
|
|
107
|
+
fresh_logger = ACBLogger()
|
|
108
|
+
_log_dependency_issue(
|
|
109
|
+
"Registering LoggerProtocol with fresh logger instance", level="INFO"
|
|
110
|
+
)
|
|
111
|
+
# Register the fresh_logger instance with the LoggerProtocol
|
|
112
|
+
depends.set(_LoggerProtocol, fresh_logger)
|
|
113
|
+
except NameError:
|
|
114
|
+
# _LoggerProtocol is not defined if the import failed
|
|
115
|
+
pass
|
|
116
|
+
except Exception:
|
|
117
|
+
pass # Any other error, just continue
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def validate_dependency_registration(
|
|
121
|
+
dep_type: type[Any], fallback_factory: typing.Callable | None = None
|
|
122
|
+
) -> bool:
|
|
123
|
+
"""
|
|
124
|
+
Validate that a dependency is properly registered and not an empty tuple or string.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
dep_type: The type of dependency to validate
|
|
128
|
+
fallback_factory: Optional factory function to create a fallback instance if needed
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if the dependency is properly registered, False otherwise
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
instance = depends.get_sync(dep_type)
|
|
135
|
+
# Check if it's an empty tuple (the problematic case)
|
|
136
|
+
if isinstance(instance, tuple) and len(instance) == 0:
|
|
137
|
+
_log_dependency_issue(
|
|
138
|
+
f"Dependency {dep_type} was registered as empty tuple"
|
|
139
|
+
)
|
|
140
|
+
# Replace with a fallback if provided
|
|
141
|
+
if fallback_factory:
|
|
142
|
+
fallback_instance = fallback_factory()
|
|
143
|
+
depends.set(dep_type, fallback_instance)
|
|
144
|
+
_log_dependency_issue(
|
|
145
|
+
f"Replaced empty tuple for {dep_type} with new instance",
|
|
146
|
+
level="INFO",
|
|
147
|
+
)
|
|
148
|
+
return True
|
|
149
|
+
return False
|
|
150
|
+
# Check if it's a string (another problematic case)
|
|
151
|
+
elif isinstance(instance, str):
|
|
152
|
+
_log_dependency_issue(
|
|
153
|
+
f"Dependency {dep_type} was registered as string: {instance!r}"
|
|
154
|
+
)
|
|
155
|
+
# Replace with a fallback if provided
|
|
156
|
+
if fallback_factory:
|
|
157
|
+
fallback_instance = fallback_factory()
|
|
158
|
+
depends.set(dep_type, fallback_instance)
|
|
159
|
+
_log_dependency_issue(
|
|
160
|
+
f"Replaced string for {dep_type} with new instance", level="INFO"
|
|
161
|
+
)
|
|
162
|
+
return True
|
|
163
|
+
return False
|
|
164
|
+
return True
|
|
165
|
+
except Exception:
|
|
166
|
+
# If dependency doesn't exist at all, return False
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def safe_get_logger() -> Logger:
|
|
171
|
+
"""
|
|
172
|
+
Safely get a logger instance, ensuring it's not an empty tuple or string.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
A valid logger instance
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
logger_instance = depends.get_sync(Logger)
|
|
179
|
+
if isinstance(logger_instance, tuple) and len(logger_instance) == 0:
|
|
180
|
+
_log_dependency_issue(
|
|
181
|
+
"Logger dependency was an empty tuple in safe_get_logger, replacing with fresh instance"
|
|
182
|
+
)
|
|
183
|
+
# Create and register a fresh logger
|
|
184
|
+
from acb.logger import Logger as ACBLogger
|
|
185
|
+
|
|
186
|
+
fresh_logger = ACBLogger()
|
|
187
|
+
depends.set(Logger, fresh_logger)
|
|
188
|
+
return fresh_logger
|
|
189
|
+
elif isinstance(logger_instance, str):
|
|
190
|
+
_log_dependency_issue(
|
|
191
|
+
f"Logger dependency was a string ({logger_instance!r}) in safe_get_logger, replacing with fresh instance"
|
|
192
|
+
)
|
|
193
|
+
# Create and register a fresh logger
|
|
194
|
+
from acb.logger import Logger as ACBLogger
|
|
195
|
+
|
|
196
|
+
fresh_logger = ACBLogger()
|
|
197
|
+
depends.set(Logger, fresh_logger)
|
|
198
|
+
return fresh_logger
|
|
199
|
+
return logger_instance
|
|
200
|
+
except Exception:
|
|
201
|
+
# If no logger is registered, create one
|
|
202
|
+
from acb.logger import Logger as ACBLogger
|
|
203
|
+
|
|
204
|
+
_log_dependency_issue(
|
|
205
|
+
"No logger registered, creating and registering a fresh logger instance",
|
|
206
|
+
level="INFO",
|
|
207
|
+
)
|
|
208
|
+
fresh_logger = ACBLogger()
|
|
209
|
+
depends.set(Logger, fresh_logger)
|
|
210
|
+
return fresh_logger
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def check_all_dependencies_for_empty_tuples():
|
|
214
|
+
"""
|
|
215
|
+
Debug function to check all registered dependencies for empty tuples.
|
|
216
|
+
This can help identify which dependencies have been incorrectly registered.
|
|
217
|
+
"""
|
|
218
|
+
# This would require access to the internal state of the ACB DI system
|
|
219
|
+
# which might not be available, so we'll just print a notice
|
|
220
|
+
_log_dependency_issue(
|
|
221
|
+
"Dependency check: To check all dependencies for empty tuples, you would need access to ACB's internal container state.",
|
|
222
|
+
level="INFO",
|
|
223
|
+
)
|
|
224
|
+
_log_dependency_issue(
|
|
225
|
+
"This is currently not possible without modifying ACB itself.", level="INFO"
|
|
226
|
+
)
|
|
227
|
+
_log_dependency_issue(
|
|
228
|
+
"The best approach is to use the individual validation functions for known problematic dependencies.",
|
|
229
|
+
level="INFO",
|
|
230
|
+
)
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Retry utilities for handling API connection errors and other transient failures.
|
|
2
|
+
|
|
3
|
+
This module provides a general-purpose retry decorator that can be used across
|
|
4
|
+
the Crackerjack codebase to handle API connection errors and other transient failures.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import functools
|
|
9
|
+
import random
|
|
10
|
+
import time
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import Any, TypeVar, cast
|
|
13
|
+
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _calculate_delay(current_delay: float, jitter: bool, backoff: float) -> float:
|
|
20
|
+
"""Calculate the delay for the next retry attempt."""
|
|
21
|
+
if jitter:
|
|
22
|
+
return current_delay * (0.5 + random.random() * 0.5) # nosec B311 # Not used for cryptographic purposes
|
|
23
|
+
return current_delay * backoff
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _prepare_next_attempt(
|
|
27
|
+
current_delay: float,
|
|
28
|
+
max_delay: float | None,
|
|
29
|
+
backoff: float,
|
|
30
|
+
jitter: bool,
|
|
31
|
+
attempt: int,
|
|
32
|
+
max_attempts: int,
|
|
33
|
+
e: BaseException,
|
|
34
|
+
logger_func: Callable[[str], None] | None,
|
|
35
|
+
) -> float:
|
|
36
|
+
"""Prepare for the next retry attempt by calculating delay and logging."""
|
|
37
|
+
current_delay = _calculate_delay(current_delay, jitter, backoff)
|
|
38
|
+
|
|
39
|
+
if max_delay:
|
|
40
|
+
current_delay = min(current_delay, max_delay)
|
|
41
|
+
|
|
42
|
+
log_msg = (
|
|
43
|
+
f"Attempt {attempt + 1}/{max_attempts} failed: {type(e).__name__}: {e}. "
|
|
44
|
+
f"Retrying in {current_delay:.2f}s..."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if logger_func:
|
|
48
|
+
logger_func(log_msg)
|
|
49
|
+
else:
|
|
50
|
+
logger.warning(log_msg)
|
|
51
|
+
|
|
52
|
+
return current_delay
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _should_retry(attempt: int, max_attempts: int) -> bool:
|
|
56
|
+
"""Determine if we should make another retry attempt."""
|
|
57
|
+
return attempt != max_attempts - 1 # Continue unless it's the last attempt
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def retry(
|
|
61
|
+
max_attempts: int = 3,
|
|
62
|
+
delay: float = 1.0,
|
|
63
|
+
backoff: float = 2.0,
|
|
64
|
+
max_delay: float | None = None,
|
|
65
|
+
jitter: bool = True,
|
|
66
|
+
exceptions: tuple[type[BaseException], ...] = (Exception,),
|
|
67
|
+
logger_func: Callable[[str], None] | None = None,
|
|
68
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
|
69
|
+
"""Decorator to retry a function when specific exceptions are raised.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
max_attempts: Maximum number of attempts (including initial call)
|
|
73
|
+
delay: Initial delay between retries in seconds
|
|
74
|
+
backoff: Multiplier for delay between attempts (exponential backoff)
|
|
75
|
+
max_delay: Maximum delay between retries (caps exponential growth)
|
|
76
|
+
jitter: Add random jitter to delay to prevent thundering herd
|
|
77
|
+
exceptions: Tuple of exception types to catch and retry on
|
|
78
|
+
logger_func: Optional logger function to use for retry messages
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Decorator function
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
|
85
|
+
@functools.wraps(func)
|
|
86
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> T:
|
|
87
|
+
return await _retry_async(
|
|
88
|
+
func,
|
|
89
|
+
args,
|
|
90
|
+
kwargs,
|
|
91
|
+
max_attempts,
|
|
92
|
+
delay,
|
|
93
|
+
backoff,
|
|
94
|
+
max_delay,
|
|
95
|
+
jitter,
|
|
96
|
+
exceptions,
|
|
97
|
+
logger_func,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
@functools.wraps(func)
|
|
101
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> T:
|
|
102
|
+
return _retry_sync(
|
|
103
|
+
func,
|
|
104
|
+
args,
|
|
105
|
+
kwargs,
|
|
106
|
+
max_attempts,
|
|
107
|
+
delay,
|
|
108
|
+
backoff,
|
|
109
|
+
max_delay,
|
|
110
|
+
jitter,
|
|
111
|
+
exceptions,
|
|
112
|
+
logger_func,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if asyncio.iscoroutinefunction(func):
|
|
116
|
+
return cast(Callable[..., T], async_wrapper)
|
|
117
|
+
return cast(Callable[..., T], sync_wrapper)
|
|
118
|
+
|
|
119
|
+
return decorator
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def _retry_async[T](
|
|
123
|
+
func: Callable[..., T],
|
|
124
|
+
args: tuple[Any, ...],
|
|
125
|
+
kwargs: dict[str, Any],
|
|
126
|
+
max_attempts: int,
|
|
127
|
+
delay: float,
|
|
128
|
+
backoff: float,
|
|
129
|
+
max_delay: float | None,
|
|
130
|
+
jitter: bool,
|
|
131
|
+
exceptions: tuple[type[BaseException], ...],
|
|
132
|
+
logger_func: Callable[[str], None] | None,
|
|
133
|
+
) -> T:
|
|
134
|
+
"""Execute async function with retry logic."""
|
|
135
|
+
last_exception: BaseException | None = None
|
|
136
|
+
current_delay = delay
|
|
137
|
+
|
|
138
|
+
for attempt in range(max_attempts):
|
|
139
|
+
try:
|
|
140
|
+
result = await func(*args, **kwargs) # type: ignore[misc]
|
|
141
|
+
return result # type: ignore[no-any-return]
|
|
142
|
+
|
|
143
|
+
except exceptions as e:
|
|
144
|
+
last_exception = e
|
|
145
|
+
|
|
146
|
+
if not _should_retry(attempt, max_attempts):
|
|
147
|
+
break
|
|
148
|
+
|
|
149
|
+
current_delay = _prepare_next_attempt(
|
|
150
|
+
current_delay,
|
|
151
|
+
max_delay,
|
|
152
|
+
backoff,
|
|
153
|
+
jitter,
|
|
154
|
+
attempt,
|
|
155
|
+
max_attempts,
|
|
156
|
+
e,
|
|
157
|
+
logger_func,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
await asyncio.sleep(current_delay)
|
|
161
|
+
|
|
162
|
+
if last_exception is not None:
|
|
163
|
+
raise last_exception
|
|
164
|
+
raise RuntimeError("Retry failed but no exception was captured")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _retry_sync[T](
|
|
168
|
+
func: Callable[..., T],
|
|
169
|
+
args: tuple[Any, ...],
|
|
170
|
+
kwargs: dict[str, Any],
|
|
171
|
+
max_attempts: int,
|
|
172
|
+
delay: float,
|
|
173
|
+
backoff: float,
|
|
174
|
+
max_delay: float | None,
|
|
175
|
+
jitter: bool,
|
|
176
|
+
exceptions: tuple[type[BaseException], ...],
|
|
177
|
+
logger_func: Callable[[str], None] | None,
|
|
178
|
+
) -> T:
|
|
179
|
+
"""Execute sync function with retry logic."""
|
|
180
|
+
last_exception: BaseException | None = None
|
|
181
|
+
current_delay = delay
|
|
182
|
+
|
|
183
|
+
for attempt in range(max_attempts):
|
|
184
|
+
try:
|
|
185
|
+
result = func(*args, **kwargs)
|
|
186
|
+
return result # type: ignore[no-any-return]
|
|
187
|
+
|
|
188
|
+
except exceptions as e:
|
|
189
|
+
last_exception = e
|
|
190
|
+
|
|
191
|
+
if not _should_retry(attempt, max_attempts):
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
current_delay = _prepare_next_attempt(
|
|
195
|
+
current_delay,
|
|
196
|
+
max_delay,
|
|
197
|
+
backoff,
|
|
198
|
+
jitter,
|
|
199
|
+
attempt,
|
|
200
|
+
max_attempts,
|
|
201
|
+
e,
|
|
202
|
+
logger_func,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
time.sleep(current_delay)
|
|
206
|
+
|
|
207
|
+
if last_exception is not None:
|
|
208
|
+
raise last_exception
|
|
209
|
+
raise RuntimeError("Retry failed but no exception was captured")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# Common exception types for API connection retries
|
|
213
|
+
API_CONNECTION_EXCEPTIONS = (
|
|
214
|
+
ConnectionError,
|
|
215
|
+
TimeoutError,
|
|
216
|
+
ConnectionResetError,
|
|
217
|
+
ConnectionAbortedError,
|
|
218
|
+
BrokenPipeError,
|
|
219
|
+
OSError, # Network-related OS errors
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# Convenience decorator for API calls with common settings
|
|
224
|
+
def retry_api_call(
|
|
225
|
+
max_attempts: int = 3,
|
|
226
|
+
delay: float = 1.0,
|
|
227
|
+
backoff: float = 2.0,
|
|
228
|
+
max_delay: float | None = 30.0, # Cap at 30 seconds
|
|
229
|
+
jitter: bool = True,
|
|
230
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
|
231
|
+
"""Convenience decorator for API calls with sensible defaults.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
max_attempts: Maximum number of attempts (default: 3)
|
|
235
|
+
delay: Initial delay in seconds (default: 1.0)
|
|
236
|
+
backoff: Exponential backoff multiplier (default: 2.0)
|
|
237
|
+
max_delay: Maximum delay between retries (default: 30.0)
|
|
238
|
+
jitter: Add jitter to prevent thundering herd (default: True)
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Decorator function configured for API calls
|
|
242
|
+
"""
|
|
243
|
+
return retry(
|
|
244
|
+
max_attempts=max_attempts,
|
|
245
|
+
delay=delay,
|
|
246
|
+
backoff=backoff,
|
|
247
|
+
max_delay=max_delay,
|
|
248
|
+
jitter=jitter,
|
|
249
|
+
exceptions=API_CONNECTION_EXCEPTIONS,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
# Example usage functions for testing purposes
|
|
254
|
+
@retry_api_call(max_attempts=3, delay=0.5)
|
|
255
|
+
async def example_api_call_async(url: str) -> str:
|
|
256
|
+
"""Example async API call that might fail with network issues."""
|
|
257
|
+
# Simulate an API call that might fail
|
|
258
|
+
# import random # Already imported at the top of the file
|
|
259
|
+
|
|
260
|
+
if random.random() < 0.7: # 70% chance of failure for testing # nosec B311
|
|
261
|
+
raise ConnectionError("Simulated network error")
|
|
262
|
+
|
|
263
|
+
return f"Success: {url}"
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@retry_api_call(max_attempts=3, delay=0.5)
|
|
267
|
+
def example_api_call_sync(url: str) -> str:
|
|
268
|
+
"""Example sync API call that might fail with network issues."""
|
|
269
|
+
# Simulate an API call that might fail
|
|
270
|
+
# import random # Already imported at the top of file
|
|
271
|
+
|
|
272
|
+
if random.random() < 0.7: # 70% chance of failure for testing # nosec B311
|
|
273
|
+
raise ConnectionError("Simulated network error")
|
|
274
|
+
|
|
275
|
+
return f"Success: {url}"
|