coding-client 1.21.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 (109) hide show
  1. coding_client/__init__.py +1 -0
  2. coding_client/api/__init__.py +19 -0
  3. coding_client/api/exception_handlers.py +158 -0
  4. coding_client/api/logging_setup.py +44 -0
  5. coding_client/api/metrics.py +40 -0
  6. coding_client/api/openapi_config.py +124 -0
  7. coding_client/api/request_logging.py +60 -0
  8. coding_client/api/validation.py +150 -0
  9. coding_client/auth/__init__.py +78 -0
  10. coding_client/auth/_default_tenant.py +132 -0
  11. coding_client/auth/_header_trust.py +98 -0
  12. coding_client/auth/_onbehalf.py +315 -0
  13. coding_client/auth/_trusted_networks.py +94 -0
  14. coding_client/auth/dependencies.py +61 -0
  15. coding_client/auth/middleware.py +445 -0
  16. coding_client/auth/models.py +83 -0
  17. coding_client/auth/outbound.py +137 -0
  18. coding_client/auth/rbac.py +21 -0
  19. coding_client/auth/rbac_coding.py +5 -0
  20. coding_client/auth/tenant.py +650 -0
  21. coding_client/auth/websocket.py +148 -0
  22. coding_client/clients/__init__.py +24 -0
  23. coding_client/clients/base.py +131 -0
  24. coding_client/clients/coding_ai.py +33 -0
  25. coding_client/clients/coding_analysis.py +36 -0
  26. coding_client/clients/coding_containers.py +45 -0
  27. coding_client/clients/coding_github.py +127 -0
  28. coding_client/clients/coding_realtime.py +127 -0
  29. coding_client/clients/coding_security.py +51 -0
  30. coding_client/clients/coding_testing.py +31 -0
  31. coding_client/clients/coding_workspaces.py +265 -0
  32. coding_client/config/__init__.py +12 -0
  33. coding_client/config/base_settings.py +61 -0
  34. coding_client/config/yaml_config_loader.py +172 -0
  35. coding_client/db/__init__.py +42 -0
  36. coding_client/db/db_types.py +122 -0
  37. coding_client/db/exceptions.py +59 -0
  38. coding_client/db/postgres_connection.py +572 -0
  39. coding_client/db/tenant_query_helper.py +417 -0
  40. coding_client/db_init.py +49 -0
  41. coding_client/exceptions/__init__.py +67 -0
  42. coding_client/exceptions/api_exceptions.py +395 -0
  43. coding_client/exceptions/pydantic_ai_exceptions.py +533 -0
  44. coding_client/interfaces/__init__.py +22 -0
  45. coding_client/interfaces/cache_interface.py +179 -0
  46. coding_client/interfaces/container_interface.py +49 -0
  47. coding_client/interfaces/docker_interface.py +283 -0
  48. coding_client/interfaces/graph_interface.py +28 -0
  49. coding_client/interfaces/security_interface.py +72 -0
  50. coding_client/interfaces/webhook_interface.py +67 -0
  51. coding_client/llm/__init__.py +54 -0
  52. coding_client/llm/base_agent.py +195 -0
  53. coding_client/llm/context.py +121 -0
  54. coding_client/llm/responses.py +102 -0
  55. coding_client/llm/routing.py +82 -0
  56. coding_client/llm/settings.py +102 -0
  57. coding_client/models/__init__.py +62 -0
  58. coding_client/models/agent.py +38 -0
  59. coding_client/models/ai_test_failure.py +745 -0
  60. coding_client/models/analysis.py +330 -0
  61. coding_client/models/architecture.py +685 -0
  62. coding_client/models/bug.py +128 -0
  63. coding_client/models/code_graph.py +317 -0
  64. coding_client/models/container.py +241 -0
  65. coding_client/models/core.py +878 -0
  66. coding_client/models/docker_container.py +104 -0
  67. coding_client/models/github.py +293 -0
  68. coding_client/models/logging.py +29 -0
  69. coding_client/models/package_manager.py +257 -0
  70. coding_client/models/pr_validation.py +440 -0
  71. coding_client/models/pydantic_ai.py +395 -0
  72. coding_client/models/repo_scanner.py +249 -0
  73. coding_client/models/responses.py +431 -0
  74. coding_client/models/security.py +185 -0
  75. coding_client/models/static_analysis.py +508 -0
  76. coding_client/models/system.py +48 -0
  77. coding_client/models/test_executor.py +204 -0
  78. coding_client/models/testing.py +673 -0
  79. coding_client/models/testing_config.py +200 -0
  80. coding_client/models/validation.py +127 -0
  81. coding_client/models_init.py +272 -0
  82. coding_client/telemetry.py +195 -0
  83. coding_client/testing.py +180 -0
  84. coding_client/types/__init__.py +130 -0
  85. coding_client/types/constraint_types.py +276 -0
  86. coding_client/types/core_types.py +112 -0
  87. coding_client/types/state_machine.py +443 -0
  88. coding_client/utils/__init__.py +86 -0
  89. coding_client/utils/archive_utils.py +387 -0
  90. coding_client/utils/ast_debug.py +45 -0
  91. coding_client/utils/async_timeout.py +364 -0
  92. coding_client/utils/constants.py +518 -0
  93. coding_client/utils/content_processing.py +326 -0
  94. coding_client/utils/database.py +916 -0
  95. coding_client/utils/encoding_helper.py +222 -0
  96. coding_client/utils/enum_helpers.py +166 -0
  97. coding_client/utils/file_operations.py +177 -0
  98. coding_client/utils/filesystem_ops.py +528 -0
  99. coding_client/utils/http_client.py +562 -0
  100. coding_client/utils/path_utils.py +471 -0
  101. coding_client/utils/profiling.py +421 -0
  102. coding_client/utils/response_builder.py +240 -0
  103. coding_client/utils/retry_helper.py +712 -0
  104. coding_client/utils/security_validation.py +423 -0
  105. coding_client-1.21.2.dist-info/METADATA +234 -0
  106. coding_client-1.21.2.dist-info/RECORD +109 -0
  107. coding_client-1.21.2.dist-info/WHEEL +5 -0
  108. coding_client-1.21.2.dist-info/licenses/LICENSE +201 -0
  109. coding_client-1.21.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,364 @@
