unrealon 1.1.5__py3-none-any.whl → 2.0.4__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 (146) hide show
  1. {unrealon-1.1.5.dist-info/licenses → unrealon-2.0.4.dist-info}/LICENSE +1 -1
  2. unrealon-2.0.4.dist-info/METADATA +491 -0
  3. unrealon-2.0.4.dist-info/RECORD +129 -0
  4. {unrealon-1.1.5.dist-info → unrealon-2.0.4.dist-info}/WHEEL +2 -1
  5. unrealon-2.0.4.dist-info/entry_points.txt +3 -0
  6. unrealon-2.0.4.dist-info/top_level.txt +3 -0
  7. unrealon_browser/__init__.py +5 -2
  8. unrealon_browser/cli/browser_cli.py +18 -9
  9. unrealon_browser/cli/interactive_mode.py +18 -7
  10. unrealon_browser/core/browser_manager.py +76 -13
  11. unrealon_browser/dto/__init__.py +21 -0
  12. unrealon_browser/dto/bot_detection.py +175 -0
  13. unrealon_browser/dto/models/config.py +14 -1
  14. unrealon_browser/managers/__init__.py +4 -1
  15. unrealon_browser/managers/logger_bridge.py +3 -6
  16. unrealon_browser/managers/page_wait_manager.py +198 -0
  17. unrealon_browser/stealth/__init__.py +27 -0
  18. unrealon_browser/stealth/bypass_techniques.pyc +0 -0
  19. unrealon_browser/stealth/manager.pyc +0 -0
  20. unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
  21. unrealon_browser/stealth/playwright_stealth.pyc +0 -0
  22. unrealon_browser/stealth/scanner_tester.pyc +0 -0
  23. unrealon_browser/stealth/undetected_chrome.pyc +0 -0
  24. unrealon_core/__init__.py +160 -0
  25. unrealon_core/config/__init__.py +16 -0
  26. unrealon_core/config/environment.py +98 -0
  27. unrealon_core/config/urls.py +93 -0
  28. unrealon_core/enums/__init__.py +24 -0
  29. unrealon_core/enums/status.py +216 -0
  30. unrealon_core/enums/types.py +240 -0
  31. unrealon_core/error_handling/__init__.py +45 -0
  32. unrealon_core/error_handling/circuit_breaker.py +292 -0
  33. unrealon_core/error_handling/error_context.py +324 -0
  34. unrealon_core/error_handling/recovery.py +371 -0
  35. unrealon_core/error_handling/retry.py +268 -0
  36. unrealon_core/exceptions/__init__.py +46 -0
  37. unrealon_core/exceptions/base.py +292 -0
  38. unrealon_core/exceptions/communication.py +22 -0
  39. unrealon_core/exceptions/driver.py +11 -0
  40. unrealon_core/exceptions/proxy.py +11 -0
  41. unrealon_core/exceptions/task.py +12 -0
  42. unrealon_core/exceptions/validation.py +17 -0
  43. unrealon_core/models/__init__.py +98 -0
  44. unrealon_core/models/arq_context.py +252 -0
  45. unrealon_core/models/arq_responses.py +125 -0
  46. unrealon_core/models/base.py +291 -0
  47. unrealon_core/models/bridge_stats.py +58 -0
  48. unrealon_core/models/communication.py +39 -0
  49. unrealon_core/models/config.py +47 -0
  50. unrealon_core/models/connection_stats.py +47 -0
  51. unrealon_core/models/driver.py +30 -0
  52. unrealon_core/models/driver_details.py +98 -0
  53. unrealon_core/models/logging.py +28 -0
  54. unrealon_core/models/task.py +21 -0
  55. unrealon_core/models/typed_responses.py +210 -0
  56. unrealon_core/models/websocket/__init__.py +91 -0
  57. unrealon_core/models/websocket/base.py +49 -0
  58. unrealon_core/models/websocket/config.py +200 -0
  59. unrealon_core/models/websocket/driver.py +215 -0
  60. unrealon_core/models/websocket/errors.py +138 -0
  61. unrealon_core/models/websocket/heartbeat.py +100 -0
  62. unrealon_core/models/websocket/logging.py +261 -0
  63. unrealon_core/models/websocket/proxy.py +496 -0
  64. unrealon_core/models/websocket/tasks.py +275 -0
  65. unrealon_core/models/websocket/utils.py +153 -0
  66. unrealon_core/models/websocket_session.py +144 -0
  67. unrealon_core/monitoring/__init__.py +43 -0
  68. unrealon_core/monitoring/alerts.py +398 -0
  69. unrealon_core/monitoring/dashboard.py +307 -0
  70. unrealon_core/monitoring/health_check.py +354 -0
  71. unrealon_core/monitoring/metrics.py +352 -0
  72. unrealon_core/utils/__init__.py +11 -0
  73. unrealon_core/utils/time.py +61 -0
  74. unrealon_core/version.py +219 -0
  75. unrealon_driver/__init__.py +88 -50
  76. unrealon_driver/core_module/__init__.py +34 -0
  77. unrealon_driver/core_module/base.py +184 -0
  78. unrealon_driver/core_module/config.py +30 -0
  79. unrealon_driver/core_module/event_manager.py +127 -0
  80. unrealon_driver/core_module/protocols.py +98 -0
  81. unrealon_driver/core_module/registry.py +146 -0
  82. unrealon_driver/decorators/__init__.py +15 -0
  83. unrealon_driver/decorators/retry.py +117 -0
  84. unrealon_driver/decorators/schedule.py +137 -0
  85. unrealon_driver/decorators/task.py +61 -0
  86. unrealon_driver/decorators/timing.py +132 -0
  87. unrealon_driver/driver/__init__.py +20 -0
  88. unrealon_driver/driver/communication/__init__.py +10 -0
  89. unrealon_driver/driver/communication/session.py +203 -0
  90. unrealon_driver/driver/communication/websocket_client.py +197 -0
  91. unrealon_driver/driver/core/__init__.py +10 -0
  92. unrealon_driver/driver/core/config.py +85 -0
  93. unrealon_driver/driver/core/driver.py +221 -0
  94. unrealon_driver/driver/factory/__init__.py +9 -0
  95. unrealon_driver/driver/factory/manager_factory.py +130 -0
  96. unrealon_driver/driver/lifecycle/__init__.py +11 -0
  97. unrealon_driver/driver/lifecycle/daemon.py +76 -0
  98. unrealon_driver/driver/lifecycle/initialization.py +97 -0
  99. unrealon_driver/driver/lifecycle/shutdown.py +48 -0
  100. unrealon_driver/driver/monitoring/__init__.py +9 -0
  101. unrealon_driver/driver/monitoring/health.py +63 -0
  102. unrealon_driver/driver/utilities/__init__.py +10 -0
  103. unrealon_driver/driver/utilities/logging.py +51 -0
  104. unrealon_driver/driver/utilities/serialization.py +61 -0
  105. unrealon_driver/managers/__init__.py +32 -0
  106. unrealon_driver/managers/base.py +174 -0
  107. unrealon_driver/managers/browser.py +98 -0
  108. unrealon_driver/managers/cache.py +116 -0
  109. unrealon_driver/managers/http.py +107 -0
  110. unrealon_driver/managers/logger.py +286 -0
  111. unrealon_driver/managers/proxy.py +99 -0
  112. unrealon_driver/managers/registry.py +87 -0
  113. unrealon_driver/managers/threading.py +54 -0
  114. unrealon_driver/managers/update.py +107 -0
  115. unrealon_driver/utils/__init__.py +9 -0
  116. unrealon_driver/utils/time.py +10 -0
  117. unrealon/__init__.py +0 -40
  118. unrealon-1.1.5.dist-info/METADATA +0 -621
  119. unrealon-1.1.5.dist-info/RECORD +0 -54
  120. unrealon-1.1.5.dist-info/entry_points.txt +0 -9
  121. unrealon_browser/managers/stealth.py +0 -388
  122. unrealon_driver/exceptions.py +0 -33
  123. unrealon_driver/html_analyzer/__init__.py +0 -32
  124. unrealon_driver/html_analyzer/cleaner.py +0 -657
  125. unrealon_driver/html_analyzer/config.py +0 -64
  126. unrealon_driver/html_analyzer/manager.py +0 -247
  127. unrealon_driver/html_analyzer/models.py +0 -115
  128. unrealon_driver/html_analyzer/websocket_analyzer.py +0 -157
  129. unrealon_driver/models/__init__.py +0 -31
  130. unrealon_driver/models/websocket.py +0 -98
  131. unrealon_driver/parser/__init__.py +0 -36
  132. unrealon_driver/parser/cli_manager.py +0 -142
  133. unrealon_driver/parser/daemon_manager.py +0 -403
  134. unrealon_driver/parser/managers/__init__.py +0 -25
  135. unrealon_driver/parser/managers/config.py +0 -293
  136. unrealon_driver/parser/managers/error.py +0 -412
  137. unrealon_driver/parser/managers/result.py +0 -321
  138. unrealon_driver/parser/parser_manager.py +0 -458
  139. unrealon_driver/smart_logging/__init__.py +0 -24
  140. unrealon_driver/smart_logging/models.py +0 -44
  141. unrealon_driver/smart_logging/smart_logger.py +0 -406
  142. unrealon_driver/smart_logging/unified_logger.py +0 -525
  143. unrealon_driver/websocket/__init__.py +0 -31
  144. unrealon_driver/websocket/client.py +0 -249
  145. unrealon_driver/websocket/config.py +0 -188
  146. unrealon_driver/websocket/manager.py +0 -90
