dory-processor-sdk 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. dory/__init__.py +101 -0
  2. dory/auth/__init__.py +10 -0
  3. dory/auth/oauth2.py +153 -0
  4. dory/auto_instrument.py +142 -0
  5. dory/cli/__init__.py +5 -0
  6. dory/cli/main.py +137 -0
  7. dory/cli/templates.py +123 -0
  8. dory/config/__init__.py +23 -0
  9. dory/config/defaults.py +24 -0
  10. dory/config/loader.py +430 -0
  11. dory/config/presets.py +73 -0
  12. dory/config/schema.py +84 -0
  13. dory/core/__init__.py +27 -0
  14. dory/core/app.py +434 -0
  15. dory/core/context.py +209 -0
  16. dory/core/lifecycle.py +214 -0
  17. dory/core/meta.py +121 -0
  18. dory/core/modes.py +479 -0
  19. dory/core/processor.py +564 -0
  20. dory/core/signals.py +122 -0
  21. dory/decorators.py +142 -0
  22. dory/edge/__init__.py +88 -0
  23. dory/edge/adaptive.py +644 -0
  24. dory/edge/detector.py +546 -0
  25. dory/edge/fencing.py +488 -0
  26. dory/edge/heartbeat.py +598 -0
  27. dory/edge/role.py +419 -0
  28. dory/errors/__init__.py +139 -0
  29. dory/errors/classification.py +362 -0
  30. dory/errors/codes.py +498 -0
  31. dory/geo/__init__.py +40 -0
  32. dory/geo/geolocalizer.py +1034 -0
  33. dory/health/__init__.py +12 -0
  34. dory/health/probes.py +210 -0
  35. dory/health/server.py +635 -0
  36. dory/k8s/__init__.py +80 -0
  37. dory/k8s/annotation_watcher.py +184 -0
  38. dory/k8s/client.py +251 -0
  39. dory/k8s/labels.py +505 -0
  40. dory/k8s/pod_metadata.py +182 -0
  41. dory/logging/__init__.py +9 -0
  42. dory/logging/logger.py +148 -0
  43. dory/metrics/__init__.py +7 -0
  44. dory/metrics/collector.py +301 -0
  45. dory/middleware/__init__.py +46 -0
  46. dory/middleware/connection_tracker.py +608 -0
  47. dory/middleware/request_id.py +325 -0
  48. dory/middleware/request_tracker.py +511 -0
  49. dory/migration/__init__.py +33 -0
  50. dory/migration/configmap.py +232 -0
  51. dory/migration/s3_store.py +594 -0
  52. dory/migration/serialization.py +135 -0
  53. dory/migration/state_manager.py +286 -0
  54. dory/migration/transfer.py +382 -0
  55. dory/monitoring/__init__.py +29 -0
  56. dory/monitoring/opentelemetry.py +489 -0
  57. dory/output/__init__.py +31 -0
  58. dory/output/envelope.py +137 -0
  59. dory/output/formatter.py +113 -0
  60. dory/output/rabbitmq.py +632 -0
  61. dory/output/routing.py +318 -0
  62. dory/output/validator.py +199 -0
  63. dory/py.typed +2 -0
  64. dory/recovery/__init__.py +60 -0
  65. dory/recovery/golden_image.py +487 -0
  66. dory/recovery/golden_snapshot.py +713 -0
  67. dory/recovery/golden_validator.py +518 -0
  68. dory/recovery/partial_recovery.py +482 -0
  69. dory/recovery/recovery_decision.py +242 -0
  70. dory/recovery/restart_detector.py +142 -0
  71. dory/recovery/state_validator.py +183 -0
  72. dory/resilience/__init__.py +45 -0
  73. dory/resilience/circuit_breaker.py +457 -0
  74. dory/resilience/retry.py +389 -0
  75. dory/simple.py +342 -0
  76. dory/types.py +68 -0
  77. dory/utils/__init__.py +31 -0
  78. dory/utils/errors.py +59 -0
  79. dory/utils/retry.py +115 -0
  80. dory/utils/timeout.py +80 -0
  81. dory_processor_sdk-0.0.1.dist-info/METADATA +424 -0
  82. dory_processor_sdk-0.0.1.dist-info/RECORD +86 -0
  83. dory_processor_sdk-0.0.1.dist-info/WHEEL +5 -0
  84. dory_processor_sdk-0.0.1.dist-info/entry_points.txt +2 -0
  85. dory_processor_sdk-0.0.1.dist-info/licenses/LICENSE +201 -0
  86. dory_processor_sdk-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,325 @@
