crackerjack 0.33.0__py3-none-any.whl → 0.33.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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,3 @@
1
- """
2
- Thread-safe status collection to prevent race conditions.
3
-
4
- Provides synchronized status data collection with proper locking,
5
- atomic operations, and consistency guarantees.
6
- """
7
-
8
1
  import asyncio
9
2
  import json
10
3
  import threading
@@ -18,12 +11,10 @@ from .security_logger import SecurityEventLevel, SecurityEventType, get_security
18
11
 
19
12
  @dataclass
20
13
  class StatusSnapshot:
21
- """Thread-safe snapshot of system status."""
22
-
23
- services: dict[str, t.Any] = field(default_factory=dict)
24
- jobs: dict[str, t.Any] = field(default_factory=dict)
25
- server_stats: dict[str, t.Any] = field(default_factory=dict)
26
- agent_suggestions: dict[str, t.Any] = field(default_factory=dict)
14
+ services: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
15
+ jobs: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
16
+ server_stats: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
17
+ agent_suggestions: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
27
18
  timestamp: float = field(default_factory=time.time)
28
19
  collection_duration: float = 0.0
29
20
  is_complete: bool = False
@@ -31,43 +22,22 @@ class StatusSnapshot:
31
22
 
32
23
 
33
24
  class ThreadSafeStatusCollector:
34
- """
35
- Thread-safe status collector with race condition prevention.
36
-
37
- Features:
38
- - Thread-safe data collection and aggregation
39
- - Atomic status updates with consistency guarantees
40
- - Deadlock prevention with ordered locking
41
- - Timeout protection for all operations
42
- - Error isolation and recovery
43
- """
44
-
45
- def __init__(self, timeout: float = 30.0):
46
- """
47
- Initialize thread-safe status collector.
48
-
49
- Args:
50
- timeout: Maximum time for status collection operations
51
- """
25
+ def __init__(self, timeout: float = 30.0) -> None:
52
26
  self.timeout = timeout
53
27
  self.security_logger = get_security_logger()
54
28
 
55
- # Thread safety primitives with ordered locking to prevent deadlocks
56
- self._collection_lock = threading.RLock() # Main collection lock
57
- self._data_lock = threading.RLock() # Data structure lock
58
- self._file_lock = threading.RLock() # File operation lock
29
+ self._collection_lock = threading.RLock()
30
+ self._data_lock = threading.RLock()
31
+ self._file_lock = threading.RLock()
59
32
 
60
- # Status collection state
61
33
  self._current_snapshot: StatusSnapshot | None = None
62
34
  self._collection_in_progress = False
63
35
  self._collection_start_time = 0.0
64
36
 
65
- # Cached data with expiration
66
37
  self._cache: dict[str, t.Any] = {}
67
38
  self._cache_timestamps: dict[str, float] = {}
68
- self._cache_ttl = 5.0 # 5 second cache TTL
39
+ self._cache_ttl = 5.0
69
40
 
70
- # Thread-local storage for per-thread state
71
41
  self._local = threading.local()
72
42
 
73
43
  async def collect_comprehensive_status(
@@ -77,32 +47,12 @@ class ThreadSafeStatusCollector:
77
47
  include_services: bool = True,
78
48
  include_stats: bool = True,
79
49
  ) -> StatusSnapshot:
80
- """
81
- Collect comprehensive system status with thread safety.
82
-
83
- Args:
84
- client_id: Client identifier for logging
85
- include_jobs: Include job information
86
- include_services: Include service information
87
- include_stats: Include server statistics
88
-
89
- Returns:
90
- StatusSnapshot with collected data
91
-
92
- Raises:
93
- TimeoutError: If collection takes too long
94
- RuntimeError: If collection fails due to concurrency issues
95
- """
96
-
97
- # Use async context manager for proper cleanup
98
50
  async with self._collection_context(client_id):
99
51
  start_time = time.time()
100
52
 
101
53
  try:
102
- # Create new snapshot
103
54
  snapshot = StatusSnapshot(timestamp=start_time)
104
55
 
105
- # Collect data components in parallel with timeouts
106
56
  collection_tasks = []
