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.
Files changed (533) hide show
  1. crackerjack/README.md +19 -0
  2. crackerjack/__init__.py +96 -2
  3. crackerjack/__main__.py +637 -138
  4. crackerjack/adapters/README.md +18 -0
  5. crackerjack/adapters/__init__.py +39 -0
  6. crackerjack/adapters/_output_paths.py +167 -0
  7. crackerjack/adapters/_qa_adapter_base.py +309 -0
  8. crackerjack/adapters/_tool_adapter_base.py +706 -0
  9. crackerjack/adapters/ai/README.md +65 -0
  10. crackerjack/adapters/ai/__init__.py +5 -0
  11. crackerjack/adapters/ai/claude.py +853 -0
  12. crackerjack/adapters/complexity/README.md +53 -0
  13. crackerjack/adapters/complexity/__init__.py +10 -0
  14. crackerjack/adapters/complexity/complexipy.py +641 -0
  15. crackerjack/adapters/dependency/__init__.py +22 -0
  16. crackerjack/adapters/dependency/pip_audit.py +418 -0
  17. crackerjack/adapters/format/README.md +72 -0
  18. crackerjack/adapters/format/__init__.py +11 -0
  19. crackerjack/adapters/format/mdformat.py +313 -0
  20. crackerjack/adapters/format/ruff.py +516 -0
  21. crackerjack/adapters/lint/README.md +47 -0
  22. crackerjack/adapters/lint/__init__.py +11 -0
  23. crackerjack/adapters/lint/codespell.py +273 -0
  24. crackerjack/adapters/lsp/README.md +49 -0
  25. crackerjack/adapters/lsp/__init__.py +27 -0
  26. crackerjack/adapters/lsp/_base.py +194 -0
  27. crackerjack/adapters/lsp/_client.py +358 -0
  28. crackerjack/adapters/lsp/_manager.py +193 -0
  29. crackerjack/adapters/lsp/skylos.py +283 -0
  30. crackerjack/adapters/lsp/zuban.py +557 -0
  31. crackerjack/adapters/refactor/README.md +59 -0
  32. crackerjack/adapters/refactor/__init__.py +12 -0
  33. crackerjack/adapters/refactor/creosote.py +318 -0
  34. crackerjack/adapters/refactor/refurb.py +406 -0
  35. crackerjack/adapters/refactor/skylos.py +494 -0
  36. crackerjack/adapters/sast/README.md +132 -0
  37. crackerjack/adapters/sast/__init__.py +32 -0
  38. crackerjack/adapters/sast/_base.py +201 -0
  39. crackerjack/adapters/sast/bandit.py +423 -0
  40. crackerjack/adapters/sast/pyscn.py +405 -0
  41. crackerjack/adapters/sast/semgrep.py +241 -0
  42. crackerjack/adapters/security/README.md +111 -0
  43. crackerjack/adapters/security/__init__.py +17 -0
  44. crackerjack/adapters/security/gitleaks.py +339 -0
  45. crackerjack/adapters/type/README.md +52 -0
  46. crackerjack/adapters/type/__init__.py +12 -0
  47. crackerjack/adapters/type/pyrefly.py +402 -0
  48. crackerjack/adapters/type/ty.py +402 -0
  49. crackerjack/adapters/type/zuban.py +522 -0
  50. crackerjack/adapters/utility/README.md +51 -0
  51. crackerjack/adapters/utility/__init__.py +10 -0
  52. crackerjack/adapters/utility/checks.py +884 -0
  53. crackerjack/agents/README.md +264 -0
  54. crackerjack/agents/__init__.py +66 -0
  55. crackerjack/agents/architect_agent.py +238 -0
  56. crackerjack/agents/base.py +167 -0
  57. crackerjack/agents/claude_code_bridge.py +641 -0
  58. crackerjack/agents/coordinator.py +600 -0
  59. crackerjack/agents/documentation_agent.py +520 -0
  60. crackerjack/agents/dry_agent.py +585 -0
  61. crackerjack/agents/enhanced_coordinator.py +279 -0
  62. crackerjack/agents/enhanced_proactive_agent.py +185 -0
  63. crackerjack/agents/error_middleware.py +53 -0
  64. crackerjack/agents/formatting_agent.py +230 -0
  65. crackerjack/agents/helpers/__init__.py +9 -0
  66. crackerjack/agents/helpers/performance/__init__.py +22 -0
  67. crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
  68. crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
  69. crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
  70. crackerjack/agents/helpers/refactoring/__init__.py +22 -0
  71. crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
  72. crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
  73. crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
  74. crackerjack/agents/helpers/test_creation/__init__.py +19 -0
  75. crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
  76. crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
  77. crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
  78. crackerjack/agents/import_optimization_agent.py +1181 -0
  79. crackerjack/agents/performance_agent.py +325 -0
  80. crackerjack/agents/performance_helpers.py +205 -0
  81. crackerjack/agents/proactive_agent.py +55 -0
  82. crackerjack/agents/refactoring_agent.py +511 -0
  83. crackerjack/agents/refactoring_helpers.py +247 -0
  84. crackerjack/agents/security_agent.py +793 -0
  85. crackerjack/agents/semantic_agent.py +479 -0
  86. crackerjack/agents/semantic_helpers.py +356 -0
  87. crackerjack/agents/test_creation_agent.py +570 -0
  88. crackerjack/agents/test_specialist_agent.py +526 -0
  89. crackerjack/agents/tracker.py +110 -0
  90. crackerjack/api.py +647 -0
  91. crackerjack/cli/README.md +394 -0
  92. crackerjack/cli/__init__.py +24 -0
  93. crackerjack/cli/cache_handlers.py +209 -0
  94. crackerjack/cli/cache_handlers_enhanced.py +680 -0
  95. crackerjack/cli/facade.py +162 -0
  96. crackerjack/cli/formatting.py +13 -0
  97. crackerjack/cli/handlers/__init__.py +85 -0
  98. crackerjack/cli/handlers/advanced.py +103 -0
  99. crackerjack/cli/handlers/ai_features.py +62 -0
  100. crackerjack/cli/handlers/analytics.py +479 -0
  101. crackerjack/cli/handlers/changelog.py +271 -0
  102. crackerjack/cli/handlers/config_handlers.py +16 -0
  103. crackerjack/cli/handlers/coverage.py +84 -0
  104. crackerjack/cli/handlers/documentation.py +280 -0
  105. crackerjack/cli/handlers/main_handlers.py +497 -0
  106. crackerjack/cli/handlers/monitoring.py +371 -0
  107. crackerjack/cli/handlers.py +700 -0
  108. crackerjack/cli/interactive.py +488 -0
  109. crackerjack/cli/options.py +1216 -0
  110. crackerjack/cli/semantic_handlers.py +292 -0
  111. crackerjack/cli/utils.py +19 -0
  112. crackerjack/cli/version.py +19 -0
  113. crackerjack/code_cleaner.py +1307 -0
  114. crackerjack/config/README.md +472 -0
  115. crackerjack/config/__init__.py +275 -0
  116. crackerjack/config/global_lock_config.py +207 -0
  117. crackerjack/config/hooks.py +390 -0
  118. crackerjack/config/loader.py +239 -0
  119. crackerjack/config/settings.py +141 -0
  120. crackerjack/config/tool_commands.py +331 -0
  121. crackerjack/core/README.md +393 -0
  122. crackerjack/core/__init__.py +0 -0
  123. crackerjack/core/async_workflow_orchestrator.py +738 -0
  124. crackerjack/core/autofix_coordinator.py +282 -0
  125. crackerjack/core/container.py +105 -0
  126. crackerjack/core/enhanced_container.py +583 -0
  127. crackerjack/core/file_lifecycle.py +472 -0
  128. crackerjack/core/performance.py +244 -0
  129. crackerjack/core/performance_monitor.py +357 -0
  130. crackerjack/core/phase_coordinator.py +1227 -0
  131. crackerjack/core/proactive_workflow.py +267 -0
  132. crackerjack/core/resource_manager.py +425 -0
  133. crackerjack/core/retry.py +275 -0
  134. crackerjack/core/service_watchdog.py +601 -0
  135. crackerjack/core/session_coordinator.py +239 -0
  136. crackerjack/core/timeout_manager.py +563 -0
  137. crackerjack/core/websocket_lifecycle.py +410 -0
  138. crackerjack/core/workflow/__init__.py +21 -0
  139. crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
  140. crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
  141. crackerjack/core/workflow/workflow_issue_parser.py +714 -0
  142. crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
  143. crackerjack/core/workflow/workflow_security_gates.py +400 -0
  144. crackerjack/core/workflow_orchestrator.py +2243 -0
  145. crackerjack/data/README.md +11 -0
  146. crackerjack/data/__init__.py +8 -0
  147. crackerjack/data/models.py +79 -0
  148. crackerjack/data/repository.py +210 -0
  149. crackerjack/decorators/README.md +180 -0
  150. crackerjack/decorators/__init__.py +35 -0
  151. crackerjack/decorators/error_handling.py +649 -0
  152. crackerjack/decorators/error_handling_decorators.py +334 -0
  153. crackerjack/decorators/helpers.py +58 -0
  154. crackerjack/decorators/patterns.py +281 -0
  155. crackerjack/decorators/utils.py +58 -0
  156. crackerjack/docs/INDEX.md +11 -0
  157. crackerjack/docs/README.md +11 -0
  158. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  159. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  160. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  161. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  162. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  163. crackerjack/documentation/README.md +11 -0
  164. crackerjack/documentation/__init__.py +31 -0
  165. crackerjack/documentation/ai_templates.py +756 -0
  166. crackerjack/documentation/dual_output_generator.py +767 -0
  167. crackerjack/documentation/mkdocs_integration.py +518 -0
  168. crackerjack/documentation/reference_generator.py +1065 -0
  169. crackerjack/dynamic_config.py +678 -0
  170. crackerjack/errors.py +378 -0
  171. crackerjack/events/README.md +11 -0
  172. crackerjack/events/__init__.py +16 -0
  173. crackerjack/events/telemetry.py +175 -0
  174. crackerjack/events/workflow_bus.py +346 -0
  175. crackerjack/exceptions/README.md +301 -0
  176. crackerjack/exceptions/__init__.py +5 -0
  177. crackerjack/exceptions/config.py +4 -0
  178. crackerjack/exceptions/tool_execution_error.py +245 -0
  179. crackerjack/executors/README.md +591 -0
  180. crackerjack/executors/__init__.py +13 -0
  181. crackerjack/executors/async_hook_executor.py +938 -0
  182. crackerjack/executors/cached_hook_executor.py +316 -0
  183. crackerjack/executors/hook_executor.py +1295 -0
  184. crackerjack/executors/hook_lock_manager.py +708 -0
  185. crackerjack/executors/individual_hook_executor.py +739 -0
  186. crackerjack/executors/lsp_aware_hook_executor.py +349 -0
  187. crackerjack/executors/progress_hook_executor.py +282 -0
  188. crackerjack/executors/tool_proxy.py +433 -0
  189. crackerjack/hooks/README.md +485 -0
  190. crackerjack/hooks/lsp_hook.py +93 -0
  191. crackerjack/intelligence/README.md +557 -0
  192. crackerjack/intelligence/__init__.py +37 -0
  193. crackerjack/intelligence/adaptive_learning.py +693 -0
  194. crackerjack/intelligence/agent_orchestrator.py +485 -0
  195. crackerjack/intelligence/agent_registry.py +377 -0
  196. crackerjack/intelligence/agent_selector.py +439 -0
  197. crackerjack/intelligence/integration.py +250 -0
  198. crackerjack/interactive.py +719 -0
  199. crackerjack/managers/README.md +369 -0
  200. crackerjack/managers/__init__.py +11 -0
  201. crackerjack/managers/async_hook_manager.py +135 -0
  202. crackerjack/managers/hook_manager.py +585 -0
  203. crackerjack/managers/publish_manager.py +631 -0
  204. crackerjack/managers/test_command_builder.py +391 -0
  205. crackerjack/managers/test_executor.py +474 -0
  206. crackerjack/managers/test_manager.py +1357 -0
  207. crackerjack/managers/test_progress.py +187 -0
  208. crackerjack/mcp/README.md +374 -0
  209. crackerjack/mcp/__init__.py +0 -0
  210. crackerjack/mcp/cache.py +352 -0
  211. crackerjack/mcp/client_runner.py +121 -0
  212. crackerjack/mcp/context.py +802 -0
  213. crackerjack/mcp/dashboard.py +657 -0
  214. crackerjack/mcp/enhanced_progress_monitor.py +493 -0
  215. crackerjack/mcp/file_monitor.py +394 -0
  216. crackerjack/mcp/progress_components.py +607 -0
  217. crackerjack/mcp/progress_monitor.py +1016 -0
  218. crackerjack/mcp/rate_limiter.py +336 -0
  219. crackerjack/mcp/server.py +24 -0
  220. crackerjack/mcp/server_core.py +526 -0
  221. crackerjack/mcp/service_watchdog.py +505 -0
  222. crackerjack/mcp/state.py +407 -0
  223. crackerjack/mcp/task_manager.py +259 -0
  224. crackerjack/mcp/tools/README.md +27 -0
  225. crackerjack/mcp/tools/__init__.py +19 -0
  226. crackerjack/mcp/tools/core_tools.py +469 -0
  227. crackerjack/mcp/tools/error_analyzer.py +283 -0
  228. crackerjack/mcp/tools/execution_tools.py +384 -0
  229. crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
  230. crackerjack/mcp/tools/intelligence_tools.py +264 -0
  231. crackerjack/mcp/tools/monitoring_tools.py +628 -0
  232. crackerjack/mcp/tools/proactive_tools.py +367 -0
  233. crackerjack/mcp/tools/progress_tools.py +222 -0
  234. crackerjack/mcp/tools/semantic_tools.py +584 -0
  235. crackerjack/mcp/tools/utility_tools.py +358 -0
  236. crackerjack/mcp/tools/workflow_executor.py +699 -0
  237. crackerjack/mcp/websocket/README.md +31 -0
  238. crackerjack/mcp/websocket/__init__.py +14 -0
  239. crackerjack/mcp/websocket/app.py +54 -0
  240. crackerjack/mcp/websocket/endpoints.py +492 -0
  241. crackerjack/mcp/websocket/event_bridge.py +188 -0
  242. crackerjack/mcp/websocket/jobs.py +406 -0
  243. crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
  244. crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
  245. crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
  246. crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
  247. crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
  248. crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
  249. crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
  250. crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
  251. crackerjack/mcp/websocket/monitoring/factory.py +109 -0
  252. crackerjack/mcp/websocket/monitoring/filters.py +10 -0
  253. crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
  254. crackerjack/mcp/websocket/monitoring/models.py +90 -0
  255. crackerjack/mcp/websocket/monitoring/utils.py +171 -0
  256. crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
  257. crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
  258. crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
  259. crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
  260. crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
  261. crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
  262. crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
  263. crackerjack/mcp/websocket/server.py +174 -0
  264. crackerjack/mcp/websocket/websocket_handler.py +276 -0
  265. crackerjack/mcp/websocket_server.py +10 -0
  266. crackerjack/models/README.md +308 -0
  267. crackerjack/models/__init__.py +40 -0
  268. crackerjack/models/config.py +730 -0
  269. crackerjack/models/config_adapter.py +265 -0
  270. crackerjack/models/protocols.py +1535 -0
  271. crackerjack/models/pydantic_models.py +320 -0
  272. crackerjack/models/qa_config.py +145 -0
  273. crackerjack/models/qa_results.py +134 -0
  274. crackerjack/models/resource_protocols.py +299 -0
  275. crackerjack/models/results.py +35 -0
  276. crackerjack/models/semantic_models.py +258 -0
  277. crackerjack/models/task.py +173 -0
  278. crackerjack/models/test_models.py +60 -0
  279. crackerjack/monitoring/README.md +11 -0
  280. crackerjack/monitoring/__init__.py +0 -0
  281. crackerjack/monitoring/ai_agent_watchdog.py +405 -0
  282. crackerjack/monitoring/metrics_collector.py +427 -0
  283. crackerjack/monitoring/regression_prevention.py +580 -0
  284. crackerjack/monitoring/websocket_server.py +406 -0
  285. crackerjack/orchestration/README.md +340 -0
  286. crackerjack/orchestration/__init__.py +43 -0
  287. crackerjack/orchestration/advanced_orchestrator.py +894 -0
  288. crackerjack/orchestration/cache/README.md +312 -0
  289. crackerjack/orchestration/cache/__init__.py +37 -0
  290. crackerjack/orchestration/cache/memory_cache.py +338 -0
  291. crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
  292. crackerjack/orchestration/config.py +297 -0
  293. crackerjack/orchestration/coverage_improvement.py +180 -0
  294. crackerjack/orchestration/execution_strategies.py +361 -0
  295. crackerjack/orchestration/hook_orchestrator.py +1398 -0
  296. crackerjack/orchestration/strategies/README.md +401 -0
  297. crackerjack/orchestration/strategies/__init__.py +39 -0
  298. crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
  299. crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
  300. crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
  301. crackerjack/orchestration/test_progress_streamer.py +647 -0
  302. crackerjack/plugins/README.md +11 -0
  303. crackerjack/plugins/__init__.py +15 -0
  304. crackerjack/plugins/base.py +200 -0
  305. crackerjack/plugins/hooks.py +254 -0
  306. crackerjack/plugins/loader.py +335 -0
  307. crackerjack/plugins/managers.py +264 -0
  308. crackerjack/py313.py +191 -0
  309. crackerjack/security/README.md +11 -0
  310. crackerjack/security/__init__.py +0 -0
  311. crackerjack/security/audit.py +197 -0
  312. crackerjack/services/README.md +374 -0
  313. crackerjack/services/__init__.py +9 -0
  314. crackerjack/services/ai/README.md +295 -0
  315. crackerjack/services/ai/__init__.py +7 -0
  316. crackerjack/services/ai/advanced_optimizer.py +878 -0
  317. crackerjack/services/ai/contextual_ai_assistant.py +542 -0
  318. crackerjack/services/ai/embeddings.py +444 -0
  319. crackerjack/services/ai/intelligent_commit.py +328 -0
  320. crackerjack/services/ai/predictive_analytics.py +510 -0
  321. crackerjack/services/anomaly_detector.py +392 -0
  322. crackerjack/services/api_extractor.py +617 -0
  323. crackerjack/services/backup_service.py +467 -0
  324. crackerjack/services/bounded_status_operations.py +530 -0
  325. crackerjack/services/cache.py +369 -0
  326. crackerjack/services/changelog_automation.py +399 -0
  327. crackerjack/services/command_execution_service.py +305 -0
  328. crackerjack/services/config_integrity.py +132 -0
  329. crackerjack/services/config_merge.py +546 -0
  330. crackerjack/services/config_service.py +198 -0
  331. crackerjack/services/config_template.py +493 -0
  332. crackerjack/services/coverage_badge_service.py +173 -0
  333. crackerjack/services/coverage_ratchet.py +381 -0
  334. crackerjack/services/debug.py +733 -0
  335. crackerjack/services/dependency_analyzer.py +460 -0
  336. crackerjack/services/dependency_monitor.py +622 -0
  337. crackerjack/services/documentation_generator.py +493 -0
  338. crackerjack/services/documentation_service.py +704 -0
  339. crackerjack/services/enhanced_filesystem.py +497 -0
  340. crackerjack/services/enterprise_optimizer.py +865 -0
  341. crackerjack/services/error_pattern_analyzer.py +676 -0
  342. crackerjack/services/file_filter.py +221 -0
  343. crackerjack/services/file_hasher.py +149 -0
  344. crackerjack/services/file_io_service.py +361 -0
  345. crackerjack/services/file_modifier.py +615 -0
  346. crackerjack/services/filesystem.py +381 -0
  347. crackerjack/services/git.py +422 -0
  348. crackerjack/services/health_metrics.py +615 -0
  349. crackerjack/services/heatmap_generator.py +744 -0
  350. crackerjack/services/incremental_executor.py +380 -0
  351. crackerjack/services/initialization.py +823 -0
  352. crackerjack/services/input_validator.py +668 -0
  353. crackerjack/services/intelligent_commit.py +327 -0
  354. crackerjack/services/log_manager.py +289 -0
  355. crackerjack/services/logging.py +228 -0
  356. crackerjack/services/lsp_client.py +628 -0
  357. crackerjack/services/memory_optimizer.py +414 -0
  358. crackerjack/services/metrics.py +587 -0
  359. crackerjack/services/monitoring/README.md +30 -0
  360. crackerjack/services/monitoring/__init__.py +9 -0
  361. crackerjack/services/monitoring/dependency_monitor.py +678 -0
  362. crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
  363. crackerjack/services/monitoring/health_metrics.py +716 -0
  364. crackerjack/services/monitoring/metrics.py +587 -0
  365. crackerjack/services/monitoring/performance_benchmarks.py +410 -0
  366. crackerjack/services/monitoring/performance_cache.py +388 -0
  367. crackerjack/services/monitoring/performance_monitor.py +569 -0
  368. crackerjack/services/parallel_executor.py +527 -0
  369. crackerjack/services/pattern_cache.py +333 -0
  370. crackerjack/services/pattern_detector.py +478 -0
  371. crackerjack/services/patterns/__init__.py +142 -0
  372. crackerjack/services/patterns/agents.py +107 -0
  373. crackerjack/services/patterns/code/__init__.py +15 -0
  374. crackerjack/services/patterns/code/detection.py +118 -0
  375. crackerjack/services/patterns/code/imports.py +107 -0
  376. crackerjack/services/patterns/code/paths.py +159 -0
  377. crackerjack/services/patterns/code/performance.py +119 -0
  378. crackerjack/services/patterns/code/replacement.py +36 -0
  379. crackerjack/services/patterns/core.py +212 -0
  380. crackerjack/services/patterns/documentation/__init__.py +14 -0
  381. crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
  382. crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
  383. crackerjack/services/patterns/documentation/docstrings.py +89 -0
  384. crackerjack/services/patterns/formatting.py +226 -0
  385. crackerjack/services/patterns/operations.py +339 -0
  386. crackerjack/services/patterns/security/__init__.py +23 -0
  387. crackerjack/services/patterns/security/code_injection.py +122 -0
  388. crackerjack/services/patterns/security/credentials.py +190 -0
  389. crackerjack/services/patterns/security/path_traversal.py +221 -0
  390. crackerjack/services/patterns/security/unsafe_operations.py +216 -0
  391. crackerjack/services/patterns/templates.py +62 -0
  392. crackerjack/services/patterns/testing/__init__.py +18 -0
  393. crackerjack/services/patterns/testing/error_patterns.py +107 -0
  394. crackerjack/services/patterns/testing/pytest_output.py +126 -0
  395. crackerjack/services/patterns/tool_output/__init__.py +16 -0
  396. crackerjack/services/patterns/tool_output/bandit.py +72 -0
  397. crackerjack/services/patterns/tool_output/other.py +97 -0
  398. crackerjack/services/patterns/tool_output/pyright.py +67 -0
  399. crackerjack/services/patterns/tool_output/ruff.py +44 -0
  400. crackerjack/services/patterns/url_sanitization.py +114 -0
  401. crackerjack/services/patterns/utilities.py +42 -0
  402. crackerjack/services/patterns/utils.py +339 -0
  403. crackerjack/services/patterns/validation.py +46 -0
  404. crackerjack/services/patterns/versioning.py +62 -0
  405. crackerjack/services/predictive_analytics.py +523 -0
  406. crackerjack/services/profiler.py +280 -0
  407. crackerjack/services/quality/README.md +415 -0
  408. crackerjack/services/quality/__init__.py +11 -0
  409. crackerjack/services/quality/anomaly_detector.py +392 -0
  410. crackerjack/services/quality/pattern_cache.py +333 -0
  411. crackerjack/services/quality/pattern_detector.py +479 -0
  412. crackerjack/services/quality/qa_orchestrator.py +491 -0
  413. crackerjack/services/quality/quality_baseline.py +395 -0
  414. crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
  415. crackerjack/services/quality/quality_intelligence.py +949 -0
  416. crackerjack/services/regex_patterns.py +58 -0
  417. crackerjack/services/regex_utils.py +483 -0
  418. crackerjack/services/secure_path_utils.py +524 -0
  419. crackerjack/services/secure_status_formatter.py +450 -0
  420. crackerjack/services/secure_subprocess.py +635 -0
  421. crackerjack/services/security.py +239 -0
  422. crackerjack/services/security_logger.py +495 -0
  423. crackerjack/services/server_manager.py +411 -0
  424. crackerjack/services/smart_scheduling.py +167 -0
  425. crackerjack/services/status_authentication.py +460 -0
  426. crackerjack/services/status_security_manager.py +315 -0
  427. crackerjack/services/terminal_utils.py +0 -0
  428. crackerjack/services/thread_safe_status_collector.py +441 -0
  429. crackerjack/services/tool_filter.py +368 -0
  430. crackerjack/services/tool_version_service.py +43 -0
  431. crackerjack/services/unified_config.py +115 -0
  432. crackerjack/services/validation_rate_limiter.py +220 -0
  433. crackerjack/services/vector_store.py +689 -0
  434. crackerjack/services/version_analyzer.py +461 -0
  435. crackerjack/services/version_checker.py +223 -0
  436. crackerjack/services/websocket_resource_limiter.py +438 -0
  437. crackerjack/services/zuban_lsp_service.py +391 -0
  438. crackerjack/slash_commands/README.md +11 -0
  439. crackerjack/slash_commands/__init__.py +59 -0
  440. crackerjack/slash_commands/init.md +112 -0
  441. crackerjack/slash_commands/run.md +197 -0
  442. crackerjack/slash_commands/status.md +127 -0
  443. crackerjack/tools/README.md +11 -0
  444. crackerjack/tools/__init__.py +30 -0
  445. crackerjack/tools/_git_utils.py +105 -0
  446. crackerjack/tools/check_added_large_files.py +139 -0
  447. crackerjack/tools/check_ast.py +105 -0
  448. crackerjack/tools/check_json.py +103 -0
  449. crackerjack/tools/check_jsonschema.py +297 -0
  450. crackerjack/tools/check_toml.py +103 -0
  451. crackerjack/tools/check_yaml.py +110 -0
  452. crackerjack/tools/codespell_wrapper.py +72 -0
  453. crackerjack/tools/end_of_file_fixer.py +202 -0
  454. crackerjack/tools/format_json.py +128 -0
  455. crackerjack/tools/mdformat_wrapper.py +114 -0
  456. crackerjack/tools/trailing_whitespace.py +198 -0
  457. crackerjack/tools/validate_input_validator_patterns.py +236 -0
  458. crackerjack/tools/validate_regex_patterns.py +188 -0
  459. crackerjack/ui/README.md +11 -0
  460. crackerjack/ui/__init__.py +1 -0
  461. crackerjack/ui/dashboard_renderer.py +28 -0
  462. crackerjack/ui/templates/README.md +11 -0
  463. crackerjack/utils/console_utils.py +13 -0
  464. crackerjack/utils/dependency_guard.py +230 -0
  465. crackerjack/utils/retry_utils.py +275 -0
  466. crackerjack/workflows/README.md +590 -0
  467. crackerjack/workflows/__init__.py +46 -0
  468. crackerjack/workflows/actions.py +811 -0
  469. crackerjack/workflows/auto_fix.py +444 -0
  470. crackerjack/workflows/container_builder.py +499 -0
  471. crackerjack/workflows/definitions.py +443 -0
  472. crackerjack/workflows/engine.py +177 -0
  473. crackerjack/workflows/event_bridge.py +242 -0
  474. crackerjack-0.45.2.dist-info/METADATA +1678 -0
  475. crackerjack-0.45.2.dist-info/RECORD +478 -0
  476. {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
  477. crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
  478. crackerjack/.gitignore +0 -14
  479. crackerjack/.libcst.codemod.yaml +0 -18
  480. crackerjack/.pdm.toml +0 -1
  481. crackerjack/.pre-commit-config.yaml +0 -91
  482. crackerjack/.pytest_cache/.gitignore +0 -2
  483. crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
  484. crackerjack/.pytest_cache/README.md +0 -8
  485. crackerjack/.pytest_cache/v/cache/nodeids +0 -1
  486. crackerjack/.pytest_cache/v/cache/stepwise +0 -1
  487. crackerjack/.ruff_cache/.gitignore +0 -1
  488. crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
  489. crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
  490. crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
  491. crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
  492. crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
  493. crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
  494. crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
  495. crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
  496. crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
  497. crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
  498. crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
  499. crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
  500. crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
  501. crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
  502. crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
  503. crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
  504. crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
  505. crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
  506. crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
  507. crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
  508. crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
  509. crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
  510. crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
  511. crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
  512. crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
  513. crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
  514. crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
  515. crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
  516. crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
  517. crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
  518. crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
  519. crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
  520. crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
  521. crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
  522. crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
  523. crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
  524. crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
  525. crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
  526. crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
  527. crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
  528. crackerjack/crackerjack.py +0 -855
  529. crackerjack/pyproject.toml +0 -214
  530. crackerjack-0.18.2.dist-info/METADATA +0 -420
  531. crackerjack-0.18.2.dist-info/RECORD +0 -59
  532. crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
  533. {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}"