crackerjack 0.33.0__py3-none-any.whl → 0.33.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +196 -25
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Comprehensive timeout management system for async operations.
|
|
3
|
-
|
|
4
|
-
This module provides timeout handling, circuit breaker patterns, and
|
|
5
|
-
graceful degradation for all async operations in crackerjack.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
1
|
import asyncio
|
|
9
2
|
import builtins
|
|
10
3
|
import logging
|
|
@@ -19,8 +12,6 @@ logger = logging.getLogger("crackerjack.timeout_manager")
|
|
|
19
12
|
|
|
20
13
|
|
|
21
14
|
class _DummyPerformanceMonitor:
|
|
22
|
-
"""Dummy performance monitor to avoid circular import issues."""
|
|
23
|
-
|
|
24
15
|
def record_operation_start(self, operation: str) -> float:
|
|
25
16
|
return time.time()
|
|
26
17
|
|
|
@@ -43,25 +34,19 @@ class _DummyPerformanceMonitor:
|
|
|
43
34
|
pass
|
|
44
35
|
|
|
45
36
|
def get_summary_stats(self) -> dict[str, t.Any]:
|
|
46
|
-
"""Dummy implementation."""
|
|
47
37
|
return {}
|
|
48
38
|
|
|
49
39
|
def get_all_metrics(self) -> dict[str, t.Any]:
|
|
50
|
-
"""Dummy implementation."""
|
|
51
40
|
return {}
|
|
52
41
|
|
|
53
42
|
def get_performance_alerts(self) -> list[str]:
|
|
54
|
-
"""Dummy implementation."""
|
|
55
43
|
return []
|
|
56
44
|
|
|
57
45
|
def get_recent_timeout_events(self, limit: int) -> list[t.Any]:
|
|
58
|
-
"""Dummy implementation."""
|
|
59
46
|
return []
|
|
60
47
|
|
|
61
48
|
|
|
62
49
|
class TimeoutStrategy(Enum):
|
|
63
|
-
"""Timeout handling strategies."""
|
|
64
|
-
|
|
65
50
|
FAIL_FAST = "fail_fast"
|
|
66
51
|
RETRY_WITH_BACKOFF = "retry_with_backoff"
|
|
67
52
|
CIRCUIT_BREAKER = "circuit_breaker"
|
|
@@ -69,18 +54,13 @@ class TimeoutStrategy(Enum):
|
|
|
69
54
|
|
|
70
55
|
|
|
71
56
|
class CircuitBreakerState(Enum):
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
OPEN = "open" # Failing fast
|
|
76
|
-
HALF_OPEN = "half_open" # Testing if service recovered
|
|
57
|
+
CLOSED = "closed"
|
|
58
|
+
OPEN = "open"
|
|
59
|
+
HALF_OPEN = "half_open"
|
|
77
60
|
|
|
78
61
|
|
|
79
62
|
@dataclass
|
|
80
63
|
class TimeoutConfig:
|
|
81
|
-
"""Configuration for timeout handling."""
|
|
82
|
-
|
|
83
|
-
# Basic timeout settings
|
|
84
64
|
default_timeout: float = 30.0
|
|
85
65
|
operation_timeouts: dict[str, float] = field(
|
|
86
66
|
default_factory=lambda: {
|
|
@@ -91,18 +71,16 @@ class TimeoutConfig:
|
|
|
91
71
|
"file_operations": 10.0,
|
|
92
72
|
"network_operations": 15.0,
|
|
93
73
|
"websocket_broadcast": 5.0,
|
|
94
|
-
"workflow_iteration": 900.0,
|
|
95
|
-
"complete_workflow": 3600.0,
|
|
74
|
+
"workflow_iteration": 900.0,
|
|
75
|
+
"complete_workflow": 3600.0,
|
|
96
76
|
}
|
|
97
77
|
)
|
|
98
78
|
|
|
99
|
-
# Retry configuration
|
|
100
79
|
max_retries: int = 3
|
|
101
80
|
base_retry_delay: float = 1.0
|
|
102
81
|
max_retry_delay: float = 60.0
|
|
103
82
|
backoff_multiplier: float = 2.0
|
|
104
83
|
|
|
105
|
-
# Circuit breaker configuration
|
|
106
84
|
failure_threshold: int = 5
|
|
107
85
|
recovery_timeout: float = 60.0
|
|
108
86
|
half_open_max_calls: int = 3
|
|
@@ -110,8 +88,6 @@ class TimeoutConfig:
|
|
|
110
88
|
|
|
111
89
|
@dataclass
|
|
112
90
|
class CircuitBreakerStateData:
|
|
113
|
-
"""State data for a circuit breaker."""
|
|
114
|
-
|
|
115
91
|
state: CircuitBreakerState = CircuitBreakerState.CLOSED
|
|
116
92
|
failure_count: int = 0
|
|
117
93
|
last_failure_time: float = 0.0
|
|
@@ -119,45 +95,37 @@ class CircuitBreakerStateData:
|
|
|
119
95
|
|
|
120
96
|
|
|
121
97
|
class TimeoutError(Exception):
|
|
122
|
-
"""Custom timeout error with context."""
|
|
123
|
-
|
|
124
98
|
def __init__(self, operation: str, timeout: float, elapsed: float = 0.0) -> None:
|
|
125
99
|
self.operation = operation
|
|
126
100
|
self.timeout = timeout
|
|
127
101
|
self.elapsed = elapsed
|
|
128
102
|
super().__init__(
|
|
129
103
|
f"Operation '{operation}' timed out after {timeout}s "
|
|
130
|
-
f"(elapsed: {elapsed
|
|
104
|
+
f"(elapsed: {elapsed: .1f}s)"
|
|
131
105
|
)
|
|
132
106
|
|
|
133
107
|
|
|
134
108
|
class AsyncTimeoutManager:
|
|
135
|
-
"""Comprehensive async timeout and circuit breaker manager."""
|
|
136
|
-
|
|
137
109
|
def __init__(self, config: TimeoutConfig | None = None) -> None:
|
|
138
110
|
self.config = config or TimeoutConfig()
|
|
139
111
|
self.circuit_breakers: dict[str, CircuitBreakerStateData] = {}
|
|
140
112
|
self.operation_stats: dict[str, list[float]] = {}
|
|
141
113
|
|
|
142
|
-
|
|
143
|
-
self._performance_monitor = None
|
|
114
|
+
self._performance_monitor: t.Any = None
|
|
144
115
|
|
|
145
116
|
def get_timeout(self, operation: str) -> float:
|
|
146
|
-
"""Get timeout for specific operation."""
|
|
147
117
|
return self.config.operation_timeouts.get(
|
|
148
118
|
operation, self.config.default_timeout
|
|
149
119
|
)
|
|
150
120
|
|
|
151
121
|
@property
|
|
152
|
-
def performance_monitor(self):
|
|
153
|
-
"""Lazy-load performance monitor to avoid circular imports."""
|
|
122
|
+
def performance_monitor(self) -> t.Any:
|
|
154
123
|
if self._performance_monitor is None:
|
|
155
124
|
try:
|
|
156
125
|
from .performance_monitor import get_performance_monitor
|
|
157
126
|
|
|
158
127
|
self._performance_monitor = get_performance_monitor()
|
|
159
128
|
except ImportError:
|
|
160
|
-
# If performance monitor is not available, create a dummy implementation
|
|
161
129
|
self._performance_monitor = _DummyPerformanceMonitor()
|
|
162
130
|
return self._performance_monitor
|
|
163
131
|
|
|
@@ -168,24 +136,20 @@ class AsyncTimeoutManager:
|
|
|
168
136
|
timeout: float | None = None,
|
|
169
137
|
strategy: TimeoutStrategy = TimeoutStrategy.FAIL_FAST,
|
|
170
138
|
) -> t.AsyncIterator[None]:
|
|
171
|
-
"""Context manager for timeout handling with strategy."""
|
|
172
139
|
timeout_value = timeout or self.get_timeout(operation)
|
|
173
140
|
start_time = self.performance_monitor.record_operation_start(operation)
|
|
174
141
|
|
|
175
|
-
|
|
176
|
-
if timeout_value > 7200.0: # 2 hours maximum
|
|
142
|
+
if timeout_value > 7200.0:
|
|
177
143
|
logger.warning(
|
|
178
144
|
f"Capping excessive timeout for {operation}: {timeout_value}s -> 7200s"
|
|
179
145
|
)
|
|
180
146
|
timeout_value = 7200.0
|
|
181
147
|
|
|
182
148
|
try:
|
|
183
|
-
# Use asyncio.timeout for Python 3.11+ or asyncio.wait_for fallback
|
|
184
149
|
try:
|
|
185
150
|
async with asyncio.timeout(timeout_value):
|
|
186
151
|
yield
|
|
187
152
|
except AttributeError:
|
|
188
|
-
# Fallback for older Python versions without asyncio.timeout
|
|
189
153
|
task = asyncio.current_task()
|
|
190
154
|
if task:
|
|
191
155
|
try:
|
|
@@ -196,10 +160,8 @@ class AsyncTimeoutManager:
|
|
|
196
160
|
except builtins.TimeoutError:
|
|
197
161
|
raise builtins.TimeoutError(f"Operation {operation} timed out")
|
|
198
162
|
else:
|
|
199
|
-
# Direct yield if no current task context
|
|
200
163
|
yield
|
|
201
164
|
|
|
202
|
-
# Record successful operation
|
|
203
165
|
self.performance_monitor.record_operation_success(operation, start_time)
|
|
204
166
|
elapsed = time.time() - start_time
|
|
205
167
|
self._record_success(operation, elapsed)
|
|
@@ -214,12 +176,11 @@ class AsyncTimeoutManager:
|
|
|
214
176
|
if strategy == TimeoutStrategy.CIRCUIT_BREAKER:
|
|
215
177
|
self._update_circuit_breaker(operation, False)
|
|
216
178
|
|
|
217
|
-
# Handle graceful degradation
|
|
218
179
|
if strategy == TimeoutStrategy.GRACEFUL_DEGRADATION:
|
|
219
180
|
logger.warning(
|
|
220
|
-
f"Operation {operation} timed out ({elapsed
|
|
181
|
+
f"Operation {operation} timed out ({elapsed: .1f}s), continuing gracefully"
|
|
221
182
|
)
|
|
222
|
-
return
|
|
183
|
+
return
|
|
223
184
|
|
|
224
185
|
raise TimeoutError(operation, timeout_value, elapsed) from e
|
|
225
186
|
except Exception:
|
|
@@ -239,7 +200,6 @@ class AsyncTimeoutManager:
|
|
|
239
200
|
timeout: float | None = None,
|
|
240
201
|
strategy: TimeoutStrategy = TimeoutStrategy.FAIL_FAST,
|
|
241
202
|
) -> t.Any:
|
|
242
|
-
"""Execute coroutine with timeout and strategy."""
|
|
243
203
|
if strategy == TimeoutStrategy.CIRCUIT_BREAKER:
|
|
244
204
|
if not self._check_circuit_breaker(operation):
|
|
245
205
|
raise TimeoutError(operation, 0.0, 0.0)
|
|
@@ -248,10 +208,8 @@ class AsyncTimeoutManager:
|
|
|
248
208
|
start_time = self.performance_monitor.record_operation_start(operation)
|
|
249
209
|
|
|
250
210
|
try:
|
|
251
|
-
# Wrap coroutine execution with timeout protection
|
|
252
211
|
result = await asyncio.wait_for(coro, timeout=timeout_value)
|
|
253
212
|
|
|
254
|
-
# Record success
|
|
255
213
|
self.performance_monitor.record_operation_success(operation, start_time)
|
|
256
214
|
elapsed = time.time() - start_time
|
|
257
215
|
self._record_success(operation, elapsed)
|
|
@@ -271,10 +229,9 @@ class AsyncTimeoutManager:
|
|
|
271
229
|
if strategy == TimeoutStrategy.CIRCUIT_BREAKER:
|
|
272
230
|
self._update_circuit_breaker(operation, False)
|
|
273
231
|
|
|
274
|
-
# Handle graceful degradation
|
|
275
232
|
if strategy == TimeoutStrategy.GRACEFUL_DEGRADATION:
|
|
276
233
|
logger.warning(
|
|
277
|
-
f"Operation {operation} timed out ({elapsed
|
|
234
|
+
f"Operation {operation} timed out ({elapsed: .1f}s), returning None"
|
|
278
235
|
)
|
|
279
236
|
return None
|
|
280
237
|
|
|
@@ -296,14 +253,12 @@ class AsyncTimeoutManager:
|
|
|
296
253
|
coro_factory: t.Callable[[], t.Awaitable[t.Any]],
|
|
297
254
|
timeout: float | None = None,
|
|
298
255
|
) -> t.Any:
|
|
299
|
-
"""Execute with exponential backoff retry."""
|
|
300
256
|
last_exception = None
|
|
301
257
|
delay = self.config.base_retry_delay
|
|
302
258
|
|
|
303
259
|
for attempt in range(self.config.max_retries + 1):
|
|
304
260
|
try:
|
|
305
261
|
async with self.timeout_context(operation, timeout):
|
|
306
|
-
# Create a new coroutine for each attempt
|
|
307
262
|
return await coro_factory()
|
|
308
263
|
except (TimeoutError, Exception) as e:
|
|
309
264
|
last_exception = e
|
|
@@ -326,7 +281,6 @@ class AsyncTimeoutManager:
|
|
|
326
281
|
raise RuntimeError(f"No attempts made for operation: {operation}")
|
|
327
282
|
|
|
328
283
|
def _check_circuit_breaker(self, operation: str) -> bool:
|
|
329
|
-
"""Check if circuit breaker allows operation."""
|
|
330
284
|
if operation not in self.circuit_breakers:
|
|
331
285
|
self.circuit_breakers[operation] = CircuitBreakerStateData()
|
|
332
286
|
return True
|
|
@@ -342,14 +296,13 @@ class AsyncTimeoutManager:
|
|
|
342
296
|
breaker.half_open_calls = 0
|
|
343
297
|
return True
|
|
344
298
|
return False
|
|
345
|
-
else:
|
|
299
|
+
else:
|
|
346
300
|
if breaker.half_open_calls < self.config.half_open_max_calls:
|
|
347
301
|
breaker.half_open_calls += 1
|
|
348
302
|
return True
|
|
349
303
|
return False
|
|
350
304
|
|
|
351
305
|
def _update_circuit_breaker(self, operation: str, success: bool) -> None:
|
|
352
|
-
"""Update circuit breaker state based on operation result."""
|
|
353
306
|
if operation not in self.circuit_breakers:
|
|
354
307
|
self.circuit_breakers[operation] = CircuitBreakerStateData()
|
|
355
308
|
|
|
@@ -369,21 +322,18 @@ class AsyncTimeoutManager:
|
|
|
369
322
|
if breaker.failure_count >= self.config.failure_threshold:
|
|
370
323
|
breaker.state = CircuitBreakerState.OPEN
|
|
371
324
|
|
|
372
|
-
# Record circuit breaker event if state changed
|
|
373
325
|
if previous_state != CircuitBreakerState.OPEN:
|
|
374
326
|
self.performance_monitor.record_circuit_breaker_event(
|
|
375
327
|
operation, True
|
|
376
328
|
)
|
|
377
329
|
|
|
378
330
|
def _record_success(self, operation: str, elapsed: float) -> None:
|
|
379
|
-
"""Record successful operation timing."""
|
|
380
331
|
if operation not in self.operation_stats:
|
|
381
332
|
self.operation_stats[operation] = []
|
|
382
333
|
|
|
383
334
|
stats = self.operation_stats[operation]
|
|
384
335
|
stats.append(elapsed)
|
|
385
336
|
|
|
386
|
-
# Keep only recent stats (last 100 operations)
|
|
387
337
|
if len(stats) > 100:
|
|
388
338
|
stats.pop(0)
|
|
389
339
|
|
|
@@ -391,14 +341,12 @@ class AsyncTimeoutManager:
|
|
|
391
341
|
self._update_circuit_breaker(operation, True)
|
|
392
342
|
|
|
393
343
|
def _record_failure(self, operation: str, elapsed: float) -> None:
|
|
394
|
-
"""Record failed operation timing."""
|
|
395
344
|
logger.warning(
|
|
396
|
-
f"Operation '{operation}' failed after {elapsed
|
|
345
|
+
f"Operation '{operation}' failed after {elapsed: .1f}s "
|
|
397
346
|
f"(timeout: {self.get_timeout(operation)}s)"
|
|
398
347
|
)
|
|
399
348
|
|
|
400
349
|
def get_stats(self, operation: str) -> dict[str, t.Any]:
|
|
401
|
-
"""Get performance statistics for operation."""
|
|
402
350
|
stats = self.operation_stats.get(operation, [])
|
|
403
351
|
if not stats:
|
|
404
352
|
return {
|
|
@@ -431,8 +379,6 @@ def timeout_async(
|
|
|
431
379
|
) -> t.Callable[
|
|
432
380
|
[t.Callable[..., t.Awaitable[t.Any]]], t.Callable[..., t.Awaitable[t.Any]]
|
|
433
381
|
]:
|
|
434
|
-
"""Decorator for async functions with timeout handling."""
|
|
435
|
-
|
|
436
382
|
def decorator(
|
|
437
383
|
func: t.Callable[..., t.Awaitable[t.Any]],
|
|
438
384
|
) -> t.Callable[..., t.Awaitable[t.Any]]:
|
|
@@ -448,12 +394,10 @@ def timeout_async(
|
|
|
448
394
|
return decorator
|
|
449
395
|
|
|
450
396
|
|
|
451
|
-
# Global timeout manager instance
|
|
452
397
|
_global_timeout_manager: AsyncTimeoutManager | None = None
|
|
453
398
|
|
|
454
399
|
|
|
455
400
|
def get_timeout_manager() -> AsyncTimeoutManager:
|
|
456
|
-
"""Get global timeout manager instance."""
|
|
457
401
|
global _global_timeout_manager
|
|
458
402
|
if _global_timeout_manager is None:
|
|
459
403
|
_global_timeout_manager = AsyncTimeoutManager()
|
|
@@ -461,13 +405,11 @@ def get_timeout_manager() -> AsyncTimeoutManager:
|
|
|
461
405
|
|
|
462
406
|
|
|
463
407
|
def configure_timeouts(config: TimeoutConfig) -> None:
|
|
464
|
-
"""Configure global timeout manager."""
|
|
465
408
|
global _global_timeout_manager
|
|
466
409
|
_global_timeout_manager = AsyncTimeoutManager(config)
|
|
467
410
|
|
|
468
411
|
|
|
469
412
|
def get_performance_report() -> dict[str, t.Any]:
|
|
470
|
-
"""Get comprehensive performance report."""
|
|
471
413
|
timeout_manager = get_timeout_manager()
|
|
472
414
|
monitor = timeout_manager.performance_monitor
|
|
473
415
|
|