1
+ """
2
+ Async timeout utilities for handling long-running operations.
3
+
4
+ This module provides comprehensive timeout management for async operations,
5
+ including decorators, context managers, and task management utilities.
6
+ It helps prevent operations from hanging indefinitely and provides
7
+ proactive timeout handling with detailed error reporting.
8
+
9
+ Key Components:
10
+ - AsyncTimeoutError: Custom HTTP timeout exception for API operations
11
+ - run_with_timeout: Utility function to run coroutines with timeout
12
+ - with_timeout: Decorator for adding timeout to async functions
13
+ - AsyncTaskManager: Manager for handling background async tasks
14
+
15
+ Timeout Features:
16
+ - Automatic cleanup of timed-out operations
17
+ - Detailed error messages with timeout duration
18
+ - Background task management with status tracking
19
+ - Task cancellation and result retrieval
20
+ - HTTP-compatible timeout exceptions
21
+
22
+ Usage:
23
+ >>> @with_timeout(30.0, "Analysis timed out")
24
+ >>> async def analyze_code(repo_path):
25
+ >>> # Long-running analysis code
26
+ >>> pass
27
+ >>>
28
+ >>> # Or use directly
29
+ >>> result = await run_with_timeout(some_coro(), 60.0)
30
+ >>>
31
+ >>> # Task management
32
+ >>> task_id = task_manager.submit_task("analysis", analyze_code(path))
33
+ >>> status = task_manager.get_task_status(task_id)
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import asyncio
39
+ import functools
40
+ import logging
41
+ from collections.abc import Awaitable, Callable
42
+ from typing import Any, TypeVar
43
+
44
+ from fastapi import HTTPException, status
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+ T = TypeVar("T")
49
+
50
+
51
+ class AsyncTimeoutError(HTTPException):
52
+ """Custom timeout exception for API operations.
53
+
54
+ This exception is raised when async operations exceed their configured
55
+ timeout duration. It extends HTTPException to provide HTTP-compatible
56
+ error responses with 408 Request Timeout status code.
57
+
58
+ Attributes:
59
+ status_code: HTTP status code (408 Request Timeout)
60
+ detail: Error message including timeout duration
61
+ timeout_seconds: The timeout duration that was exceeded
62
+ """
63
+
64
+ def __init__(self, detail: str, timeout_seconds: float):
65
+ """
66
+ Initialize a timeout error with context information.
67
+
68
+ Args:
69
+ detail: Human-readable error detail message describing the operation
70
+ timeout_seconds: The timeout duration in seconds that was exceeded
71
+
72
+ Example:
73
+ >>> raise AsyncTimeoutError("Database query timed out", 30.0)
74
+ # Results in HTTP 408 with message: "Database query timed out (timeout: 30.0s)"
75
+ """
76
+ super().__init__(
77
+ status_code=status.HTTP_408_REQUEST_TIMEOUT,
78
+ detail=f"{detail} (timeout: {timeout_seconds}s)",
79
+ )
80
+ self.timeout_seconds = timeout_seconds
81
+
82
+
83
+ async def run_with_timeout[T](
84
+ coro: Awaitable[T],
85
+ timeout_seconds: float,
86
+ timeout_message: str = "Operation timed out",
87
+ ) -> T:
88
+ """
89
+ Run an async coroutine with a timeout.
90
+
91
+ Wraps any awaitable with a timeout mechanism, automatically canceling
92
+ the operation if it exceeds the specified duration. This prevents
93
+ operations from hanging indefinitely and provides predictable behavior.
94
+
95
+ Args:
96
+ coro: The coroutine (awaitable) to run with timeout protection
97
+ timeout_seconds: Maximum time to wait in seconds (can be fractional)
98
+ timeout_message: Custom error message if timeout occurs
99
+
100
+ Returns:
101
+ The result of the coroutine if completed within timeout
102
+
103
+ Raises:
104
+ AsyncTimeoutError: If the operation times out, with HTTP 408 status
105
+ Any other exception: Propagated from the original coroutine
106
+
107
+ Example:
108
+ >>> result = await run_with_timeout(
109
+ ... expensive_operation(),
110
+ ... 30.0,
111
+ ... "Code analysis timed out"
112
+ ... )
113
+
114
+ Note:
115
+ The timeout is enforced using asyncio.wait_for, which will
116
+ cancel the underlying coroutine if it doesn't complete in time.
117
+ This ensures proper cleanup of resources.
118
+ """
119
+ try:
120
+ return await asyncio.wait_for(coro, timeout=timeout_seconds)
121
+ except TimeoutError:
122
+ logger.warning(f"Operation timed out after {timeout_seconds}s: {timeout_message}")
123
+ raise AsyncTimeoutError(timeout_message, timeout_seconds)
124
+
125
+
126
+ def with_timeout(timeout_seconds: float, timeout_message: str | None = None):
127
+ """
128
+ Decorator to add timeout to async functions.
129
+
130
+ This decorator wraps async functions with automatic timeout handling,
131
+ preventing them from running indefinitely. It preserves the original
132
+ function signature and docstring while adding timeout protection.
133
+
134
+ Args:
135
+ timeout_seconds: Maximum time to wait in seconds (can be fractional)
136
+ timeout_message: Optional custom timeout message (defaults to function name)
137
+
138
+ Returns:
139
+ Decorator function that wraps async functions with timeout protection
140
+
141
+ Usage:
142
+ >>> @with_timeout(30.0, "Graph initialization timed out")
143
+ >>> async def initialize_graph(repo_path: str) -> GraphResult:
144
+ >>> # Long-running graph initialization
145
+ >>> return await build_graph(repo_path)
146
+ >>>
147
+ >>> # Function automatically times out after 30 seconds
148
+ >>> result = await initialize_graph("/path/to/repo")
149
+
150
+ Note:
151
+ The decorator preserves the original function's type hints and
152
+ metadata using functools.wraps. The timeout is applied to each
153
+ individual function call, not globally.
154
+ """
155
+
156
+ def decorator(
157
+ func: Callable[..., Awaitable[T]],
158
+ ) -> Callable[..., Awaitable[T]]:
159
+ func_name = getattr(func, "__name__", repr(func))
160
+
161
+ @functools.wraps(func)
162
+ async def wrapper(*args: Any, **kwargs: Any) -> T:
163
+ message = timeout_message or f"{func_name} timed out"
164
+ return await run_with_timeout(func(*args, **kwargs), timeout_seconds, message)
165
+
166
+ return wrapper
167
+
168
+ return decorator
169
+
170
+
171
+ class AsyncTaskManager:
172
+ """Manager for handling long-running async tasks.
173
+
174
+ This class provides comprehensive task management capabilities for
175
+ async operations, including task submission, status tracking, result
176
+ retrieval, and cancellation. It's designed for scenarios where you
177
+ need to run async operations in the background and check their
178
+ status later.
179
+
180
+ Features:
181
+ - Background task execution with unique task IDs
182
+ - Status tracking (running, completed, failed, not_found)
183
+ - Result storage and retrieval
184
+ - Error handling and logging
185
+ - Task cancellation support
186
+ - Automatic cleanup of completed tasks
187
+
188
+ Attributes:
189
+ tasks: Dictionary mapping task IDs to active asyncio.Task objects
190
+ results: Dictionary storing results of completed tasks
191
+ errors: Dictionary storing error messages from failed tasks
192
+
193
+ Usage:
194
+ >>> manager = AsyncTaskManager()
195
+ >>> task_id = manager.submit_task("analysis", analyze_code(path))
196
+ >>> # Check status later
197
+ >>> status = manager.get_task_status(task_id)
198
+ >>> if status["status"] == "completed":
199
+ >>> result = status["result"]
200
+ """
201
+
202
+ def __init__(self):
203
+ """
204
+ Initialize the task manager with empty task tracking dictionaries.
205
+
206
+ Creates empty dictionaries for tracking active tasks, completed
207
+ results, and error messages. All task state is maintained in
208
+ memory and will be lost when the manager is destroyed.
209
+ """
210
+ self.tasks: dict[str, asyncio.Task] = {}
211
+ self.results: dict[str, Any] = {}
212
+ self.errors: dict[str, str] = {}
213
+
214
+ def submit_task(self, task_id: str, coro: Awaitable[Any]) -> str:
215
+ """
216
+ Submit a task for background execution.
217
+
218
+ Creates a new asyncio task for the given coroutine and tracks it
219
+ under the specified task ID. The task runs in the background and
220
+ its result or error will be stored for later retrieval.
221
+
222
+ Args:
223
+ task_id: Unique identifier for the task (must not already exist)
224
+ coro: The coroutine to execute in the background
225
+
226
+ Returns:
227
+ The task ID (same as input, for convenience)
228
+
229
+ Raises:
230
+ ValueError: If a task with the given ID already exists
231
+
232
+ Example:
233
+ >>> manager = AsyncTaskManager()
234
+ >>> task_id = manager.submit_task("scan_123", scan_repository(repo_path))
235
+ >>> # Task is now running in background
236
+ >>> print(f"Submitted task: {task_id}")
237
+ """
238
+ if task_id in self.tasks:
239
+ raise ValueError(f"Task {task_id} already exists")
240
+
241
+ task = asyncio.create_task(self._run_task(task_id, coro))
242
+ self.tasks[task_id] = task
243
+ return task_id
244
+
245
+ async def _run_task(self, task_id: str, coro: Awaitable[Any]) -> None:
246
+ """
247
+ Run a task and store its result or error.
248
+
249
+ Internal method that executes the task coroutine and handles
250
+ both successful completion and error cases. Results are stored
251
+ in the appropriate dictionaries and the task is cleaned up
252
+ from the active tasks list.
253
+
254
+ Args:
255
+ task_id: Unique identifier for the task being executed
256
+ coro: The coroutine to execute
257
+
258
+ Note:
259
+ This method is called internally by submit_task() and should
260
+ not be called directly. It ensures proper error handling,
261
+ logging, and cleanup regardless of task outcome.
262
+ """
263
+ try:
264
+ result = await coro
265
+ self.results[task_id] = result
266
+ except Exception as e:
267
+ logger.error(f"Task {task_id} failed: {e}")
268
+ self.errors[task_id] = str(e)
269
+ finally:
270
+ # Clean up completed task
271
+ if task_id in self.tasks:
272
+ del self.tasks[task_id]
273
+
274
+ def get_task_status(self, task_id: str) -> dict[str, Any]:
275
+ """
276
+ Get the status of a task.
277
+
278
+ Retrieves the current status of a task, including its result
279
+ if completed or error information if failed. This method can
280
+ be called repeatedly to poll task progress.
281
+
282
+ Args:
283
+ task_id: Unique identifier for the task to check
284
+
285
+ Returns:
286
+ Dict with status information containing:
287
+ - "status": One of "running", "completed", "failed", "not_found"
288
+ - "task_id": The task identifier
289
+ - "result": Task result (only if status is "completed")
290
+ - "error": Error message (only if status is "failed")
291
+
292
+ Example:
293
+ >>> status = manager.get_task_status("scan_123")
294
+ >>> if status["status"] == "completed":
295
+ >>> print(f"Task completed: {status['result']}")
296
+ >>> elif status["status"] == "failed":
297
+ >>> print(f"Task failed: {status['error']}")
298
+ >>> elif status["status"] == "running":
299
+ >>> print("Task is still running...")
300
+ """
301
+ if task_id in self.tasks:
302
+ return {"status": "running", "task_id": task_id}
303
+ elif task_id in self.results:
304
+ return {
305
+ "status": "completed",
306
+ "task_id": task_id,
307
+ "result": self.results[task_id],
308
+ }
309
+ elif task_id in self.errors:
310
+ return {
311
+ "status": "failed",
312
+ "task_id": task_id,
313
+ "error": self.errors[task_id],
314
+ }
315
+ else:
316
+ return {"status": "not_found", "task_id": task_id}
317
+
318
+ def cancel_task(self, task_id: str) -> bool:
319
+ """
320
+ Cancel a running task.
321
+
322
+ Attempts to cancel a task that is currently running. Only active
323
+ tasks can be cancelled; completed or failed tasks cannot be cancelled.
324
+ The task will be removed from the active tasks list.
325
+
326
+ Args:
327
+ task_id: Unique identifier for the task to cancel
328
+
329
+ Returns:
330
+ True if task was found and cancelled, False if task was not found
331
+ or was not in a cancellable state
332
+
333
+ Example:
334
+ >>> success = manager.cancel_task("long_running_task")
335
+ >>> if success:
336
+ >>> print("Task cancelled successfully")
337
+ >>> else:
338
+ >>> print("Task not found or already completed")
339
+
340
+ Note:
341
+ Cancelled tasks will not appear in results or errors dictionaries.
342
+ The task coroutine will receive a CancelledError exception.
343
+ """
344
+ if task_id in self.tasks:
345
+ self.tasks[task_id].cancel()
346
+ del self.tasks[task_id]
347
+ return True
348
+ return False
349
+
350
+
351
+ # Global task manager instance
352
+ task_manager = AsyncTaskManager()
353
+ """Global instance of AsyncTaskManager for application-wide task management.
354
+
355
+ This singleton instance can be used throughout the application to manage
356
+ long-running async tasks. It provides a centralized way to submit, track,
357
+ and cancel background operations.
358
+
359
+ Example:
360
+ >>> from utils.async_timeout import task_manager
361
+ >>> task_id = task_manager.submit_task("analysis", analyze_code(path))
362
+ >>> # Later, check status
363
+ >>> status = task_manager.get_task_status(task_id)
364
+ """