1
+ """
2
+ Request ID Middleware
3
+
4
+ Automatically generates and propagates request IDs for tracing and correlation.
5
+ Eliminates manual request ID management.
6
+
7
+ Features:
8
+ - Automatic request ID generation
9
+ - Context propagation
10
+ - Log integration
11
+ - Correlation across services
12
+ """
13
+
14
+ import asyncio
15
+ import logging
16
+ import uuid
17
+ from contextvars import ContextVar
18
+ from functools import wraps
19
+ from typing import Optional, Callable, Any
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Context variable for current request ID
24
+ _request_id_context: ContextVar[Optional[str]] = ContextVar("request_id", default=None)
25
+
26
+
27
+ def generate_request_id() -> str:
28
+ """
29
+ Generate a unique request ID.
30
+
31
+ Returns:
32
+ UUID-based request ID
33
+ """
34
+ return str(uuid.uuid4())
35
+
36
+
37
+ def get_current_request_id() -> Optional[str]:
38
+ """
39
+ Get the current request ID from context.
40
+
41
+ Returns:
42
+ Current request ID or None if not set
43
+ """
44
+ return _request_id_context.get()
45
+
46
+
47
+ def set_request_id(request_id: str) -> None:
48
+ """
49
+ Set the request ID in context.
50
+
51
+ Args:
52
+ request_id: Request ID to set
53
+ """
54
+ _request_id_context.set(request_id)
55
+
56
+
57
+ class RequestIdMiddleware:
58
+ """
59
+ Middleware for automatic request ID management.
60
+
61
+ Features:
62
+ - Generates request IDs
63
+ - Propagates through context
64
+ - Integrates with logging
65
+ - Supports custom ID generation
66
+
67
+ Usage:
68
+ middleware = RequestIdMiddleware()
69
+
70
+ # Use context manager
71
+ async with middleware.with_request_id():
72
+ # All operations have access to request ID
73
+ request_id = get_current_request_id()
74
+ logger.info(f"Processing with request_id={request_id}")
75
+
76
+ # Or use decorator
77
+ @middleware.with_request_id_decorator()
78
+ async def process_item(item):
79
+ request_id = get_current_request_id()
80
+ # Process with request ID
81
+ """
82
+
83
+ def __init__(
84
+ self,
85
+ id_generator: Optional[Callable[[], str]] = None,
86
+ header_name: str = "X-Request-ID",
87
+ log_request_id: bool = True,
88
+ ):
89
+ """
90
+ Initialize request ID middleware.
91
+
92
+ Args:
93
+ id_generator: Optional custom ID generator function
94
+ header_name: Header name for request ID
95
+ log_request_id: Whether to add request ID to logs
96
+ """
97
+ self.id_generator = id_generator or generate_request_id
98
+ self.header_name = header_name
99
+ self.log_request_id = log_request_id
100
+
101
+ logger.info(
102
+ f"RequestIdMiddleware initialized: header={header_name}, "
103
+ f"log_enabled={log_request_id}"
104
+ )
105
+
106
+ def generate_id(self) -> str:
107
+ """Generate a new request ID using the configured generator."""
108
+ return self.id_generator()
109
+
110
+ async def with_request_id(
111
+ self,
112
+ request_id: Optional[str] = None,
113
+ ):
114
+ """
115
+ Context manager that sets request ID for the scope.
116
+
117
+ Args:
118
+ request_id: Optional existing request ID (generates new if None)
119
+
120
+ Example:
121
+ async with middleware.with_request_id():
122
+ # Operations here have access to request ID
123
+ request_id = get_current_request_id()
124
+ """
125
+ # Generate or use provided request ID
126
+ if request_id is None:
127
+ request_id = self.id_generator()
128
+
129
+ # Set in context
130
+ token = _request_id_context.set(request_id)
131
+
132
+ # Add to logs if enabled
133
+ if self.log_request_id:
134
+ log_filter = RequestIdLogFilter(request_id)
135
+ logging.getLogger().addFilter(log_filter)
136
+
137
+ try:
138
+ yield request_id
139
+ finally:
140
+ # Reset context
141
+ _request_id_context.reset(token)
142
+
143
+ # Remove log filter
144
+ if self.log_request_id:
145
+ logging.getLogger().removeFilter(log_filter)
146
+
147
+ def with_request_id_decorator(
148
+ self,
149
+ request_id_arg: Optional[str] = None,
150
+ ):
151
+ """
152
+ Decorator that automatically manages request ID.
153
+
154
+ Args:
155
+ request_id_arg: Optional argument name containing request ID
156
+
157
+ Example:
158
+ @middleware.with_request_id_decorator()
159
+ async def process_item(item):
160
+ request_id = get_current_request_id()
161
+ logger.info(f"Processing {item}")
162
+ # Log will include request_id
163
+
164
+ # Or extract from argument
165
+ @middleware.with_request_id_decorator(request_id_arg="headers")
166
+ async def handle_request(headers):
167
+ # Uses headers.get("X-Request-ID") if present
168
+ pass
169
+ """
170
+ def decorator(func):
171
+ @wraps(func)
172
+ async def wrapper(*args, **kwargs):
173
+ # Try to extract request ID from arguments if specified
174
+ extracted_id = None
175
+ if request_id_arg:
176
+ # Check kwargs
177
+ if request_id_arg in kwargs:
178
+ arg_value = kwargs[request_id_arg]
179
+ if isinstance(arg_value, dict):
180
+ extracted_id = arg_value.get(self.header_name)
181
+ else:
182
+ extracted_id = arg_value
183
+
184
+ # Use extracted or generate new
185
+ request_id = extracted_id or self.id_generator()
186
+
187
+ async with self.with_request_id(request_id):
188
+ return await func(*args, **kwargs)
189
+
190
+ return wrapper
191
+
192
+ return decorator
193
+
194
+ def extract_request_id(self, headers: dict) -> Optional[str]:
195
+ """
196
+ Extract request ID from headers.
197
+
198
+ Args:
199
+ headers: HTTP headers or similar dict
200
+
201
+ Returns:
202
+ Request ID if present, None otherwise
203
+ """
204
+ return headers.get(self.header_name)
205
+
206
+ def inject_request_id(self, headers: dict, request_id: Optional[str] = None) -> dict:
207
+ """
208
+ Inject request ID into headers.
209
+
210
+ Args:
211
+ headers: HTTP headers or similar dict
212
+ request_id: Optional request ID (uses current context if None)
213
+
214
+ Returns:
215
+ Headers with request ID injected
216
+ """
217
+ if request_id is None:
218
+ request_id = get_current_request_id()
219
+
220
+ if request_id:
221
+ headers = dict(headers)
222
+ headers[self.header_name] = request_id
223
+
224
+ return headers
225
+
226
+
227
+ class RequestIdLogFilter(logging.Filter):
228
+ """
229
+ Log filter that adds request ID to log records.
230
+ """
231
+
232
+ def __init__(self, request_id: str):
233
+ """
234
+ Initialize log filter.
235
+
236
+ Args:
237
+ request_id: Request ID to add to logs
238
+ """
239
+ super().__init__()
240
+ self.request_id = request_id
241
+
242
+ def filter(self, record):
243
+ """Add request ID to log record."""
244
+ record.request_id = self.request_id
245
+ return True
246
+
247
+
248
+ # Convenience decorator using global middleware instance
249
+ _global_middleware: Optional[RequestIdMiddleware] = None
250
+
251
+
252
+ def get_global_middleware() -> RequestIdMiddleware:
253
+ """Get or create global middleware instance."""
254
+ global _global_middleware
255
+ if _global_middleware is None:
256
+ _global_middleware = RequestIdMiddleware()
257
+ return _global_middleware
258
+
259
+
260
+ def with_request_id(func: Optional[Callable] = None, request_id: Optional[str] = None):
261
+ """
262
+ Convenience decorator using global middleware.
263
+
264
+ Args:
265
+ func: Function to decorate
266
+ request_id: Optional request ID
267
+
268
+ Example:
269
+ @with_request_id
270
+ async def process_item(item):
271
+ request_id = get_current_request_id()
272
+ logger.info(f"Processing {item}") # Log includes request_id
273
+ """
274
+ middleware = get_global_middleware()
275
+
276
+ if func is None:
277
+ # Used as @with_request_id()
278
+ return middleware.with_request_id_decorator()
279
+
280
+ # Used as @with_request_id
281
+ return middleware.with_request_id_decorator()(func)
282
+
283
+
284
+ # Example log format with request ID
285
+ RECOMMENDED_LOG_FORMAT = "%(asctime)s [%(levelname)s] [%(request_id)s] %(name)s: %(message)s"
286
+
287
+
288
+ def configure_logging_with_request_id(
289
+ level: int = logging.INFO,
290
+ format_string: str = RECOMMENDED_LOG_FORMAT,
291
+ ) -> None:
292
+ """
293
+ Configure logging to include request IDs.
294
+
295
+ Args:
296
+ level: Logging level
297
+ format_string: Format string (should include %(request_id)s)
298
+
299
+ Example:
300
+ configure_logging_with_request_id()
301
+
302
+ @with_request_id
303
+ async def my_function():
304
+ logger.info("Hello") # [INFO] [<request-id>] module: Hello
305
+ """
306
+ # Add default value for request_id if not in context
307
+ old_factory = logging.getLogRecordFactory()
308
+
309
+ def record_factory(*args, **kwargs):
310
+ record = old_factory(*args, **kwargs)
311
+ if not hasattr(record, "request_id"):
312
+ request_id = get_current_request_id()
313
+ record.request_id = request_id or "no-request-id"
314
+ return record
315
+
316
+ logging.setLogRecordFactory(record_factory)
317
+
318
+ # Configure basic logging
319
+ logging.basicConfig(
320
+ level=level,
321
+ format=format_string,
322
+ force=True,
323
+ )
324
+
325
+ logger.info("Logging configured with request ID support")