107
57
 
108
58
  if include_services:
@@ -120,21 +70,18 @@ class ThreadSafeStatusCollector:
120
70
  self._collect_server_stats(client_id, snapshot)
121
71
  )
122
72
 
123
- # Wait for all collections to complete with timeout
124
73
  await asyncio.wait_for(
125
74
  asyncio.gather(*collection_tasks, return_exceptions=True),
126
75
  timeout=self.timeout,
127
76
  )
128
77
 
129
- # Finalize snapshot
130
78
  snapshot.collection_duration = time.time() - start_time
131
79
  snapshot.is_complete = True
132
80
 
133
- # Log successful collection
134
81
  self.security_logger.log_security_event(
135
82
  event_type=SecurityEventType.STATUS_COLLECTED,
136
83
  level=SecurityEventLevel.INFO,
137
- message=f"Status collection completed in {snapshot.collection_duration:.2f}s",
84
+ message=f"Status collection completed in {snapshot.collection_duration: .2f}s",
138
85
  client_id=client_id,
139
86
  operation="collect_status",
140
87
  additional_data={
@@ -169,7 +116,6 @@ class ThreadSafeStatusCollector:
169
116
  additional_data={"error": str(e)},
170
117
  )
171
118
 
172
- # Return partial snapshot with error information
173
119
  snapshot = StatusSnapshot(
174
120
  timestamp=start_time,
175
121
  collection_duration=time.time() - start_time,
@@ -180,16 +126,12 @@ class ThreadSafeStatusCollector:
180
126
  return snapshot
181
127
 
182
128
  @asynccontextmanager
183
- async def _collection_context(self, client_id: str):
184
- """Context manager for status collection with proper cleanup."""
185
-
186
- # Acquire collection lock with timeout
129
+ async def _collection_context(self, client_id: str) -> t.AsyncGenerator[None]:
187
130
  collection_acquired = False
188
131
  start_wait = time.time()
189
132
 
190
133
  try:
191
- # Try to acquire collection lock
192
- while time.time() - start_wait < 5.0: # 5 second wait limit
134
+ while time.time() - start_wait < 5.0:
193
135
  with self._collection_lock:
194
136
  if not self._collection_in_progress:
195
137
  self._collection_in_progress = True
@@ -213,7 +155,6 @@ class ThreadSafeStatusCollector:
213
155
  yield
214
156
 
215
157
  finally:
216
- # Always release the collection lock
217
158
  if collection_acquired:
218
159
  with self._collection_lock:
219
160
  self._collection_in_progress = False
@@ -232,23 +173,18 @@ class ThreadSafeStatusCollector:
232
173
  client_id: str,
233
174
  snapshot: StatusSnapshot,
234
175
  ) -> None:
235
- """Collect services data with thread safety."""
236
-
237
176
  try:
238
- # Check cache first
239
177
  cached_data = self._get_cached_data("services")
240
178
  if cached_data is not None:
241
179
  with self._data_lock:
242
180
  snapshot.services = cached_data
243
181
  return
244
182
 
245
- # Import here to avoid circular dependencies
246
183
  from crackerjack.services.server_manager import (
247
184
  find_mcp_server_processes,
248
185
  find_websocket_server_processes,
249
186
  )
250
187
 
251
- # Collect process data with timeout
252
188
  mcp_task = asyncio.create_task(asyncio.to_thread(find_mcp_server_processes))
253
189
  websocket_task = asyncio.create_task(
254
190
  asyncio.to_thread(find_websocket_server_processes)
@@ -270,7 +206,6 @@ class ThreadSafeStatusCollector:
270
206
  },
271
207
  }
272
208
 
273
- # Atomically update snapshot and cache
274
209
  with self._data_lock:
275
210
  snapshot.services = services_data
276
211
  self._set_cached_data("services", services_data)
@@ -286,17 +221,13 @@ class ThreadSafeStatusCollector:
286
221
  client_id: str,
287
222
  snapshot: StatusSnapshot,
288
223
  ) -> None:
289
- """Collect jobs data with thread safety."""
290
-
291
224
  try:
