crackerjack 0.31.10__py3-none-any.whl → 0.31.12__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/CLAUDE.md +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +281 -94
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +343 -209
- crackerjack/dynamic_config.py +47 -6
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +17 -63
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +44 -73
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +71 -47
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +276 -428
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/METADATA +197 -12
- crackerjack-0.31.12.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.10.dist-info/RECORD +0 -149
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"""Resource lifecycle management protocols.
|
|
2
|
+
|
|
3
|
+
Defines protocols and interfaces for comprehensive resource management
|
|
4
|
+
patterns throughout the crackerjack codebase.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import typing as t
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from types import TracebackType
|
|
10
|
+
|
|
11
|
+
if t.TYPE_CHECKING:
|
|
12
|
+
import asyncio
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AsyncCleanupProtocol(t.Protocol):
|
|
17
|
+
"""Protocol for resources that support async cleanup."""
|
|
18
|
+
|
|
19
|
+
async def cleanup(self) -> None:
|
|
20
|
+
"""Clean up the resource asynchronously."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SyncCleanupProtocol(t.Protocol):
|
|
25
|
+
"""Protocol for resources that support synchronous cleanup."""
|
|
26
|
+
|
|
27
|
+
def cleanup(self) -> None:
|
|
28
|
+
"""Clean up the resource synchronously."""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ResourceLifecycleProtocol(t.Protocol):
|
|
33
|
+
"""Protocol for resources with full lifecycle management."""
|
|
34
|
+
|
|
35
|
+
async def initialize(self) -> None:
|
|
36
|
+
"""Initialize the resource."""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
async def cleanup(self) -> None:
|
|
40
|
+
"""Clean up the resource."""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
def is_initialized(self) -> bool:
|
|
44
|
+
"""Check if the resource is initialized."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
def is_closed(self) -> bool:
|
|
48
|
+
"""Check if the resource is closed."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AsyncContextProtocol(t.Protocol):
|
|
53
|
+
"""Protocol for async context managers."""
|
|
54
|
+
|
|
55
|
+
async def __aenter__(self) -> t.Self:
|
|
56
|
+
"""Enter async context."""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
async def __aexit__(
|
|
60
|
+
self,
|
|
61
|
+
exc_type: type[BaseException] | None,
|
|
62
|
+
exc_val: BaseException | None,
|
|
63
|
+
exc_tb: TracebackType | None,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Exit async context."""
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ResourceManagerProtocol(t.Protocol):
|
|
70
|
+
"""Protocol for resource managers."""
|
|
71
|
+
|
|
72
|
+
def register_resource(self, resource: AsyncCleanupProtocol) -> None:
|
|
73
|
+
"""Register a resource for management."""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def register_cleanup_callback(
|
|
77
|
+
self, callback: t.Callable[[], t.Awaitable[None]]
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Register a cleanup callback."""
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
async def cleanup_all(self) -> None:
|
|
83
|
+
"""Clean up all managed resources."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class FileResourceProtocol(t.Protocol):
|
|
88
|
+
"""Protocol for file-based resources."""
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def path(self) -> "Path":
|
|
92
|
+
"""Get the file path."""
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
def exists(self) -> bool:
|
|
96
|
+
"""Check if the file exists."""
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
async def cleanup(self) -> None:
|
|
100
|
+
"""Clean up the file resource."""
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class ProcessResourceProtocol(t.Protocol):
|
|
105
|
+
"""Protocol for process-based resources."""
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def pid(self) -> int:
|
|
109
|
+
"""Get the process ID."""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
def is_running(self) -> bool:
|
|
113
|
+
"""Check if the process is running."""
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
async def cleanup(self) -> None:
|
|
117
|
+
"""Clean up the process resource."""
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class TaskResourceProtocol(t.Protocol):
|
|
122
|
+
"""Protocol for asyncio task resources."""
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def task(self) -> "asyncio.Task[t.Any]":
|
|
126
|
+
"""Get the asyncio task."""
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
def is_done(self) -> bool:
|
|
130
|
+
"""Check if the task is done."""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
def is_cancelled(self) -> bool:
|
|
134
|
+
"""Check if the task is cancelled."""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
async def cleanup(self) -> None:
|
|
138
|
+
"""Clean up the task resource."""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class NetworkResourceProtocol(t.Protocol):
|
|
143
|
+
"""Protocol for network-based resources."""
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def is_connected(self) -> bool:
|
|
147
|
+
"""Check if the resource is connected."""
|
|
148
|
+
...
|
|
149
|
+
|
|
150
|
+
async def disconnect(self) -> None:
|
|
151
|
+
"""Disconnect the resource."""
|
|
152
|
+
...
|
|
153
|
+
|
|
154
|
+
async def cleanup(self) -> None:
|
|
155
|
+
"""Clean up the network resource."""
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class CacheResourceProtocol(t.Protocol):
|
|
160
|
+
"""Protocol for cache-based resources."""
|
|
161
|
+
|
|
162
|
+
def clear(self) -> None:
|
|
163
|
+
"""Clear the cache."""
|
|
164
|
+
...
|
|
165
|
+
|
|
166
|
+
def get_size(self) -> int:
|
|
167
|
+
"""Get the cache size."""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
async def cleanup(self) -> None:
|
|
171
|
+
"""Clean up the cache resource."""
|
|
172
|
+
...
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# Abstract base classes for resource implementations
|
|
176
|
+
class AbstractManagedResource(ABC):
|
|
177
|
+
"""Abstract base class for managed resources."""
|
|
178
|
+
|
|
179
|
+
def __init__(self) -> None:
|
|
180
|
+
self._initialized = False
|
|
181
|
+
self._closed = False
|
|
182
|
+
|
|
183
|
+
@abstractmethod
|
|
184
|
+
async def _do_initialize(self) -> None:
|
|
185
|
+
"""Perform resource initialization. Override in subclasses."""
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
@abstractmethod
|
|
189
|
+
async def _do_cleanup(self) -> None:
|
|
190
|
+
"""Perform resource cleanup. Override in subclasses."""
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
async def initialize(self) -> None:
|
|
194
|
+
"""Initialize the resource if not already initialized."""
|
|
195
|
+
if self._initialized:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
await self._do_initialize()
|
|
200
|
+
self._initialized = True
|
|
201
|
+
except Exception:
|
|
202
|
+
self._closed = True
|
|
203
|
+
raise
|
|
204
|
+
|
|
205
|
+
async def cleanup(self) -> None:
|
|
206
|
+
"""Clean up the resource if not already closed."""
|
|
207
|
+
if self._closed:
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
self._closed = True
|
|
211
|
+
try:
|
|
212
|
+
await self._do_cleanup()
|
|
213
|
+
except Exception:
|
|
214
|
+
# Log but don't re-raise during cleanup
|
|
215
|
+
import logging
|
|
216
|
+
|
|
217
|
+
logging.getLogger(__name__).warning(
|
|
218
|
+
f"Error during cleanup of {self.__class__.__name__}", exc_info=True
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def is_initialized(self) -> bool:
|
|
222
|
+
"""Check if the resource is initialized."""
|
|
223
|
+
return self._initialized
|
|
224
|
+
|
|
225
|
+
def is_closed(self) -> bool:
|
|
226
|
+
"""Check if the resource is closed."""
|
|
227
|
+
return self._closed
|
|
228
|
+
|
|
229
|
+
async def __aenter__(self) -> t.Self:
|
|
230
|
+
"""Enter async context and initialize."""
|
|
231
|
+
await self.initialize()
|
|
232
|
+
return self
|
|
233
|
+
|
|
234
|
+
async def __aexit__(
|
|
235
|
+
self,
|
|
236
|
+
exc_type: type[BaseException] | None,
|
|
237
|
+
exc_val: BaseException | None,
|
|
238
|
+
exc_tb: TracebackType | None,
|
|
239
|
+
) -> None:
|
|
240
|
+
"""Exit async context and cleanup."""
|
|
241
|
+
await self.cleanup()
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class AbstractFileResource(AbstractManagedResource):
|
|
245
|
+
"""Abstract base class for file-based resources."""
|
|
246
|
+
|
|
247
|
+
def __init__(self, path: "Path") -> None:
|
|
248
|
+
super().__init__()
|
|
249
|
+
self._path = path
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def path(self) -> "Path":
|
|
253
|
+
"""Get the file path."""
|
|
254
|
+
return self._path
|
|
255
|
+
|
|
256
|
+
def exists(self) -> bool:
|
|
257
|
+
"""Check if the file exists."""
|
|
258
|
+
return self._path.exists()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AbstractProcessResource(AbstractManagedResource):
|
|
262
|
+
"""Abstract base class for process-based resources."""
|
|
263
|
+
|
|
264
|
+
def __init__(self, pid: int) -> None:
|
|
265
|
+
super().__init__()
|
|
266
|
+
self._pid = pid
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def pid(self) -> int:
|
|
270
|
+
"""Get the process ID."""
|
|
271
|
+
return self._pid
|
|
272
|
+
|
|
273
|
+
def is_running(self) -> bool:
|
|
274
|
+
"""Check if the process is running."""
|
|
275
|
+
try:
|
|
276
|
+
import os
|
|
277
|
+
|
|
278
|
+
os.kill(self._pid, 0)
|
|
279
|
+
return True
|
|
280
|
+
except (OSError, ProcessLookupError):
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class AbstractTaskResource(AbstractManagedResource):
|
|
285
|
+
"""Abstract base class for asyncio task resources."""
|
|
286
|
+
|
|
287
|
+
def __init__(self, task: "asyncio.Task[t.Any]") -> None:
|
|
288
|
+
super().__init__()
|
|
289
|
+
self._task = task
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def task(self) -> "asyncio.Task[t.Any]":
|
|
293
|
+
"""Get the asyncio task."""
|
|
294
|
+
return self._task
|
|
295
|
+
|
|
296
|
+
def is_done(self) -> bool:
|
|
297
|
+
"""Check if the task is done."""
|
|
298
|
+
return self._task.done()
|
|
299
|
+
|
|
300
|
+
def is_cancelled(self) -> bool:
|
|
301
|
+
"""Check if the task is cancelled."""
|
|
302
|
+
return self._task.cancelled()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class AbstractNetworkResource(AbstractManagedResource):
|
|
306
|
+
"""Abstract base class for network-based resources."""
|
|
307
|
+
|
|
308
|
+
def __init__(self) -> None:
|
|
309
|
+
super().__init__()
|
|
310
|
+
self._connected = False
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def is_connected(self) -> bool:
|
|
314
|
+
"""Check if the resource is connected."""
|
|
315
|
+
return self._connected and not self._closed
|
|
316
|
+
|
|
317
|
+
async def disconnect(self) -> None:
|
|
318
|
+
"""Disconnect the resource."""
|
|
319
|
+
if self._connected:
|
|
320
|
+
self._connected = False
|
|
321
|
+
await self._do_disconnect()
|
|
322
|
+
|
|
323
|
+
@abstractmethod
|
|
324
|
+
async def _do_disconnect(self) -> None:
|
|
325
|
+
"""Perform disconnection. Override in subclasses."""
|
|
326
|
+
pass
|
|
327
|
+
|
|
328
|
+
async def _do_cleanup(self) -> None:
|
|
329
|
+
"""Cleanup includes disconnection."""
|
|
330
|
+
await self.disconnect()
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# Resource lifecycle decorators
|
|
334
|
+
def with_resource_cleanup(resource_attr: str):
|
|
335
|
+
"""Decorator to ensure resource cleanup on method exit."""
|
|
336
|
+
|
|
337
|
+
def decorator(func: t.Callable[..., t.Awaitable[t.Any]]):
|
|
338
|
+
async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
339
|
+
resource = getattr(self, resource_attr, None)
|
|
340
|
+
try:
|
|
341
|
+
return await func(self, *args, **kwargs)
|
|
342
|
+
finally:
|
|
343
|
+
if resource and hasattr(resource, "cleanup"):
|
|
344
|
+
await resource.cleanup()
|
|
345
|
+
|
|
346
|
+
return wrapper
|
|
347
|
+
|
|
348
|
+
return decorator
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def ensure_initialized(resource_attr: str):
|
|
352
|
+
"""Decorator to ensure resource is initialized before method execution."""
|
|
353
|
+
|
|
354
|
+
def decorator(func: t.Callable[..., t.Awaitable[t.Any]]):
|
|
355
|
+
async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
356
|
+
resource = getattr(self, resource_attr, None)
|
|
357
|
+
if resource and hasattr(resource, "initialize"):
|
|
358
|
+
await resource.initialize()
|
|
359
|
+
return await func(self, *args, **kwargs)
|
|
360
|
+
|
|
361
|
+
return wrapper
|
|
362
|
+
|
|
363
|
+
return decorator
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# Resource health monitoring protocols
|
|
367
|
+
class HealthCheckProtocol(t.Protocol):
|
|
368
|
+
"""Protocol for health checking resources."""
|
|
369
|
+
|
|
370
|
+
async def health_check(self) -> dict[str, t.Any]:
|
|
371
|
+
"""Perform health check and return status."""
|
|
372
|
+
...
|
|
373
|
+
|
|
374
|
+
def is_healthy(self) -> bool:
|
|
375
|
+
"""Quick health check."""
|
|
376
|
+
...
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class MonitorableResourceProtocol(t.Protocol):
|
|
380
|
+
"""Protocol for resources that can be monitored."""
|
|
381
|
+
|
|
382
|
+
def get_metrics(self) -> dict[str, t.Any]:
|
|
383
|
+
"""Get resource metrics."""
|
|
384
|
+
...
|
|
385
|
+
|
|
386
|
+
def get_status(self) -> str:
|
|
387
|
+
"""Get resource status string."""
|
|
388
|
+
...
|
|
389
|
+
|
|
390
|
+
async def health_check(self) -> dict[str, t.Any]:
|
|
391
|
+
"""Perform health check."""
|
|
392
|
+
...
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
# Resource factory protocols
|
|
396
|
+
class ResourceFactoryProtocol(t.Protocol):
|
|
397
|
+
"""Protocol for resource factories."""
|
|
398
|
+
|
|
399
|
+
async def create_resource(self, **kwargs: t.Any) -> AsyncCleanupProtocol:
|
|
400
|
+
"""Create a new resource instance."""
|
|
401
|
+
...
|
|
402
|
+
|
|
403
|
+
def get_resource_type(self) -> str:
|
|
404
|
+
"""Get the resource type name."""
|
|
405
|
+
...
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class PooledResourceProtocol(t.Protocol):
|
|
409
|
+
"""Protocol for pooled resources."""
|
|
410
|
+
|
|
411
|
+
async def acquire(self) -> AsyncCleanupProtocol:
|
|
412
|
+
"""Acquire a resource from the pool."""
|
|
413
|
+
...
|
|
414
|
+
|
|
415
|
+
async def release(self, resource: AsyncCleanupProtocol) -> None:
|
|
416
|
+
"""Release a resource back to the pool."""
|
|
417
|
+
...
|
|
418
|
+
|
|
419
|
+
def get_pool_size(self) -> int:
|
|
420
|
+
"""Get current pool size."""
|
|
421
|
+
...
|
|
422
|
+
|
|
423
|
+
def get_active_count(self) -> int:
|
|
424
|
+
"""Get count of active resources."""
|
|
425
|
+
...
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# Error handling protocols
|
|
429
|
+
class ResourceErrorProtocol(t.Protocol):
|
|
430
|
+
"""Protocol for resource error handling."""
|
|
431
|
+
|
|
432
|
+
def handle_error(self, error: Exception) -> bool:
|
|
433
|
+
"""Handle resource error. Return True if error was handled."""
|
|
434
|
+
...
|
|
435
|
+
|
|
436
|
+
def should_retry(self, error: Exception) -> bool:
|
|
437
|
+
"""Check if operation should be retried on this error."""
|
|
438
|
+
...
|
|
439
|
+
|
|
440
|
+
def get_retry_delay(self, attempt: int) -> float:
|
|
441
|
+
"""Get delay before retry attempt."""
|
|
442
|
+
...
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
class FallbackResourceProtocol(t.Protocol):
|
|
446
|
+
"""Protocol for resources with fallback capabilities."""
|
|
447
|
+
|
|
448
|
+
async def get_fallback(self) -> AsyncCleanupProtocol | None:
|
|
449
|
+
"""Get fallback resource if primary fails."""
|
|
450
|
+
...
|
|
451
|
+
|
|
452
|
+
def has_fallback(self) -> bool:
|
|
453
|
+
"""Check if fallback is available."""
|
|
454
|
+
...
|
crackerjack/models/task.py
CHANGED
|
@@ -95,7 +95,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
|
|
|
95
95
|
)
|
|
96
96
|
self.tasks[task_id] = task
|
|
97
97
|
self.current_task = task_id
|
|
98
|
-
self.console.print(f"[yellow]⏳[/yellow] Started: {task_name}")
|
|
98
|
+
self.console.print(f"[yellow]⏳[/ yellow] Started: {task_name}")
|
|
99
99
|
|
|
100
100
|
def complete_task(
|
|
101
101
|
self,
|
|
@@ -112,7 +112,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
|
|
|
112
112
|
task.details = details
|
|
113
113
|
if files_changed:
|
|
114
114
|
task.files_changed = files_changed
|
|
115
|
-
self.console.print(f"[green]✅[/green] Completed: {task.name}")
|
|
115
|
+
self.console.print(f"[green]✅[/ green] Completed: {task.name}")
|
|
116
116
|
if self.current_task == task_id:
|
|
117
117
|
self.current_task = None
|
|
118
118
|
|
|
@@ -131,7 +131,7 @@ class SessionTracker(BaseModel, arbitrary_types_allowed=True):
|
|
|
131
131
|
if details:
|
|
132
132
|
task.details = details
|
|
133
133
|
self.console.print(
|
|
134
|
-
f"[red]❌[/red] Failed: {task.name} - {error_message}",
|
|
134
|
+
f"[red]❌[/ red] Failed: {task.name} - {error_message}",
|
|
135
135
|
)
|
|
136
136
|
if self.current_task == task_id:
|
|
137
137
|
self.current_task = None
|
|
File without changes
|