@@ -0,0 +1,292 @@
1
+ """
2
+ Circuit Breaker Pattern
3
+
4
+ Prevents cascading failures by temporarily disabling failing services.
5
+ Following critical requirements - max 500 lines, functions < 20 lines.
6
+
7
+ Phase 2: Core Systems - Error Handling
8
+ """
9
+
10
+ import asyncio
11
+ import logging
12
+ from datetime import datetime, timedelta
13
+ from typing import Any, Callable, Optional, Dict
14
+ from enum import Enum
15
+
16
+ from pydantic import BaseModel, Field, ConfigDict
17
+
18
+ from ..exceptions.base import UnrealOnError
19
+ from ..utils.time import utc_now
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class CircuitBreakerState(str, Enum):
25
+ """Circuit breaker states."""
26
+ CLOSED = "closed" # Normal operation
27
+ OPEN = "open" # Failing, requests blocked
28
+ HALF_OPEN = "half_open" # Testing if service recovered
29
+
30
+
31
+ class CircuitBreakerError(UnrealOnError):
32
+ """Circuit breaker is open."""
33
+ pass
34
+
35
+
36
+ class CircuitBreakerConfig(BaseModel):
37
+ """Configuration for circuit breaker."""
38
+
39
+ model_config = ConfigDict(
40
+ validate_assignment=True,
41
+ extra="forbid"
42
+ )
43
+
44
+ failure_threshold: int = Field(
45
+ default=5,
46
+ ge=1,
47
+ le=20,
48
+ description="Failures needed to open circuit"
49
+ )
50
+ recovery_timeout: float = Field(
51
+ default=60.0,
52
+ ge=1.0,
53
+ le=300.0,
54
+ description="Seconds before trying half-open"
55
+ )
56
+ success_threshold: int = Field(
57
+ default=3,
58
+ ge=1,
59
+ le=10,
60
+ description="Successes needed to close circuit"
61
+ )
62
+ timeout: float = Field(
63
+ default=30.0,
64
+ ge=1.0,
65
+ le=120.0,
66
+ description="Operation timeout in seconds"
67
+ )
68
+
69
+
70
+ class CircuitBreakerStats(BaseModel):
71
+ """Circuit breaker statistics."""
72
+
73
+ model_config = ConfigDict(
74
+ validate_assignment=True,
75
+ extra="forbid"
76
+ )
77
+
78
+ total_requests: int = Field(default=0, description="Total requests processed")
79
+ successful_requests: int = Field(default=0, description="Successful requests")
80
+ failed_requests: int = Field(default=0, description="Failed requests")
81
+ rejected_requests: int = Field(default=0, description="Requests rejected by circuit")
82
+ last_failure_time: Optional[datetime] = Field(default=None, description="Last failure timestamp")
83
+ last_success_time: Optional[datetime] = Field(default=None, description="Last success timestamp")
84
+
85
+ def get_failure_rate(self) -> float:
86
+ """Calculate failure rate percentage."""
87
+ if self.total_requests == 0:
88
+ return 0.0
89
+ return (self.failed_requests / self.total_requests) * 100.0
90
+
91
+
92
+ class CircuitBreaker:
93
+ """
94
+ Circuit breaker implementation.
95
+
96
+ Prevents cascading failures by monitoring service health
97
+ and temporarily blocking requests to failing services.
98
+ """
99
+
100
+ def __init__(self, name: str, config: CircuitBreakerConfig):
101
+ """Initialize circuit breaker."""
102
+ self.name = name
103
+ self.config = config
104
+ self.state = CircuitBreakerState.CLOSED
105
+ self.stats = CircuitBreakerStats()
106
+
107
+ # State tracking
108
+ self._consecutive_failures = 0
109
+ self._consecutive_successes = 0
110
+ self._last_failure_time: Optional[datetime] = None
111
+ self._lock = asyncio.Lock()
112
+
113
+ self.logger = logging.getLogger(f"circuit_breaker.{name}")
114
+
115
+ async def call(self, func: Callable[..., Any], *args, **kwargs) -> Any:
116
+ """
117
+ Execute function through circuit breaker.
118
+
119
+ Args:
120
+ func: Function to execute
121
+ *args, **kwargs: Function arguments
122
+
123
+ Returns:
124
+ Function result
125
+
126
+ Raises:
127
+ CircuitBreakerError: If circuit is open
128
+ """
129
+ async with self._lock:
130
+ await self._update_state()
131
+
132
+ if self.state == CircuitBreakerState.OPEN:
133
+ self.stats.rejected_requests += 1
134
+ self.logger.warning(f"Circuit breaker {self.name} is OPEN - rejecting request")
135
+ raise CircuitBreakerError(f"Circuit breaker {self.name} is open")
136
+
137
+ self.stats.total_requests += 1
138
+
139
+ # Execute function with timeout
140
+ try:
141
+ result = await asyncio.wait_for(
142
+ func(*args, **kwargs),
143
+ timeout=self.config.timeout
144
+ )
145
+
146
+ await self._record_success()
147
+ return result
148
+
149
+ except Exception as e:
150
+ await self._record_failure()
151
+ raise
152
+
153
+ async def _update_state(self) -> None:
154
+ """Update circuit breaker state based on current conditions."""
155
+ now = utc_now()
156
+
157
+ if self.state == CircuitBreakerState.OPEN:
158
+ # Check if we should transition to half-open
159
+ if (self._last_failure_time and
160
+ (now - self._last_failure_time).total_seconds() >= self.config.recovery_timeout):
161
+
162
+ self.state = CircuitBreakerState.HALF_OPEN
163
+ self._consecutive_successes = 0
164
+ self.logger.info(f"Circuit breaker {self.name} transitioning to HALF_OPEN")
165
+
166
+ elif self.state == CircuitBreakerState.HALF_OPEN:
167
+ # Check if we should close the circuit
168
+ if self._consecutive_successes >= self.config.success_threshold:
169
+ self.state = CircuitBreakerState.CLOSED
170
+ self._consecutive_failures = 0
171
+ self.logger.info(f"Circuit breaker {self.name} transitioning to CLOSED")
172
+
173
+ async def _record_success(self) -> None:
174
+ """Record successful operation."""
175
+ async with self._lock:
176
+ self.stats.successful_requests += 1
177
+ self.stats.last_success_time = utc_now()
178
+
179
+ if self.state == CircuitBreakerState.HALF_OPEN:
180
+ self._consecutive_successes += 1
181
+ self.logger.debug(
182
+ f"Circuit breaker {self.name} success "
183
+ f"({self._consecutive_successes}/{self.config.success_threshold})"
184
+ )
185
+
186
+ # Reset failure counter on success
187
+ self._consecutive_failures = 0
188
+
189
+ async def _record_failure(self) -> None:
190
+ """Record failed operation."""
191
+ async with self._lock:
192
+ self.stats.failed_requests += 1
193
+ self.stats.last_failure_time = utc_now()
194
+ self._last_failure_time = self.stats.last_failure_time
195
+
196
+ self._consecutive_failures += 1
197
+ self._consecutive_successes = 0
198
+
199
+ self.logger.debug(
200
+ f"Circuit breaker {self.name} failure "
201
+ f"({self._consecutive_failures}/{self.config.failure_threshold})"
202
+ )
203
+
204
+ # Check if we should open the circuit
205
+ if (self.state == CircuitBreakerState.CLOSED and
206
+ self._consecutive_failures >= self.config.failure_threshold):
207
+
208
+ self.state = CircuitBreakerState.OPEN
209
+ self.logger.warning(f"Circuit breaker {self.name} transitioning to OPEN")
210
+
211
+ elif self.state == CircuitBreakerState.HALF_OPEN:
212
+ # Any failure in half-open state reopens the circuit
213
+ self.state = CircuitBreakerState.OPEN
214
+ self.logger.warning(f"Circuit breaker {self.name} reopening due to failure in HALF_OPEN")
215
+
216
+ def get_status(self) -> Dict[str, Any]:
217
+ """Get circuit breaker status."""
218
+ return {
219
+ 'name': self.name,
220
+ 'state': self.state.value,
221
+ 'consecutive_failures': self._consecutive_failures,
222
+ 'consecutive_successes': self._consecutive_successes,
223
+ 'config': self.config.model_dump(),
224
+ 'stats': self.stats.model_dump()
225
+ }
226
+
227
+ def reset(self) -> None:
228
+ """Reset circuit breaker to closed state."""
229
+ self.state = CircuitBreakerState.CLOSED
230
+ self._consecutive_failures = 0
231
+ self._consecutive_successes = 0
232
+ self._last_failure_time = None
233
+ self.logger.info(f"Circuit breaker {self.name} manually reset to CLOSED")
234
+
235
+
236
+ # Global circuit breaker registry
237
+ _circuit_breakers: Dict[str, CircuitBreaker] = {}
238
+
239
+
240
+ def get_circuit_breaker(
241
+ name: str,
242
+ config: Optional[CircuitBreakerConfig] = None
243
+ ) -> CircuitBreaker:
244
+ """Get or create circuit breaker by name."""
245
+ if name not in _circuit_breakers:
246
+ if config is None:
247
+ config = CircuitBreakerConfig()
248
+ _circuit_breakers[name] = CircuitBreaker(name, config)
249
+
250
+ return _circuit_breakers[name]
251
+
252
+
253
+ def circuit_breaker(
254
+ name: str,
255
+ config: Optional[CircuitBreakerConfig] = None
256
+ ):
257
+ """Decorator to wrap function with circuit breaker."""
258
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
259
+ cb = get_circuit_breaker(name, config)
260
+
261
+ async def async_wrapper(*args, **kwargs):
262
+ return await cb.call(func, *args, **kwargs)
263
+
264
+ def sync_wrapper(*args, **kwargs):
265
+ # For sync functions, we need to handle differently
266
+ # This is a simplified version - in production you might want
267
+ # to use threading or a different approach
268
+ import asyncio
269
+ try:
270
+ loop = asyncio.get_event_loop()
271
+ return loop.run_until_complete(cb.call(func, *args, **kwargs))
272
+ except RuntimeError:
273
+ # No event loop running
274
+ return asyncio.run(cb.call(func, *args, **kwargs))
275
+
276
+ if asyncio.iscoroutinefunction(func):
277
+ return async_wrapper
278
+ else:
279
+ return sync_wrapper
280
+
281
+ return decorator
282
+
283
+
284
+ def get_all_circuit_breakers() -> Dict[str, Dict[str, Any]]:
285
+ """Get status of all circuit breakers."""
286
+ return {name: cb.get_status() for name, cb in _circuit_breakers.items()}
287
+
288
+
289
+ def reset_all_circuit_breakers() -> None:
290
+ """Reset all circuit breakers."""
291
+ for cb in _circuit_breakers.values():
292
+ cb.reset()
@@ -0,0 +1,324 @@
1
+ """
2
+ Error Context System
3
+
4
+ Rich error context for debugging and monitoring.
5
+ Following critical requirements - max 500 lines, functions < 20 lines.
6
+
7
+ Phase 2: Core Systems - Error Handling
8
+ """
9
+
10
+ import traceback
11
+ from datetime import datetime
12
+ from typing import Any, Dict, Optional, List
13
+ from enum import Enum
14
+
15
+ from pydantic import BaseModel, Field, ConfigDict
16
+
17
+ from ..utils.time import utc_now
18
+
19
+
20
+ class ErrorSeverity(str, Enum):
21
+ """Error severity levels."""
22
+ LOW = "low"
23
+ MEDIUM = "medium"
24
+ HIGH = "high"
25
+ CRITICAL = "critical"
26
+
27
+
28
+ class ErrorContext(BaseModel):
29
+ """Rich error context information."""
30
+
31
+ model_config = ConfigDict(
32
+ validate_assignment=True,
33
+ extra="forbid"
34
+ )
35
+
36
+ # Basic error info
37
+ error_id: str = Field(description="Unique error identifier")
38
+ error_type: str = Field(description="Error class name")
39
+ error_message: str = Field(description="Error message")
40
+ severity: ErrorSeverity = Field(description="Error severity level")
41
+
42
+ # Timing
43
+ timestamp: datetime = Field(description="When error occurred")
44
+ duration_ms: Optional[float] = Field(default=None, description="Operation duration before error")
45
+
46
+ # Context
47
+ operation: str = Field(description="Operation that failed")
48
+ component: str = Field(description="Component where error occurred")
49
+ user_id: Optional[str] = Field(default=None, description="Associated user ID")
50
+ request_id: Optional[str] = Field(default=None, description="Request correlation ID")
51
+
52
+ # Technical details
53
+ stack_trace: Optional[str] = Field(default=None, description="Full stack trace")
54
+ function_name: str = Field(description="Function where error occurred")
55
+ file_name: str = Field(description="File where error occurred")
56
+ line_number: int = Field(description="Line number where error occurred")
57
+
58
+ # Additional context
59
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Function parameters")
60
+ environment: Dict[str, str] = Field(default_factory=dict, description="Environment variables")
61
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
62
+
63
+ # Recovery info
64
+ is_retryable: bool = Field(default=True, description="Whether error is retryable")
65
+ retry_count: int = Field(default=0, description="Number of retries attempted")
66
+ recovery_suggestions: List[str] = Field(default_factory=list, description="Recovery suggestions")
67
+
68
+
69
+ def create_error_context(
70
+ error: Exception,
71
+ operation: str,
72
+ component: str,
73
+ severity: ErrorSeverity = ErrorSeverity.MEDIUM,
74
+ **kwargs
75
+ ) -> ErrorContext:
76
+ """
77
+ Create error context from exception.
78
+
79
+ Args:
80
+ error: Exception that occurred
81
+ operation: Operation that failed
82
+ component: Component where error occurred
83
+ severity: Error severity level
84
+ **kwargs: Additional context data
85
+
86
+ Returns:
87
+ ErrorContext with rich error information
88
+ """
89
+ import uuid
90
+ import inspect
91
+ import os
92
+
93
+ # Get stack trace info
94
+ tb = traceback.extract_tb(error.__traceback__)
95
+ if tb:
96
+ frame = tb[-1] # Last frame (where error occurred)
97
+ file_name = os.path.basename(frame.filename)
98
+ function_name = frame.name
99
+ line_number = frame.lineno
100
+ else:
101
+ file_name = "unknown"
102
+ function_name = "unknown"
103
+ line_number = 0
104
+
105
+ # Generate error ID
106
+ error_id = str(uuid.uuid4())[:8]
107
+
108
+ # Extract parameters from current frame
109
+ parameters = {}
110
+ try:
111
+ frame = inspect.currentframe()
112
+ if frame and frame.f_back:
113
+ parameters = {
114
+ k: str(v) for k, v in frame.f_back.f_locals.items()
115
+ if not k.startswith('_') and not callable(v)
116
+ }
117
+ except Exception:
118
+ pass # Ignore parameter extraction errors
119
+
120
+ # Basic environment info
121
+ environment = {
122
+ 'python_version': f"{os.sys.version_info.major}.{os.sys.version_info.minor}",
123
+ 'platform': os.sys.platform,
124
+ }
125
+
126
+ return ErrorContext(
127
+ error_id=error_id,
128
+ error_type=error.__class__.__name__,
129
+ error_message=str(error),
130
+ severity=severity,
131
+ timestamp=utc_now(),
132
+ operation=operation,
133
+ component=component,
134
+ stack_trace=traceback.format_exc(),
135
+ function_name=function_name,
136
+ file_name=file_name,
137
+ line_number=line_number,
138
+ parameters=parameters,
139
+ environment=environment,
140
+ **kwargs
141
+ )
142
+
143
+
144
+ def format_error_context(context: ErrorContext, include_stack_trace: bool = False) -> str:
145
+ """
146
+ Format error context for logging.
147
+
148
+ Args:
149
+ context: Error context to format
150
+ include_stack_trace: Whether to include full stack trace
151
+
152
+ Returns:
153
+ Formatted error message
154
+ """
155
+ lines = [
156
+ f"🚨 ERROR [{context.error_id}] {context.severity.upper()}",
157
+ f" Type: {context.error_type}",
158
+ f" Message: {context.error_message}",
159
+ f" Operation: {context.operation}",
160
+ f" Component: {context.component}",
161
+ f" Location: {context.file_name}:{context.line_number} in {context.function_name}()",
162
+ f" Time: {context.timestamp.isoformat()}",
163
+ ]
164
+
165
+ if context.duration_ms:
166
+ lines.append(f" Duration: {context.duration_ms:.2f}ms")
167
+
168
+ if context.request_id:
169
+ lines.append(f" Request ID: {context.request_id}")
170
+
171
+ if context.user_id:
172
+ lines.append(f" User ID: {context.user_id}")
173
+
174
+ if context.retry_count > 0:
175
+ lines.append(f" Retries: {context.retry_count}")
176
+
177
+ if context.parameters:
178
+ lines.append(" Parameters:")
179
+ for key, value in context.parameters.items():
180
+ lines.append(f" {key}: {value}")
181
+
182
+ if context.recovery_suggestions:
183
+ lines.append(" Recovery suggestions:")
184
+ for suggestion in context.recovery_suggestions:
185
+ lines.append(f" • {suggestion}")
186
+
187
+ if include_stack_trace and context.stack_trace:
188
+ lines.append(" Stack trace:")
189
+ for line in context.stack_trace.split('\n'):
190
+ if line.strip():
191
+ lines.append(f" {line}")
192
+
193
+ return '\n'.join(lines)
194
+
195
+
196
+ def determine_severity(error: Exception) -> ErrorSeverity:
197
+ """
198
+ Determine error severity based on exception type.
199
+
200
+ Args:
201
+ error: Exception to analyze
202
+
203
+ Returns:
204
+ Appropriate severity level
205
+ """
206
+ error_name = error.__class__.__name__
207
+
208
+ # Critical errors
209
+ critical_errors = [
210
+ 'SystemError', 'MemoryError', 'KeyboardInterrupt',
211
+ 'SystemExit', 'GeneratorExit'
212
+ ]
213
+
214
+ # High severity errors
215
+ high_errors = [
216
+ 'ConnectionError', 'DatabaseError', 'AuthenticationError',
217
+ 'PermissionError', 'SecurityError'
218
+ ]
219
+
220
+ # Medium severity errors
221
+ medium_errors = [
222
+ 'ValidationError', 'ValueError', 'TypeError',
223
+ 'AttributeError', 'KeyError', 'IndexError'
224
+ ]
225
+
226
+ if error_name in critical_errors:
227
+ return ErrorSeverity.CRITICAL
228
+ elif error_name in high_errors:
229
+ return ErrorSeverity.HIGH
230
+ elif error_name in medium_errors:
231
+ return ErrorSeverity.MEDIUM
232
+ else:
233
+ return ErrorSeverity.LOW
234
+
235
+
236
+ def is_retryable_error(error: Exception) -> bool:
237
+ """
238
+ Determine if error is retryable.
239
+
240
+ Args:
241
+ error: Exception to analyze
242
+
243
+ Returns:
244
+ True if error should be retried
245
+ """
246
+ error_name = error.__class__.__name__
247
+
248
+ # Non-retryable errors
249
+ non_retryable = [
250
+ 'ValidationError', 'ValueError', 'TypeError',
251
+ 'AttributeError', 'KeyError', 'IndexError',
252
+ 'AuthenticationError', 'PermissionError',
253
+ 'NotImplementedError', 'AssertionError'
254
+ ]
255
+
256
+ # Retryable errors
257
+ retryable = [
258
+ 'ConnectionError', 'TimeoutError', 'HTTPError',
259
+ 'NetworkError', 'ServiceUnavailableError',
260
+ 'TemporaryError', 'RateLimitError'
261
+ ]
262
+
263
+ if error_name in non_retryable:
264
+ return False
265
+ elif error_name in retryable:
266
+ return True
267
+ else:
268
+ # Default to retryable for unknown errors
269
+ return True
270
+
271
+
272
+ def get_recovery_suggestions(error: Exception, operation: str) -> List[str]:
273
+ """
274
+ Get recovery suggestions for error.
275
+
276
+ Args:
277
+ error: Exception that occurred
278
+ operation: Operation that failed
279
+
280
+ Returns:
281
+ List of recovery suggestions
282
+ """
283
+ error_name = error.__class__.__name__
284
+ suggestions = []
285
+
286
+ if error_name == 'ConnectionError':
287
+ suggestions.extend([
288
+ "Check network connectivity",
289
+ "Verify service endpoint is accessible",
290
+ "Check firewall settings"
291
+ ])
292
+
293
+ elif error_name == 'TimeoutError':
294
+ suggestions.extend([
295
+ "Increase timeout value",
296
+ "Check service performance",
297
+ "Retry with exponential backoff"
298
+ ])
299
+
300
+ elif error_name == 'ValidationError':
301
+ suggestions.extend([
302
+ "Check input data format",
303
+ "Verify required fields are present",
304
+ "Review data validation rules"
305
+ ])
306
+
307
+ elif error_name == 'AuthenticationError':
308
+ suggestions.extend([
309
+ "Check authentication credentials",
310
+ "Verify token is not expired",
311
+ "Refresh authentication token"
312
+ ])
313
+
314
+ elif error_name == 'PermissionError':
315
+ suggestions.extend([
316
+ "Check user permissions",
317
+ "Verify access rights for operation",
318
+ "Contact administrator for access"
319
+ ])
320
+
321
+ else:
322
+ suggestions.append(f"Review {operation} implementation")
323
+
324
+ return suggestions