292
- # Check cache first
293
225
  cached_data = self._get_cached_data("jobs")
294
226
  if cached_data is not None:
295
227
  with self._data_lock:
296
228
  snapshot.jobs = cached_data
297
229
  return
298
230
 
299
- # Get active jobs with file locking
300
231
  active_jobs = await self._get_active_jobs_safe()
301
232
 
302
233
  jobs_data = {
@@ -312,7 +243,6 @@ class ThreadSafeStatusCollector:
312
243
  "details": active_jobs,
313
244
  }
314
245
 
315
- # Atomically update snapshot and cache
316
246
  with self._data_lock:
317
247
  snapshot.jobs = jobs_data
318
248
  self._set_cached_data("jobs", jobs_data)
@@ -328,14 +258,11 @@ class ThreadSafeStatusCollector:
328
258
  client_id: str,
329
259
  snapshot: StatusSnapshot,
330
260
  ) -> None:
331
- """Collect server statistics with thread safety."""
332
-
333
261
  try:
334
- # Get context safely
335
262
  from crackerjack.mcp.context import get_context
336
263
 
337
264
  try:
338
- context = get_context()
265
+ context: MCPServerContext | None = get_context()
339
266
  except RuntimeError:
340
267
  context = None
341
268
 
@@ -344,14 +271,12 @@ class ThreadSafeStatusCollector:
344
271
  snapshot.server_stats = {"error": "Server context not available"}
345
272
  return
346
273
 
347
- # Build stats with timeout protection
348
274
  stats_task = asyncio.create_task(
349
275
  asyncio.to_thread(self._build_server_stats_safe, context)
350
276
  )
351
277
 
352
278
  server_stats = await asyncio.wait_for(stats_task, timeout=5.0)
353
279
 
354
- # Atomically update snapshot
355
280
  with self._data_lock:
356
281
  snapshot.server_stats = server_stats
357
282
 
@@ -362,11 +287,8 @@ class ThreadSafeStatusCollector:
362
287
  snapshot.server_stats = {"error": error_msg}
363
288
 
364
289
  async def _get_active_jobs_safe(self) -> list[dict[str, t.Any]]:
365
- """Get active jobs with file system synchronization."""
290
+ jobs: list[dict[str, t.Any]] = []
366
291
 
367
- jobs = []
368
-
369
- # Use file lock to prevent race conditions during file reading
370
292
  with self._file_lock:
371
293
  try:
372
294
  from crackerjack.mcp.context import get_context
@@ -375,14 +297,11 @@ class ThreadSafeStatusCollector:
375
297
  if not context or not context.progress_dir.exists():
376
298
  return jobs
377
299
 
378
- # Read job files with error handling
379
300
  for progress_file in context.progress_dir.glob("job-*.json"):
380
301
  try:
381
- # Use atomic read with timeout
382
302
  content = progress_file.read_text(encoding="utf-8")
383
303
  progress_data = json.loads(content)
384
304
 
