provide-foundation 0.0.0.dev0__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 (149) hide show
  1. provide/__init__.py +15 -0
  2. provide/foundation/__init__.py +155 -0
  3. provide/foundation/_version.py +58 -0
  4. provide/foundation/cli/__init__.py +67 -0
  5. provide/foundation/cli/commands/__init__.py +3 -0
  6. provide/foundation/cli/commands/deps.py +71 -0
  7. provide/foundation/cli/commands/logs/__init__.py +63 -0
  8. provide/foundation/cli/commands/logs/generate.py +357 -0
  9. provide/foundation/cli/commands/logs/generate_old.py +569 -0
  10. provide/foundation/cli/commands/logs/query.py +174 -0
  11. provide/foundation/cli/commands/logs/send.py +166 -0
  12. provide/foundation/cli/commands/logs/tail.py +112 -0
  13. provide/foundation/cli/decorators.py +262 -0
  14. provide/foundation/cli/main.py +65 -0
  15. provide/foundation/cli/testing.py +220 -0
  16. provide/foundation/cli/utils.py +210 -0
  17. provide/foundation/config/__init__.py +106 -0
  18. provide/foundation/config/base.py +295 -0
  19. provide/foundation/config/env.py +369 -0
  20. provide/foundation/config/loader.py +311 -0
  21. provide/foundation/config/manager.py +387 -0
  22. provide/foundation/config/schema.py +284 -0
  23. provide/foundation/config/sync.py +281 -0
  24. provide/foundation/config/types.py +78 -0
  25. provide/foundation/config/validators.py +80 -0
  26. provide/foundation/console/__init__.py +29 -0
  27. provide/foundation/console/input.py +364 -0
  28. provide/foundation/console/output.py +178 -0
  29. provide/foundation/context/__init__.py +12 -0
  30. provide/foundation/context/core.py +356 -0
  31. provide/foundation/core.py +20 -0
  32. provide/foundation/crypto/__init__.py +182 -0
  33. provide/foundation/crypto/algorithms.py +111 -0
  34. provide/foundation/crypto/certificates.py +896 -0
  35. provide/foundation/crypto/checksums.py +301 -0
  36. provide/foundation/crypto/constants.py +57 -0
  37. provide/foundation/crypto/hashing.py +265 -0
  38. provide/foundation/crypto/keys.py +188 -0
  39. provide/foundation/crypto/signatures.py +144 -0
  40. provide/foundation/crypto/utils.py +164 -0
  41. provide/foundation/errors/__init__.py +96 -0
  42. provide/foundation/errors/auth.py +73 -0
  43. provide/foundation/errors/base.py +81 -0
  44. provide/foundation/errors/config.py +103 -0
  45. provide/foundation/errors/context.py +299 -0
  46. provide/foundation/errors/decorators.py +484 -0
  47. provide/foundation/errors/handlers.py +360 -0
  48. provide/foundation/errors/integration.py +105 -0
  49. provide/foundation/errors/platform.py +37 -0
  50. provide/foundation/errors/process.py +140 -0
  51. provide/foundation/errors/resources.py +133 -0
  52. provide/foundation/errors/runtime.py +160 -0
  53. provide/foundation/errors/safe_decorators.py +133 -0
  54. provide/foundation/errors/types.py +276 -0
  55. provide/foundation/file/__init__.py +79 -0
  56. provide/foundation/file/atomic.py +157 -0
  57. provide/foundation/file/directory.py +134 -0
  58. provide/foundation/file/formats.py +236 -0
  59. provide/foundation/file/lock.py +175 -0
  60. provide/foundation/file/safe.py +179 -0
  61. provide/foundation/file/utils.py +170 -0
  62. provide/foundation/hub/__init__.py +88 -0
  63. provide/foundation/hub/click_builder.py +310 -0
  64. provide/foundation/hub/commands.py +42 -0
  65. provide/foundation/hub/components.py +640 -0
  66. provide/foundation/hub/decorators.py +244 -0
  67. provide/foundation/hub/info.py +32 -0
  68. provide/foundation/hub/manager.py +446 -0
  69. provide/foundation/hub/registry.py +279 -0
  70. provide/foundation/hub/type_mapping.py +54 -0
  71. provide/foundation/hub/types.py +28 -0
  72. provide/foundation/logger/__init__.py +41 -0
  73. provide/foundation/logger/base.py +22 -0
  74. provide/foundation/logger/config/__init__.py +16 -0
  75. provide/foundation/logger/config/base.py +40 -0
  76. provide/foundation/logger/config/logging.py +394 -0
  77. provide/foundation/logger/config/telemetry.py +188 -0
  78. provide/foundation/logger/core.py +239 -0
  79. provide/foundation/logger/custom_processors.py +172 -0
  80. provide/foundation/logger/emoji/__init__.py +44 -0
  81. provide/foundation/logger/emoji/matrix.py +209 -0
  82. provide/foundation/logger/emoji/sets.py +458 -0
  83. provide/foundation/logger/emoji/types.py +56 -0
  84. provide/foundation/logger/factories.py +56 -0
  85. provide/foundation/logger/processors/__init__.py +13 -0
  86. provide/foundation/logger/processors/main.py +254 -0
  87. provide/foundation/logger/processors/trace.py +113 -0
  88. provide/foundation/logger/ratelimit/__init__.py +31 -0
  89. provide/foundation/logger/ratelimit/limiters.py +294 -0
  90. provide/foundation/logger/ratelimit/processor.py +203 -0
  91. provide/foundation/logger/ratelimit/queue_limiter.py +305 -0
  92. provide/foundation/logger/setup/__init__.py +29 -0
  93. provide/foundation/logger/setup/coordinator.py +138 -0
  94. provide/foundation/logger/setup/emoji_resolver.py +64 -0
  95. provide/foundation/logger/setup/processors.py +85 -0
  96. provide/foundation/logger/setup/testing.py +39 -0
  97. provide/foundation/logger/trace.py +38 -0
  98. provide/foundation/metrics/__init__.py +119 -0
  99. provide/foundation/metrics/otel.py +122 -0
  100. provide/foundation/metrics/simple.py +165 -0
  101. provide/foundation/observability/__init__.py +53 -0
  102. provide/foundation/observability/openobserve/__init__.py +79 -0
  103. provide/foundation/observability/openobserve/auth.py +72 -0
  104. provide/foundation/observability/openobserve/client.py +307 -0
  105. provide/foundation/observability/openobserve/commands.py +357 -0
  106. provide/foundation/observability/openobserve/exceptions.py +41 -0
  107. provide/foundation/observability/openobserve/formatters.py +298 -0
  108. provide/foundation/observability/openobserve/models.py +134 -0
  109. provide/foundation/observability/openobserve/otlp.py +320 -0
  110. provide/foundation/observability/openobserve/search.py +222 -0
  111. provide/foundation/observability/openobserve/streaming.py +235 -0
  112. provide/foundation/platform/__init__.py +44 -0
  113. provide/foundation/platform/detection.py +193 -0
  114. provide/foundation/platform/info.py +157 -0
  115. provide/foundation/process/__init__.py +39 -0
  116. provide/foundation/process/async_runner.py +373 -0
  117. provide/foundation/process/lifecycle.py +406 -0
  118. provide/foundation/process/runner.py +390 -0
  119. provide/foundation/setup/__init__.py +101 -0
  120. provide/foundation/streams/__init__.py +44 -0
  121. provide/foundation/streams/console.py +57 -0
  122. provide/foundation/streams/core.py +65 -0
  123. provide/foundation/streams/file.py +104 -0
  124. provide/foundation/testing/__init__.py +166 -0
  125. provide/foundation/testing/cli.py +227 -0
  126. provide/foundation/testing/crypto.py +163 -0
  127. provide/foundation/testing/fixtures.py +49 -0
  128. provide/foundation/testing/hub.py +23 -0
  129. provide/foundation/testing/logger.py +106 -0
  130. provide/foundation/testing/streams.py +54 -0
  131. provide/foundation/tracer/__init__.py +49 -0
  132. provide/foundation/tracer/context.py +115 -0
  133. provide/foundation/tracer/otel.py +135 -0
  134. provide/foundation/tracer/spans.py +174 -0
  135. provide/foundation/types.py +32 -0
  136. provide/foundation/utils/__init__.py +97 -0
  137. provide/foundation/utils/deps.py +195 -0
  138. provide/foundation/utils/env.py +491 -0
  139. provide/foundation/utils/formatting.py +483 -0
  140. provide/foundation/utils/parsing.py +235 -0
  141. provide/foundation/utils/rate_limiting.py +112 -0
  142. provide/foundation/utils/streams.py +67 -0
  143. provide/foundation/utils/timing.py +93 -0
  144. provide_foundation-0.0.0.dev0.dist-info/METADATA +469 -0
  145. provide_foundation-0.0.0.dev0.dist-info/RECORD +149 -0
  146. provide_foundation-0.0.0.dev0.dist-info/WHEEL +5 -0
  147. provide_foundation-0.0.0.dev0.dist-info/entry_points.txt +2 -0
  148. provide_foundation-0.0.0.dev0.dist-info/licenses/LICENSE +201 -0
  149. provide_foundation-0.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,357 @@
