provide-foundation 0.0.0.dev1__py3-none-any.whl → 0.0.0.dev3__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 (163) hide show
  1. provide/foundation/__init__.py +36 -10
  2. provide/foundation/archive/__init__.py +1 -1
  3. provide/foundation/archive/base.py +15 -14
  4. provide/foundation/archive/bzip2.py +40 -40
  5. provide/foundation/archive/gzip.py +42 -42
  6. provide/foundation/archive/operations.py +93 -96
  7. provide/foundation/archive/tar.py +33 -31
  8. provide/foundation/archive/zip.py +52 -50
  9. provide/foundation/asynctools/__init__.py +20 -0
  10. provide/foundation/asynctools/core.py +126 -0
  11. provide/foundation/cli/__init__.py +2 -2
  12. provide/foundation/cli/commands/deps.py +15 -9
  13. provide/foundation/cli/commands/logs/__init__.py +3 -3
  14. provide/foundation/cli/commands/logs/generate.py +2 -2
  15. provide/foundation/cli/commands/logs/query.py +4 -4
  16. provide/foundation/cli/commands/logs/send.py +3 -3
  17. provide/foundation/cli/commands/logs/tail.py +3 -3
  18. provide/foundation/cli/decorators.py +11 -11
  19. provide/foundation/cli/main.py +1 -1
  20. provide/foundation/cli/testing.py +2 -40
  21. provide/foundation/cli/utils.py +21 -18
  22. provide/foundation/config/__init__.py +35 -2
  23. provide/foundation/config/base.py +2 -2
  24. provide/foundation/config/converters.py +477 -0
  25. provide/foundation/config/defaults.py +67 -0
  26. provide/foundation/config/env.py +6 -20
  27. provide/foundation/config/loader.py +10 -4
  28. provide/foundation/config/sync.py +8 -6
  29. provide/foundation/config/types.py +5 -5
  30. provide/foundation/config/validators.py +4 -4
  31. provide/foundation/console/input.py +5 -5
  32. provide/foundation/console/output.py +36 -14
  33. provide/foundation/context/__init__.py +8 -4
  34. provide/foundation/context/core.py +88 -110
  35. provide/foundation/crypto/certificates/__init__.py +9 -5
  36. provide/foundation/crypto/certificates/base.py +2 -2
  37. provide/foundation/crypto/certificates/certificate.py +48 -19
  38. provide/foundation/crypto/certificates/factory.py +26 -18
  39. provide/foundation/crypto/certificates/generator.py +24 -23
  40. provide/foundation/crypto/certificates/loader.py +24 -16
  41. provide/foundation/crypto/certificates/operations.py +17 -10
  42. provide/foundation/crypto/certificates/trust.py +21 -21
  43. provide/foundation/env/__init__.py +28 -0
  44. provide/foundation/env/core.py +218 -0
  45. provide/foundation/errors/__init__.py +3 -3
  46. provide/foundation/errors/decorators.py +0 -234
  47. provide/foundation/errors/types.py +0 -98
  48. provide/foundation/eventsets/display.py +13 -14
  49. provide/foundation/eventsets/registry.py +61 -31
  50. provide/foundation/eventsets/resolver.py +50 -46
  51. provide/foundation/eventsets/sets/das.py +8 -8
  52. provide/foundation/eventsets/sets/database.py +14 -14
  53. provide/foundation/eventsets/sets/http.py +21 -21
  54. provide/foundation/eventsets/sets/llm.py +16 -16
  55. provide/foundation/eventsets/sets/task_queue.py +13 -13
  56. provide/foundation/eventsets/types.py +7 -7
  57. provide/foundation/file/directory.py +14 -23
  58. provide/foundation/file/lock.py +4 -3
  59. provide/foundation/hub/components.py +75 -389
  60. provide/foundation/hub/config.py +157 -0
  61. provide/foundation/hub/discovery.py +63 -0
  62. provide/foundation/hub/handlers.py +89 -0
  63. provide/foundation/hub/lifecycle.py +195 -0
  64. provide/foundation/hub/manager.py +7 -4
  65. provide/foundation/hub/processors.py +49 -0
  66. provide/foundation/integrations/__init__.py +11 -0
  67. provide/foundation/{observability → integrations}/openobserve/__init__.py +10 -7
  68. provide/foundation/{observability → integrations}/openobserve/auth.py +1 -1
  69. provide/foundation/{observability → integrations}/openobserve/client.py +14 -14
  70. provide/foundation/{observability → integrations}/openobserve/commands.py +12 -12
  71. provide/foundation/integrations/openobserve/config.py +37 -0
  72. provide/foundation/{observability → integrations}/openobserve/formatters.py +1 -1
  73. provide/foundation/{observability → integrations}/openobserve/otlp.py +2 -2
  74. provide/foundation/{observability → integrations}/openobserve/search.py +2 -3
  75. provide/foundation/{observability → integrations}/openobserve/streaming.py +5 -5
  76. provide/foundation/logger/__init__.py +0 -1
  77. provide/foundation/logger/config/base.py +1 -1
  78. provide/foundation/logger/config/logging.py +69 -299
  79. provide/foundation/logger/config/telemetry.py +39 -121
  80. provide/foundation/logger/factories.py +2 -2
  81. provide/foundation/logger/processors/main.py +12 -10
  82. provide/foundation/logger/ratelimit/limiters.py +4 -4
  83. provide/foundation/logger/ratelimit/processor.py +1 -1
  84. provide/foundation/logger/setup/coordinator.py +39 -25
  85. provide/foundation/logger/setup/processors.py +3 -3
  86. provide/foundation/logger/setup/testing.py +14 -0
  87. provide/foundation/logger/trace.py +5 -5
  88. provide/foundation/metrics/__init__.py +1 -1
  89. provide/foundation/metrics/otel.py +3 -1
  90. provide/foundation/observability/__init__.py +3 -3
  91. provide/foundation/process/__init__.py +9 -0
  92. provide/foundation/process/exit.py +48 -0
  93. provide/foundation/process/lifecycle.py +69 -46
  94. provide/foundation/resilience/__init__.py +36 -0
  95. provide/foundation/resilience/circuit.py +166 -0
  96. provide/foundation/resilience/decorators.py +236 -0
  97. provide/foundation/resilience/fallback.py +208 -0
  98. provide/foundation/resilience/retry.py +327 -0
  99. provide/foundation/serialization/__init__.py +16 -0
  100. provide/foundation/serialization/core.py +70 -0
  101. provide/foundation/streams/config.py +78 -0
  102. provide/foundation/streams/console.py +4 -5
  103. provide/foundation/streams/core.py +5 -2
  104. provide/foundation/streams/file.py +12 -2
  105. provide/foundation/testing/__init__.py +29 -9
  106. provide/foundation/testing/archive/__init__.py +7 -7
  107. provide/foundation/testing/archive/fixtures.py +58 -54
  108. provide/foundation/testing/cli.py +30 -20
  109. provide/foundation/testing/common/__init__.py +13 -15
  110. provide/foundation/testing/common/fixtures.py +27 -57
  111. provide/foundation/testing/file/__init__.py +15 -15
  112. provide/foundation/testing/file/content_fixtures.py +289 -0
  113. provide/foundation/testing/file/directory_fixtures.py +107 -0
  114. provide/foundation/testing/file/fixtures.py +42 -516
  115. provide/foundation/testing/file/special_fixtures.py +145 -0
  116. provide/foundation/testing/logger.py +89 -8
  117. provide/foundation/testing/mocking/__init__.py +21 -21
  118. provide/foundation/testing/mocking/fixtures.py +80 -67
  119. provide/foundation/testing/process/__init__.py +23 -23
  120. provide/foundation/testing/process/async_fixtures.py +414 -0
  121. provide/foundation/testing/process/fixtures.py +48 -571
  122. provide/foundation/testing/process/subprocess_fixtures.py +210 -0
  123. provide/foundation/testing/threading/__init__.py +17 -17
  124. provide/foundation/testing/threading/basic_fixtures.py +105 -0
  125. provide/foundation/testing/threading/data_fixtures.py +101 -0
  126. provide/foundation/testing/threading/execution_fixtures.py +278 -0
  127. provide/foundation/testing/threading/fixtures.py +32 -502
  128. provide/foundation/testing/threading/sync_fixtures.py +100 -0
  129. provide/foundation/testing/time/__init__.py +11 -11
  130. provide/foundation/testing/time/fixtures.py +95 -83
  131. provide/foundation/testing/transport/__init__.py +9 -9
  132. provide/foundation/testing/transport/fixtures.py +54 -54
  133. provide/foundation/time/__init__.py +18 -0
  134. provide/foundation/time/core.py +63 -0
  135. provide/foundation/tools/__init__.py +2 -2
  136. provide/foundation/tools/base.py +68 -67
  137. provide/foundation/tools/cache.py +69 -74
  138. provide/foundation/tools/downloader.py +68 -62
  139. provide/foundation/tools/installer.py +51 -57
  140. provide/foundation/tools/registry.py +38 -45
  141. provide/foundation/tools/resolver.py +70 -68
  142. provide/foundation/tools/verifier.py +39 -50
  143. provide/foundation/tracer/spans.py +2 -14
  144. provide/foundation/transport/__init__.py +26 -33
  145. provide/foundation/transport/base.py +32 -30
  146. provide/foundation/transport/client.py +44 -49
  147. provide/foundation/transport/config.py +36 -107
  148. provide/foundation/transport/errors.py +13 -27
  149. provide/foundation/transport/http.py +69 -55
  150. provide/foundation/transport/middleware.py +113 -114
  151. provide/foundation/transport/registry.py +29 -27
  152. provide/foundation/transport/types.py +6 -6
  153. provide/foundation/utils/deps.py +17 -14
  154. provide/foundation/utils/parsing.py +49 -4
  155. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/METADATA +2 -2
  156. provide_foundation-0.0.0.dev3.dist-info/RECORD +233 -0
  157. provide_foundation-0.0.0.dev1.dist-info/RECORD +0 -200
  158. /provide/foundation/{observability → integrations}/openobserve/exceptions.py +0 -0
  159. /provide/foundation/{observability → integrations}/openobserve/models.py +0 -0
  160. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/WHEEL +0 -0
  161. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/entry_points.txt +0 -0
  162. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/licenses/LICENSE +0 -0
  163. {provide_foundation-0.0.0.dev1.dist-info → provide_foundation-0.0.0.dev3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,414 @@
1
+ """
2
+ Async-specific test fixtures for process testing.
3
+
4
+ Provides fixtures for testing async operations, event loops, and
5
+ async context management across the provide-io ecosystem.
6
+ """
7
+
8
+ import asyncio
9
+ from collections.abc import AsyncGenerator, Callable
10
+ from unittest.mock import AsyncMock
11
+
12
+ import pytest
13
+
14
+
15
+ @pytest.fixture
16
+ async def clean_event_loop() -> AsyncGenerator[None, None]:
17
+ """
18
+ Ensure clean event loop for async tests.
19
+
20
+ Cancels all pending tasks after the test to prevent event loop issues.
21
+
22
+ Yields:
23
+ None - fixture for test setup/teardown.
24
+ """
25
+ yield
26
+
27
+ # Clean up any pending tasks
28
+ loop = asyncio.get_event_loop()
29
+ pending = asyncio.all_tasks(loop)
30
+
31
+ for task in pending:
32
+ if not task.done():
33
+ task.cancel()
34
+
35
+ # Wait for all tasks to complete cancellation
36
+ if pending:
37
+ await asyncio.gather(*pending, return_exceptions=True)
38
+
39
+
40
+ @pytest.fixture
41
+ def async_timeout() -> Callable[[float], asyncio.Task]:
42
+ """
43
+ Provide configurable timeout wrapper for async operations.
44
+
45
+ Returns:
46
+ A function that wraps async operations with a timeout.
47
+ """
48
+
49
+ def _timeout_wrapper(coro, seconds: float = 5.0):
50
+ """
51
+ Wrap a coroutine with a timeout.
52
+
53
+ Args:
54
+ coro: Coroutine to wrap
55
+ seconds: Timeout in seconds
56
+
57
+ Returns:
58
+ Result of the coroutine or raises asyncio.TimeoutError
59
+ """
60
+ return asyncio.wait_for(coro, timeout=seconds)
61
+
62
+ return _timeout_wrapper
63
+
64
+
65
+ @pytest.fixture
66
+ def event_loop_policy():
67
+ """
68
+ Set event loop policy for tests to avoid conflicts.
69
+
70
+ Returns:
71
+ New event loop policy for isolated testing.
72
+ """
73
+ policy = asyncio.get_event_loop_policy()
74
+ new_policy = asyncio.DefaultEventLoopPolicy()
75
+ asyncio.set_event_loop_policy(new_policy)
76
+
77
+ yield new_policy
78
+
79
+ # Restore original policy
80
+ asyncio.set_event_loop_policy(policy)
81
+
82
+
83
+ @pytest.fixture
84
+ async def async_context_manager():
85
+ """
86
+ Factory for creating mock async context managers.
87
+
88
+ Returns:
89
+ Function that creates configured async context manager mocks.
90
+ """
91
+
92
+ def _create_async_cm(enter_value=None, exit_value=None):
93
+ """
94
+ Create a mock async context manager.
95
+
96
+ Args:
97
+ enter_value: Value to return from __aenter__
98
+ exit_value: Value to return from __aexit__
99
+
100
+ Returns:
101
+ AsyncMock configured as context manager
102
+ """
103
+ mock_cm = AsyncMock()
104
+ mock_cm.__aenter__ = AsyncMock(return_value=enter_value)
105
+ mock_cm.__aexit__ = AsyncMock(return_value=exit_value)
106
+ return mock_cm
107
+
108
+ return _create_async_cm
109
+
110
+
111
+ @pytest.fixture
112
+ async def async_iterator():
113
+ """
114
+ Factory for creating mock async iterators.
115
+
116
+ Returns:
117
+ Function that creates async iterator mocks with specified values.
118
+ """
119
+
120
+ def _create_async_iter(values):
121
+ """
122
+ Create a mock async iterator.
123
+
124
+ Args:
125
+ values: List of values to yield
126
+
127
+ Returns:
128
+ Async iterator that yields the specified values
129
+ """
130
+
131
+ class AsyncIterator:
132
+ def __init__(self, vals):
133
+ self.vals = vals
134
+ self.index = 0
135
+
136
+ def __aiter__(self):
137
+ return self
138
+
139
+ async def __anext__(self):
140
+ if self.index >= len(self.vals):
141
+ raise StopAsyncIteration
142
+ value = self.vals[self.index]
143
+ self.index += 1
144
+ return value
145
+
146
+ return AsyncIterator(values)
147
+
148
+ return _create_async_iter
149
+
150
+
151
+ @pytest.fixture
152
+ def async_queue():
153
+ """
154
+ Create an async queue for testing producer/consumer patterns.
155
+
156
+ Returns:
157
+ asyncio.Queue instance for testing.
158
+ """
159
+ return asyncio.Queue()
160
+
161
+
162
+ @pytest.fixture
163
+ async def async_lock():
164
+ """
165
+ Create an async lock for testing synchronization.
166
+
167
+ Returns:
168
+ asyncio.Lock instance for testing.
169
+ """
170
+ return asyncio.Lock()
171
+
172
+
173
+ @pytest.fixture
174
+ def mock_async_sleep():
175
+ """
176
+ Mock asyncio.sleep to speed up tests.
177
+
178
+ Returns:
179
+ Mock that replaces asyncio.sleep with instant return.
180
+ """
181
+ original_sleep = asyncio.sleep
182
+
183
+ async def instant_sleep(seconds):
184
+ """Sleep replacement that returns immediately."""
185
+ return None
186
+
187
+ asyncio.sleep = instant_sleep
188
+
189
+ yield instant_sleep
190
+
191
+ # Restore original
192
+ asyncio.sleep = original_sleep
193
+
194
+
195
+ @pytest.fixture
196
+ def async_gather_helper():
197
+ """
198
+ Helper for testing asyncio.gather operations.
199
+
200
+ Returns:
201
+ Function to gather async results with error handling.
202
+ """
203
+
204
+ async def _gather(*coroutines, return_exceptions: bool = False):
205
+ """
206
+ Gather results from multiple coroutines.
207
+
208
+ Args:
209
+ *coroutines: Coroutines to gather
210
+ return_exceptions: Whether to return exceptions as results
211
+
212
+ Returns:
213
+ List of results from coroutines
214
+ """
215
+ return await asyncio.gather(*coroutines, return_exceptions=return_exceptions)
216
+
217
+ return _gather
218
+
219
+
220
+ @pytest.fixture
221
+ def async_task_group():
222
+ """
223
+ Manage a group of async tasks with cleanup.
224
+
225
+ Returns:
226
+ AsyncTaskGroup instance for managing tasks.
227
+ """
228
+
229
+ class AsyncTaskGroup:
230
+ def __init__(self):
231
+ self.tasks = []
232
+
233
+ def create_task(self, coro):
234
+ """Create and track a task."""
235
+ task = asyncio.create_task(coro)
236
+ self.tasks.append(task)
237
+ return task
238
+
239
+ async def wait_all(self, timeout: float = None):
240
+ """Wait for all tasks to complete."""
241
+ if not self.tasks:
242
+ return []
243
+
244
+ done, pending = await asyncio.wait(
245
+ self.tasks, timeout=timeout, return_when=asyncio.ALL_COMPLETED
246
+ )
247
+
248
+ if pending:
249
+ for task in pending:
250
+ task.cancel()
251
+
252
+ results = []
253
+ for task in done:
254
+ try:
255
+ results.append(task.result())
256
+ except Exception as e:
257
+ results.append(e)
258
+
259
+ return results
260
+
261
+ async def cancel_all(self):
262
+ """Cancel all tasks."""
263
+ for task in self.tasks:
264
+ if not task.done():
265
+ task.cancel()
266
+
267
+ if self.tasks:
268
+ await asyncio.gather(*self.tasks, return_exceptions=True)
269
+
270
+ async def __aenter__(self):
271
+ return self
272
+
273
+ async def __aexit__(self, *args):
274
+ await self.cancel_all()
275
+
276
+ return AsyncTaskGroup()
277
+
278
+
279
+ @pytest.fixture
280
+ def async_condition_waiter():
281
+ """
282
+ Helper for waiting on async conditions in tests.
283
+
284
+ Returns:
285
+ Function to wait for conditions with timeout.
286
+ """
287
+
288
+ async def _wait_for(
289
+ condition: Callable[[], bool], timeout: float = 5.0, interval: float = 0.1
290
+ ):
291
+ """
292
+ Wait for a condition to become true.
293
+
294
+ Args:
295
+ condition: Function that returns True when condition is met
296
+ timeout: Maximum wait time
297
+ interval: Check interval
298
+
299
+ Returns:
300
+ True if condition met, False if timeout
301
+ """
302
+ start = asyncio.get_event_loop().time()
303
+
304
+ while asyncio.get_event_loop().time() - start < timeout:
305
+ if condition():
306
+ return True
307
+ await asyncio.sleep(interval)
308
+
309
+ return False
310
+
311
+ return _wait_for
312
+
313
+
314
+ @pytest.fixture
315
+ def async_pipeline():
316
+ """
317
+ Create an async pipeline for testing data flow.
318
+
319
+ Returns:
320
+ AsyncPipeline instance for chaining async operations.
321
+ """
322
+
323
+ class AsyncPipeline:
324
+ def __init__(self):
325
+ self.stages = []
326
+ self.results = []
327
+
328
+ def add_stage(self, func: Callable):
329
+ """Add a processing stage."""
330
+ self.stages.append(func)
331
+ return self
332
+
333
+ async def process(self, data):
334
+ """Process data through all stages."""
335
+ result = data
336
+ for stage in self.stages:
337
+ if asyncio.iscoroutinefunction(stage):
338
+ result = await stage(result)
339
+ else:
340
+ result = stage(result)
341
+ self.results.append(result)
342
+ return result
343
+
344
+ async def process_batch(self, items: list):
345
+ """Process multiple items."""
346
+ tasks = [self.process(item) for item in items]
347
+ return await asyncio.gather(*tasks)
348
+
349
+ def clear(self):
350
+ """Clear stages and results."""
351
+ self.stages.clear()
352
+ self.results.clear()
353
+
354
+ return AsyncPipeline()
355
+
356
+
357
+ @pytest.fixture
358
+ def async_rate_limiter():
359
+ """
360
+ Create an async rate limiter for testing.
361
+
362
+ Returns:
363
+ AsyncRateLimiter instance for controlling request rates.
364
+ """
365
+
366
+ class AsyncRateLimiter:
367
+ def __init__(self, rate: int = 10, per: float = 1.0):
368
+ self.rate = rate
369
+ self.per = per
370
+ self.allowance = rate
371
+ self.last_check = asyncio.get_event_loop().time()
372
+
373
+ async def acquire(self):
374
+ """Acquire permission to proceed."""
375
+ current = asyncio.get_event_loop().time()
376
+ time_passed = current - self.last_check
377
+ self.last_check = current
378
+
379
+ self.allowance += time_passed * (self.rate / self.per)
380
+ if self.allowance > self.rate:
381
+ self.allowance = self.rate
382
+
383
+ if self.allowance < 1.0:
384
+ sleep_time = (1.0 - self.allowance) * (self.per / self.rate)
385
+ await asyncio.sleep(sleep_time)
386
+ self.allowance = 0.0
387
+ else:
388
+ self.allowance -= 1.0
389
+
390
+ async def __aenter__(self):
391
+ await self.acquire()
392
+ return self
393
+
394
+ async def __aexit__(self, *args):
395
+ pass
396
+
397
+ return AsyncRateLimiter()
398
+
399
+
400
+ __all__ = [
401
+ "async_condition_waiter",
402
+ "async_context_manager",
403
+ "async_gather_helper",
404
+ "async_iterator",
405
+ "async_lock",
406
+ "async_pipeline",
407
+ "async_queue",
408
+ "async_rate_limiter",
409
+ "async_task_group",
410
+ "async_timeout",
411
+ "clean_event_loop",
412
+ "event_loop_policy",
413
+ "mock_async_sleep",
414
+ ]