385
- # Validate required fields
386
305
  job_data = {
387
306
  "job_id": progress_data.get("job_id", "unknown"),
388
307
  "status": progress_data.get("status", "unknown"),
@@ -403,7 +322,6 @@ class ThreadSafeStatusCollector:
403
322
  jobs.append(job_data)
404
323
 
405
324
  except (json.JSONDecodeError, OSError, UnicodeDecodeError) as e:
406
- # Log file read error but continue processing other files
407
325
  self.security_logger.log_security_event(
408
326
  event_type=SecurityEventType.FILE_READ_ERROR,
409
327
  level=SecurityEventLevel.WARNING,
@@ -423,8 +341,6 @@ class ThreadSafeStatusCollector:
423
341
  return jobs
424
342
 
425
343
  def _build_server_stats_safe(self, context: t.Any) -> dict[str, t.Any]:
426
- """Build server stats with thread safety."""
427
-
428
344
  try:
429
345
  stats = {
430
346
  "server_info": {
@@ -442,7 +358,9 @@ class ThreadSafeStatusCollector:
442
358
  else None,
443
359
  },
444
360
  "resource_usage": {
445
- "temp_files_count": len(list(context.progress_dir.glob("*.json")))
361
+ "temp_files_count": len(
362
+ list[t.Any](context.progress_dir.glob("*.json"))
363
+ )
446
364
  if context.progress_dir.exists()
447
365
  else 0,
448
366
  "progress_dir": str(context.progress_dir),
@@ -450,7 +368,6 @@ class ThreadSafeStatusCollector:
450
368
  "timestamp": time.time(),
451
369
  }
452
370
 
453
- # Add state manager stats if available
454
371
  state_manager = getattr(context, "state_manager", None)
455
372
  if state_manager:
456
373
  stats["state_manager"] = {
@@ -465,35 +382,28 @@ class ThreadSafeStatusCollector:
465
382
  return {"error": f"Failed to build server stats: {e}"}
466
383
 
467
384
  def _get_cached_data(self, key: str) -> dict[str, t.Any] | None:
468
- """Get cached data if still valid."""
469
-
470
385
  current_time = time.time()
471
386
 
472
387
  with self._data_lock:
473
388
  if key in self._cache and key in self._cache_timestamps:
474
389
  cache_age = current_time - self._cache_timestamps[key]
475
390
  if cache_age < self._cache_ttl:
476
- return self._cache[key]
391
+ cached_result: dict[str, t.Any] = self._cache[key]
392
+ return cached_result
477
393
 
478
394
  return None
479
395
 
480
396
  def _set_cached_data(self, key: str, data: dict[str, t.Any]) -> None:
481
- """Set cached data with timestamp."""
482
-
483
397
  with self._data_lock:
484
398
  self._cache[key] = data.copy() if hasattr(data, "copy") else data
485
399
  self._cache_timestamps[key] = time.time()
486
400
 
487
401
  def clear_cache(self) -> None:
488
- """Clear all cached data."""
489
-
490
402
  with self._data_lock:
491
403
  self._cache.clear()
492
404
  self._cache_timestamps.clear()
493
405
 
494
406
  def get_collection_status(self) -> dict[str, t.Any]:
495
- """Get current collection status and metrics."""
496
-
497
407
  with self._collection_lock:
498
408
  return {
499
409
  "collection_in_progress": self._collection_in_progress,
@@ -505,13 +415,10 @@ class ThreadSafeStatusCollector:
505
415
  }
506
416
 
507
417
 
508
- # Global singleton instance
509
418
  _status_collector: ThreadSafeStatusCollector | None = None
510
419
 
511
420
 
512
421
  def get_thread_safe_status_collector() -> ThreadSafeStatusCollector:
513
- """Get the global thread-safe status collector instance."""
514
-
515
422
  global _status_collector
516
423
  if _status_collector is None:
517
424
  _status_collector = ThreadSafeStatusCollector()
@@ -524,19 +431,6 @@ async def collect_secure_status(
524
431
  include_services: bool = True,
525
432
  include_stats: bool = True,
526
433
  ) -> StatusSnapshot:
527
- """
528
- Convenience function for secure status collection.
529
-
530
- Args:
531
- client_id: Client identifier for logging
532
- include_jobs: Include job information
533
- include_services: Include service information
534
- include_stats: Include server statistics
535
-
536
- Returns:
537
- StatusSnapshot with collected data
538
- """
539
-
540
434
  collector = get_thread_safe_status_collector()
541
435
  return await collector.collect_comprehensive_status(
542
436
  client_id=client_id,
@@ -36,8 +36,6 @@ class CrackerjackConfig(BaseModel):
36
36
  skip_hooks: bool = False
37
37
  experimental_hooks: bool = False
38
38
 
39
- # Removed unused configuration fields: performance_tracking, benchmark_mode, publish_enabled, keyring_provider
40
-
41
39
  batch_file_operations: bool = True
42
40
  file_operation_batch_size: int = 10
43
41
 
@@ -48,7 +46,8 @@ class CrackerjackConfig(BaseModel):
48
46
  def validate_package_path(cls, v: Any) -> Path:
49
47
  if isinstance(v, str):
50
48
  v = Path(v)
51
- return v.resolve()
49
+ resolved_path: Path = v.resolve()
50
+ return resolved_path
52
51
 
53
52
  @field_validator("log_file", mode="before")
54
53
  @classmethod
@@ -56,8 +55,10 @@ class CrackerjackConfig(BaseModel):
56
55
  if v is None:
57
56
  return v
58
57
  if isinstance(v, str):
59
- v = Path(v)
60
- return v
58
+ path_v: Path = Path(v)
59
+ return path_v
60
+ validated_v: Path = t.cast(Path, v)
61
+ return validated_v
61
62
 
62
63
  @field_validator("test_workers")
63
64
  @classmethod
@@ -119,7 +120,6 @@ class EnvironmentConfigSource(ConfigSource):
119
120
  if value.lower() in ("false", "0", "no", "off"):
120
121
  return False
121
122
 
122
- # Handle negative numbers with spaces (e.g., "- 10")
123
123
  cleaned_value = value.replace(" ", "")
124
124
 
125
125
  with suppress(ValueError):
@@ -186,9 +186,13 @@ class PyprojectConfigSource(ConfigSource):
186
186
  with self.pyproject_path.open("rb") as f:
187
187
  pyproject_data = tomllib.load(f)
188
188
 
189
- config = pyproject_data.get("tool", {}).get("crackerjack", {})
189
+ config: dict[str, t.Any] = pyproject_data.get("tool", {}).get(
190
+ "crackerjack", {}
191
+ )
190
192
 
191
- self.logger.debug("Loaded pyproject config", keys=list(config.keys()))
193
+ self.logger.debug(
194
+ "Loaded pyproject config", keys=list[t.Any](config.keys())
195
+ )
192
196
  return config
193
197
 
194
198
  except ImportError:
@@ -197,9 +201,13 @@ class PyprojectConfigSource(ConfigSource):
197
201
 
198
202
  with self.pyproject_path.open("rb") as f:
199
203
  pyproject_data = tomllib.load(f)
200
- config = pyproject_data.get("tool", {}).get("crackerjack", {})
201
- self.logger.debug("Loaded pyproject config", keys=list(config.keys()))
202
- return config
204
+ config_data: dict[str, t.Any] = pyproject_data.get("tool", {}).get(
205
+ "crackerjack", {}
206
+ )
207
+ self.logger.debug(
208
+ "Loaded pyproject config", keys=list[t.Any](config_data.keys())
209
+ )
210
+ return config_data
203
211
  except ImportError:
204
212
  self.logger.warning(
205
213
  "No TOML library available for pyproject.toml parsing",
@@ -236,7 +244,7 @@ class OptionsConfigSource(ConfigSource):
236
244
  if value is not None:
237
245
  config[config_key] = value
238
246
 
239
- self.logger.debug("Loaded options config", keys=list(config.keys()))
247
+ self.logger.debug("Loaded options config", keys=list[t.Any](config.keys()))
240
248
  return config
241
249
 
242
250
 
@@ -304,7 +312,7 @@ class UnifiedConfigurationService:
304
312
  "Merged config from source",
305
313
  source_type=type(source).__name__,
306
314
  priority=source.priority,
307
- keys=list(source_config.keys()),
315
+ keys=list[t.Any](source_config.keys()),
308
316
  )
309
317
  except Exception as e:
310
318
  self.logger.exception(
@@ -1,12 +1,3 @@
1
- """
2
- Rate limiting for input validation to prevent abuse and DoS attacks.
3
-
4
- This module provides rate limiting functionality to:
5
- - Prevent excessive validation failures
6
- - Protect against DoS attacks via malformed input
7
- - Track validation patterns for security monitoring
8
- """
9
-
10
1
  import time
11
2
  import typing as t
12
3
  from collections import defaultdict, deque
@@ -16,13 +7,11 @@ from .security_logger import SecurityEventLevel, get_security_logger
16
7
 
17
8
 
18
9
  class ValidationRateLimit:
19
- """Rate limiting configuration for different validation types."""
20
-
21
10
  def __init__(
22
11
  self,
23
12
  max_failures: int = 10,
24
13
  window_seconds: int = 60,
25
- block_duration: int = 300, # 5 minutes
14
+ block_duration: int = 300,
26
15
  ):
27
16
  self.max_failures = max_failures
28
17
  self.window_seconds = window_seconds
@@ -30,20 +19,12 @@ class ValidationRateLimit:
30
19
 
31
20
 
32
21
  class ValidationRateLimiter:
33
- """
34
- Rate limiter for validation failures to prevent abuse.
35
-
36
- Tracks validation failures by client/source and blocks excessive failures.
37
- Uses sliding window approach for accurate rate limiting.
38
- """
39
-
40
- def __init__(self):
22
+ def __init__(self) -> None:
41
23
  self._failure_windows: dict[str, deque[float]] = defaultdict(deque)
42
24
  self._blocked_until: dict[str, float] = {}
43
25
  self._lock = Lock()
44
26
  self._logger = get_security_logger()
45
27
 
46
- # Default rate limits by validation type
47
28
  self._limits = {
48
29
  "default": ValidationRateLimit(),
49
30
  "command_injection": ValidationRateLimit(
@@ -58,13 +39,11 @@ class ValidationRateLimiter:
58
39
  }
59
40
 
60
41
  def is_blocked(self, client_id: str) -> bool:
61
- """Check if a client is currently blocked."""
62
42
  with self._lock:
63
43
  if client_id in self._blocked_until:
64
44
  if time.time() < self._blocked_until[client_id]:
65
45
  return True
66
46
  else:
67
- # Block expired, remove it
68
47
  del self._blocked_until[client_id]
69
48
  return False
70
49
 
@@ -74,35 +53,23 @@ class ValidationRateLimiter:
74
53
  validation_type: str,
75
54
  severity: SecurityEventLevel = SecurityEventLevel.MEDIUM,
76
55
  ) -> bool:
77
- """
78
- Record a validation failure and check if client should be blocked.
79
-
80
- Returns True if the client should be blocked, False otherwise.
81
- """
82
56
  with self._lock:
83
57
  current_time = time.time()
84
58
 
85
- # Get rate limit for this validation type
86
59
  limit = self._limits.get(validation_type, self._limits["default"])
87
60
 
88
- # Initialize failure window if needed
89
61
  if client_id not in self._failure_windows:
90
62
  self._failure_windows[client_id] = deque()
91
63
 
92
- # Clean old failures outside the window
93
64
  window = self._failure_windows[client_id]
94
65
  while window and current_time - window[0] > limit.window_seconds:
95
66
  window.popleft()
96
67
 
97
- # Record this failure
98
68
  window.append(current_time)
99
69
 
100
- # Check if limit exceeded
101
70
  if len(window) >= limit.max_failures:
102
- # Block this client
103
71
  self._blocked_until[client_id] = current_time + limit.block_duration
104
72
 
105
- # Log the rate limit exceeded event
106
73
  self._logger.log_rate_limit_exceeded(
107
74
  limit_type=validation_type,
108
75
  current_count=len(window),
@@ -111,7 +78,6 @@ class ValidationRateLimiter:
111
78
  block_duration=limit.block_duration,
112
79
  )
113
80
 
114
- # Clear the failure window since client is now blocked
115
81
  self._failure_windows[client_id].clear()
116
82
 
117
83
  return True
@@ -119,7 +85,6 @@ class ValidationRateLimiter:
119
85
  return False
120
86
 
121
87
  def get_remaining_attempts(self, client_id: str, validation_type: str) -> int:
122
- """Get remaining validation attempts before rate limit."""
123
88
  with self._lock:
124
89
  if self.is_blocked(client_id):
125
90
  return 0
@@ -129,7 +94,6 @@ class ValidationRateLimiter:
129
94
  return max(0, limit.max_failures - current_failures)
130
95
 
131
96
  def get_block_time_remaining(self, client_id: str) -> int:
132
- """Get seconds remaining until client is unblocked."""
133
97
  with self._lock:
134
98
  if client_id in self._blocked_until:
135
99
  remaining = max(0, int(self._blocked_until[client_id] - time.time()))
@@ -137,7 +101,6 @@ class ValidationRateLimiter:
137
101
  return 0
138
102
 
139
103
  def get_client_stats(self, client_id: str) -> dict[str, t.Any]:
140
- """Get rate limiting statistics for a client."""
141
104
  with self._lock:
142
105
  current_time = time.time()
143
106
 
@@ -153,7 +116,6 @@ class ValidationRateLimiter:
153
116
  window = self._failure_windows[client_id]
154
117
  stats["total_failures"] = len(window)
155
118
 
156
- # Count recent failures (last 5 minutes)
157
119
  recent_count = sum(
158
120
  1 for failure_time in window if current_time - failure_time <= 300
159
121
  )
@@ -162,12 +124,10 @@ class ValidationRateLimiter:
162
124
  return stats
163
125
 
164
126
  def cleanup_expired_data(self) -> int:
165
- """Clean up expired data and return number of items removed."""
166
127
  with self._lock:
167
128
  current_time = time.time()
168
129
  removed_count = 0
169
130
 
170
- # Clean expired blocks
171
131
  expired_blocks = [
172
132
  client_id
173
133
  for client_id, block_until in self._blocked_until.items()
@@ -178,14 +138,11 @@ class ValidationRateLimiter:
178
138
  del self._blocked_until[client_id]
179
139
  removed_count += 1
180
140
 
181
- # Clean old failure windows (keep only last 24 hours)
182
- for client_id, window in list(self._failure_windows.items()):
183
- # Remove failures older than 24 hours
184
- while window and current_time - window[0] > 86400: # 24 hours
141
+ for client_id, window in list[t.Any](self._failure_windows.items()):
142
+ while window and current_time - window[0] > 86400:
185
143
  window.popleft()
186
144
  removed_count += 1
187
145
 
188
- # Remove empty windows
189
146
  if not window:
190
147
  del self._failure_windows[client_id]
191
148
 
@@ -198,7 +155,6 @@ class ValidationRateLimiter:
198
155
  window_seconds: int,
199
156
  block_duration: int,
200
157
  ) -> None:
201
- """Update rate limits for a specific validation type."""
202
158
  with self._lock:
203
159
  self._limits[validation_type] = ValidationRateLimit(
204
160
  max_failures=max_failures,
@@ -207,7 +163,6 @@ class ValidationRateLimiter:
207
163
  )
208
164
 
209
165
  def get_all_stats(self) -> dict[str, t.Any]:
210
- """Get comprehensive rate limiting statistics."""
211
166
  with self._lock:
212
167
  current_time = time.time()
213
168
 
@@ -226,7 +181,6 @@ class ValidationRateLimiter:
226
181
  "active_clients": [],
227
182
  }
228
183
 
229
- # Get blocked client info
230
184
  for client_id, block_until in self._blocked_until.items():
231
185
  remaining = max(0, int(block_until - current_time))
232
186
  stats["blocked_clients"].append(
@@ -237,13 +191,12 @@ class ValidationRateLimiter:
237
191
  }
238
192
  )
239
193
 
240
- # Get active client info (with recent failures)
241
194
  for client_id, window in self._failure_windows.items():
242
195
  if client_id not in self._blocked_until and window:
243
196
  recent_failures = sum(
244
197
  1
245
198
  for failure_time in window
246
- if current_time - failure_time <= 300 # Last 5 minutes
199
+ if current_time - failure_time <= 300
247
200
  )
248
201
  if recent_failures > 0:
249
202
  stats["active_clients"].append(
@@ -257,12 +210,10 @@ class ValidationRateLimiter:
257
210
  return stats
258
211
 
259
212
 
260
- # Global rate limiter instance
261
213
  _rate_limiter: ValidationRateLimiter | None = None
262
214
 
263
215
 
264
216
  def get_validation_rate_limiter() -> ValidationRateLimiter:
265
- """Get the global validation rate limiter instance."""
266
217
  global _rate_limiter
267
218
  if _rate_limiter is None:
268
219
  _rate_limiter = ValidationRateLimiter()