1
+ #
2
+ # generate.py
3
+ #
4
+ """
5
+ Command to generate logs for testing OpenObserve integration with Foundation's rate limiting.
6
+ """
7
+
8
+ import random
9
+ import time
10
+ from typing import Any
11
+
12
+ try:
13
+ import click
14
+
15
+ _HAS_CLICK = True
16
+ except ImportError:
17
+ click = None
18
+ _HAS_CLICK = False
19
+
20
+ import threading
21
+
22
+ from provide.foundation.logger import get_logger
23
+
24
+ log = get_logger(__name__)
25
+
26
+ # Cut-up phrases inspired by Burroughs
27
+ BURROUGHS_PHRASES = [
28
+ "mutated Soft Machine prescribed within data stream",
29
+ "pre-recorded talking asshole dissolved into under neon hum",
30
+ "the viral Word carrying a new strain of reality",
31
+ "memory banks spilling future-pasts onto the terminal floor",
32
+ "the soft typewriter of the Other Half",
33
+ "control mechanisms broadcast in reversed time signatures",
34
+ "equations of control flickering on a broken monitor",
35
+ "semantic disturbances in Sector 9",
36
+ "the Biologic Courts passing sentence in a dream",
37
+ "a thousand junk units screaming in unison",
38
+ "frequency shift reported by Sector 5",
39
+ "the algebra of need written in neural static",
40
+ ]
41
+
42
+ # Service names
43
+ SERVICE_NAMES = [
44
+ "api-gateway",
45
+ "auth-service",
46
+ "user-service",
47
+ "payment-processor",
48
+ "notification-service",
49
+ "search-index",
50
+ "cache-layer",
51
+ "data-pipeline",
52
+ "ml-inference",
53
+ "report-generator",
54
+ "webhook-handler",
55
+ "queue-processor",
56
+ "stream-analyzer",
57
+ "batch-job",
58
+ "cron-scheduler",
59
+ "interzone-terminal",
60
+ "nova-police",
61
+ "reality-studio",
62
+ ]
63
+
64
+ # Operations
65
+ OPERATIONS = [
66
+ "process_request",
67
+ "validate_input",
68
+ "execute_query",
69
+ "transform_data",
70
+ "send_notification",
71
+ "update_cache",
72
+ "sync_state",
73
+ "aggregate_metrics",
74
+ "encode_response",
75
+ "decode_request",
76
+ "authorize_access",
77
+ "refresh_token",
78
+ "persist_data",
79
+ "emit_event",
80
+ "handle_error",
81
+ "transmit_signal",
82
+ "intercept_word",
83
+ "decode_reality",
84
+ ]
85
+
86
+ # Trace and span ID tracking
87
+ _trace_counter = 0
88
+ _span_counter = 0
89
+ _trace_lock = threading.Lock()
90
+
91
+
92
+ def generate_trace_id() -> str:
93
+ """Generate a unique trace ID."""
94
+ global _trace_counter
95
+ with _trace_lock:
96
+ trace_id = f"trace_{_trace_counter:08d}"
97
+ _trace_counter += 1
98
+ return trace_id
99
+
100
+
101
+ def generate_span_id() -> str:
102
+ """Generate a unique span ID."""
103
+ global _span_counter
104
+ with _trace_lock:
105
+ span_id = f"span_{_span_counter:08d}"
106
+ _span_counter += 1
107
+ return span_id
108
+
109
+
110
+ def generate_log_entry(
111
+ index: int, style: str = "normal", error_rate: float = 0.1
112
+ ) -> dict[str, Any]:
113
+ """
114
+ Generate a single log entry with optional error simulation.
115
+
116
+ Args:
117
+ index: Log entry index
118
+ style: Style of log generation ("normal" or "burroughs")
119
+ error_rate: Probability of generating an error log (0.0 to 1.0)
120
+
121
+ Returns:
122
+ Dict containing log entry data
123
+ """
124
+ # Choose message based on style
125
+ if style == "burroughs":
126
+ message = random.choice(BURROUGHS_PHRASES)
127
+ else:
128
+ # Normal tech-style messages
129
+ operations = [
130
+ "processed",
131
+ "validated",
132
+ "executed",
133
+ "transformed",
134
+ "cached",
135
+ "synced",
136
+ ]
137
+ objects = ["request", "query", "data", "event", "message", "transaction"]
138
+ message = f"Successfully {random.choice(operations)} {random.choice(objects)}"
139
+
140
+ # Generate error condition
141
+ is_error = random.random() < error_rate
142
+
143
+ # Base entry
144
+ entry = {
145
+ "message": message,
146
+ "service": random.choice(SERVICE_NAMES),
147
+ "operation": random.choice(OPERATIONS),
148
+ "iteration": index,
149
+ "trace_id": generate_trace_id()
150
+ if index % 10 == 0
151
+ else f"trace_{(_trace_counter - 1):08d}",
152
+ "span_id": generate_span_id(),
153
+ "duration_ms": random.randint(10, 5000),
154
+ }
155
+
156
+ # Add error fields if this is an error
157
+ if is_error:
158
+ entry["level"] = "error"
159
+ entry["error_code"] = random.choice([400, 404, 500, 503])
160
+ entry["error_type"] = random.choice(
161
+ [
162
+ "ValidationError",
163
+ "ServiceUnavailable",
164
+ "TimeoutError",
165
+ "DatabaseError",
166
+ "RateLimitExceeded",
167
+ ]
168
+ )
169
+ else:
170
+ # Random log level for non-errors
171
+ entry["level"] = random.choice(["debug", "info", "warning"])
172
+
173
+ # Add domain/action/status for DAS emoji system
174
+ entry["domain"] = random.choice(["user", "system", "data", "api", None])
175
+ entry["action"] = random.choice(["create", "read", "update", "delete", None])
176
+ entry["status"] = (
177
+ "error" if is_error else random.choice(["success", "pending", None])
178
+ )
179
+
180
+ return entry
181
+
182
+
183
+ @click.command(name="generate")
184
+ @click.option(
185
+ "-n", "--count", default=100, help="Number of logs to generate (0 for continuous)"
186
+ )
187
+ @click.option("-r", "--rate", default=10.0, help="Logs per second rate")
188
+ @click.option("-s", "--stream", default="default", help="Target stream name")
189
+ @click.option(
190
+ "--style",
191
+ type=click.Choice(["normal", "burroughs"]),
192
+ default="normal",
193
+ help="Message generation style",
194
+ )
195
+ @click.option("-e", "--error-rate", default=0.1, help="Error rate (0.0 to 1.0)")
196
+ @click.option(
197
+ "--enable-rate-limit", is_flag=True, help="Enable Foundation's rate limiting"
198
+ )
199
+ @click.option("--rate-limit", default=100.0, help="Rate limit (logs/s) when enabled")
200
+ def generate_logs_command(
201
+ count: int,
202
+ rate: float,
203
+ stream: str,
204
+ style: str,
205
+ error_rate: float,
206
+ enable_rate_limit: bool,
207
+ rate_limit: float,
208
+ ):
209
+ """Generate logs to test OpenObserve integration with Foundation's rate limiting."""
210
+
211
+ click.echo("šŸš€ Starting log generation...")
212
+ click.echo(f" Style: {style}")
213
+ click.echo(f" Error rate: {int(error_rate * 100)}%")
214
+ click.echo(f" Target stream: {stream}")
215
+
216
+ if count == 0:
217
+ click.echo(f" Mode: Continuous at {rate} logs/second")
218
+ else:
219
+ click.echo(f" Count: {count} logs at {rate} logs/second")
220
+
221
+ if enable_rate_limit:
222
+ click.echo(f" āš ļø Foundation rate limiting enabled: {rate_limit} logs/s max")
223
+
224
+ # Configure Foundation's rate limiting
225
+ from provide.foundation.logger.ratelimit import GlobalRateLimiter
226
+
227
+ limiter = GlobalRateLimiter()
228
+ limiter.configure(
229
+ global_rate=rate_limit,
230
+ global_capacity=rate_limit * 2, # Allow burst up to 2x the rate
231
+ )
232
+
233
+ click.echo(" Press Ctrl+C to stop\n")
234
+
235
+ # Track statistics
236
+ logs_sent = 0
237
+ logs_failed = 0
238
+ logs_rate_limited = 0
239
+ start_time = time.time()
240
+ last_stats_time = start_time
241
+ last_stats_sent = 0
242
+
243
+ try:
244
+ if count == 0:
245
+ # Continuous mode
246
+ index = 0
247
+ while True:
248
+ current_time = time.time()
249
+
250
+ # Generate log entry
251
+ entry = generate_log_entry(index, style, error_rate)
252
+ index += 1
253
+
254
+ # Send using Foundation's logger
255
+ try:
256
+ service_logger = get_logger(f"generated.{entry['service']}")
257
+
258
+ # Extract level and remove from entry
259
+ level = entry.pop("level", "info")
260
+ message = entry.pop("message")
261
+
262
+ # Log at appropriate level
263
+ getattr(service_logger, level)(message, **entry)
264
+ logs_sent += 1
265
+
266
+ except Exception as e:
267
+ logs_failed += 1
268
+ if "rate limit" in str(e).lower():
269
+ logs_rate_limited += 1
270
+
271
+ # Control rate
272
+ target_interval = 1.0 / rate
273
+ elapsed = current_time - start_time
274
+ expected_count = int(elapsed * rate)
275
+
276
+ if logs_sent < expected_count:
277
+ # We're behind, no sleep
278
+ pass
279
+ else:
280
+ # We're on track or ahead, sleep until next interval
281
+ next_time = start_time + (logs_sent / rate)
282
+ sleep_time = next_time - time.time()
283
+ if sleep_time > 0:
284
+ time.sleep(sleep_time)
285
+
286
+ # Print stats every second
287
+ if current_time - last_stats_time >= 1.0:
288
+ current_rate = (logs_sent - last_stats_sent) / (
289
+ current_time - last_stats_time
290
+ )
291
+
292
+ status = f"šŸ“Š Sent: {logs_sent:,} | Rate: {current_rate:.0f}/s"
293
+ if logs_failed > 0:
294
+ status += f" | Failed: {logs_failed:,}"
295
+ if enable_rate_limit and logs_rate_limited > 0:
296
+ status += f" | āš ļø Rate limited: {logs_rate_limited:,}"
297
+
298
+ click.echo(status)
299
+ last_stats_time = current_time
300
+ last_stats_sent = logs_sent
301
+
302
+ else:
303
+ # Fixed count mode
304
+ for i in range(count):
305
+ # Generate log entry
306
+ entry = generate_log_entry(i, style, error_rate)
307
+
308
+ # Send using Foundation's logger
309
+ try:
310
+ service_logger = get_logger(f"generated.{entry['service']}")
311
+
312
+ # Extract level and remove from entry
313
+ level = entry.pop("level", "info")
314
+ message = entry.pop("message")
315
+
316
+ # Log at appropriate level
317
+ getattr(service_logger, level)(message, **entry)
318
+ logs_sent += 1
319
+
320
+ except Exception as e:
321
+ logs_failed += 1
322
+ if "rate limit" in str(e).lower():
323
+ logs_rate_limited += 1
324
+
325
+ # Control rate
326
+ if rate > 0:
327
+ time.sleep(1.0 / rate)
328
+
329
+ # Print progress
330
+ if (i + 1) % max(1, count // 10) == 0:
331
+ progress = (i + 1) / count * 100
332
+ click.echo(f"Progress: {progress:.0f}% ({i + 1}/{count})")
333
+
334
+ except KeyboardInterrupt:
335
+ click.echo("\n\nā›” Generation interrupted by user")
336
+
337
+ finally:
338
+ # Print final statistics
339
+ total_time = time.time() - start_time
340
+ actual_rate = logs_sent / total_time if total_time > 0 else 0
341
+
342
+ click.echo("\nšŸ“Š Generation complete:")
343
+ click.echo(f" Total sent: {logs_sent} logs")
344
+ click.echo(f" Total failed: {logs_failed} logs")
345
+ if enable_rate_limit:
346
+ click.echo(f" āš ļø Rate limited: {logs_rate_limited} logs")
347
+ click.echo(f" Time: {total_time:.2f}s")
348
+ click.echo(f" Target rate: {rate} logs/second")
349
+ click.echo(f" Actual rate: {actual_rate:.1f} logs/second")
350
+
351
+
352
+ if not _HAS_CLICK:
353
+
354
+ def generate_logs_command(*args, **kwargs):
355
+ raise ImportError(
356
+ "Click is required for CLI commands. Install with: pip install click"
357
+ )