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
@@ -1,520 +1,50 @@
1
1
  """
2
2
  Threading Test Fixtures and Utilities.
3
3
 
4
+ Core threading fixtures with re-exports from specialized modules.
4
5
  Fixtures for testing multi-threaded code, thread synchronization,
5
6
  and concurrent operations across the provide-io ecosystem.
6
7
  """
7
8
 
8
- import threading
9
- import time
10
- import queue
11
- from typing import Any, Callable, Optional
12
- from collections.abc import Generator
13
- from concurrent.futures import ThreadPoolExecutor
14
- from unittest.mock import Mock
15
-
16
- import pytest
17
-
18
-
19
- @pytest.fixture
20
- def test_thread():
21
- """
22
- Create a test thread with automatic cleanup.
23
-
24
- Returns:
25
- Function to create and manage test threads.
26
- """
27
- threads = []
28
-
29
- def _create_thread(target: Callable, args: tuple = (), kwargs: dict = None, daemon: bool = True) -> threading.Thread:
30
- """
31
- Create a test thread.
32
-
33
- Args:
34
- target: Function to run in thread
35
- args: Positional arguments for target
36
- kwargs: Keyword arguments for target
37
- daemon: Whether thread should be daemon
38
-
39
- Returns:
40
- Started thread instance
41
- """
42
- kwargs = kwargs or {}
43
- thread = threading.Thread(target=target, args=args, kwargs=kwargs, daemon=daemon)
44
- threads.append(thread)
45
- thread.start()
46
- return thread
47
-
48
- yield _create_thread
49
-
50
- # Cleanup: wait for all threads to complete
51
- for thread in threads:
52
- if thread.is_alive():
53
- thread.join(timeout=1.0)
54
-
55
-
56
- @pytest.fixture
57
- def thread_pool():
58
- """
59
- Create a thread pool executor for testing.
60
-
61
- Returns:
62
- ThreadPoolExecutor instance with automatic cleanup.
63
- """
64
- executor = ThreadPoolExecutor(max_workers=4)
65
- yield executor
66
- executor.shutdown(wait=True, cancel_futures=True)
67
-
68
-
69
- @pytest.fixture
70
- def thread_barrier():
71
- """
72
- Create a barrier for thread synchronization.
73
-
74
- Returns:
75
- Function to create barriers for N threads.
76
- """
77
- barriers = []
78
-
79
- def _create_barrier(n_threads: int, timeout: Optional[float] = None) -> threading.Barrier:
80
- """
81
- Create a barrier for synchronizing threads.
82
-
83
- Args:
84
- n_threads: Number of threads to synchronize
85
- timeout: Optional timeout for barrier
86
-
87
- Returns:
88
- Barrier instance
89
- """
90
- barrier = threading.Barrier(n_threads, timeout=timeout)
91
- barriers.append(barrier)
92
- return barrier
93
-
94
- yield _create_barrier
95
-
96
- # Cleanup: abort all barriers
97
- for barrier in barriers:
98
- try:
99
- barrier.abort()
100
- except threading.BrokenBarrierError:
101
- pass
102
-
103
-
104
- @pytest.fixture
105
- def thread_safe_list():
106
- """
107
- Create a thread-safe list for collecting results.
108
-
109
- Returns:
110
- Thread-safe list implementation.
111
- """
112
- class ThreadSafeList:
113
- def __init__(self):
114
- self._list = []
115
- self._lock = threading.Lock()
116
-
117
- def append(self, item: Any):
118
- """Thread-safe append."""
119
- with self._lock:
120
- self._list.append(item)
121
-
122
- def extend(self, items):
123
- """Thread-safe extend."""
124
- with self._lock:
125
- self._list.extend(items)
126
-
127
- def get_all(self) -> list:
128
- """Get copy of all items."""
129
- with self._lock:
130
- return self._list.copy()
131
-
132
- def clear(self):
133
- """Clear the list."""
134
- with self._lock:
135
- self._list.clear()
136
-
137
- def __len__(self) -> int:
138
- with self._lock:
139
- return len(self._list)
140
-
141
- def __getitem__(self, index):
142
- with self._lock:
143
- return self._list[index]
144
-
145
- return ThreadSafeList()
146
-
147
-
148
- @pytest.fixture
149
- def thread_safe_counter():
150
- """
151
- Create a thread-safe counter.
152
-
153
- Returns:
154
- Thread-safe counter implementation.
155
- """
156
- class ThreadSafeCounter:
157
- def __init__(self, initial: int = 0):
158
- self._value = initial
159
- self._lock = threading.Lock()
160
-
161
- def increment(self, amount: int = 1) -> int:
162
- """Thread-safe increment."""
163
- with self._lock:
164
- self._value += amount
165
- return self._value
166
-
167
- def decrement(self, amount: int = 1) -> int:
168
- """Thread-safe decrement."""
169
- with self._lock:
170
- self._value -= amount
171
- return self._value
172
-
173
- @property
174
- def value(self) -> int:
175
- """Get current value."""
176
- with self._lock:
177
- return self._value
178
-
179
- def reset(self, value: int = 0):
180
- """Reset counter."""
181
- with self._lock:
182
- self._value = value
183
-
184
- return ThreadSafeCounter()
185
-
186
-
187
- @pytest.fixture
188
- def thread_event():
189
- """
190
- Create thread events for signaling.
191
-
192
- Returns:
193
- Function to create thread events.
194
- """
195
- events = []
196
-
197
- def _create_event() -> threading.Event:
198
- """Create a thread event."""
199
- event = threading.Event()
200
- events.append(event)
201
- return event
202
-
203
- yield _create_event
204
-
205
- # Cleanup: set all events to release waiting threads
206
- for event in events:
207
- event.set()
208
-
209
-
210
- @pytest.fixture
211
- def thread_condition():
212
- """
213
- Create condition variables for thread coordination.
214
-
215
- Returns:
216
- Function to create condition variables.
217
- """
218
- def _create_condition(lock: Optional[threading.Lock] = None) -> threading.Condition:
219
- """
220
- Create a condition variable.
221
-
222
- Args:
223
- lock: Optional lock to use (creates new if None)
224
-
225
- Returns:
226
- Condition variable
227
- """
228
- return threading.Condition(lock)
229
-
230
- return _create_condition
231
-
232
-
233
- @pytest.fixture
234
- def mock_thread():
235
- """
236
- Create a mock thread for testing without actual threading.
237
-
238
- Returns:
239
- Mock thread object.
240
- """
241
- mock = Mock(spec=threading.Thread)
242
- mock.is_alive.return_value = False
243
- mock.daemon = False
244
- mock.name = "MockThread"
245
- mock.ident = 12345
246
- mock.start = Mock()
247
- mock.join = Mock()
248
- mock.run = Mock()
249
-
250
- return mock
251
-
252
-
253
- @pytest.fixture
254
- def thread_local_storage():
255
- """
256
- Create thread-local storage for testing.
257
-
258
- Returns:
259
- Thread-local storage object.
260
- """
261
- return threading.local()
262
-
263
-
264
- @pytest.fixture
265
- def concurrent_executor():
266
- """
267
- Helper for executing functions concurrently in tests.
268
-
269
- Returns:
270
- Concurrent execution helper.
271
- """
272
- class ConcurrentExecutor:
273
- def __init__(self):
274
- self.results = []
275
- self.exceptions = []
276
-
277
- def run_concurrent(self, func: Callable, args_list: list[tuple], max_workers: int = 4) -> list[Any]:
278
- """
279
- Run function concurrently with different arguments.
280
-
281
- Args:
282
- func: Function to execute
283
- args_list: List of argument tuples
284
- max_workers: Maximum concurrent workers
285
-
286
- Returns:
287
- List of results in order
288
- """
289
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
290
- futures = []
291
- for args in args_list:
292
- if isinstance(args, tuple):
293
- future = executor.submit(func, *args)
294
- else:
295
- future = executor.submit(func, args)
296
- futures.append(future)
297
-
298
- results = []
299
- for future in futures:
300
- try:
301
- result = future.result(timeout=10)
302
- results.append(result)
303
- self.results.append(result)
304
- except Exception as e:
305
- self.exceptions.append(e)
306
- results.append(None)
307
-
308
- return results
309
-
310
- def run_parallel(self, funcs: list[Callable], timeout: float = 10) -> list[Any]:
311
- """
312
- Run different functions in parallel.
313
-
314
- Args:
315
- funcs: List of functions to execute
316
- timeout: Timeout for each function
317
-
318
- Returns:
319
- List of results
320
- """
321
- with ThreadPoolExecutor(max_workers=len(funcs)) as executor:
322
- futures = [executor.submit(func) for func in funcs]
323
- results = []
324
-
325
- for future in futures:
326
- try:
327
- result = future.result(timeout=timeout)
328
- results.append(result)
329
- except Exception as e:
330
- self.exceptions.append(e)
331
- results.append(None)
332
-
333
- return results
334
-
335
- return ConcurrentExecutor()
336
-
337
-
338
- @pytest.fixture
339
- def thread_synchronizer():
340
- """
341
- Helper for synchronizing test threads.
342
-
343
- Returns:
344
- Thread synchronization helper.
345
- """
346
- class ThreadSynchronizer:
347
- def __init__(self):
348
- self.checkpoints = {}
349
-
350
- def checkpoint(self, name: str, thread_id: Optional[int] = None):
351
- """
352
- Record that a thread reached a checkpoint.
353
-
354
- Args:
355
- name: Checkpoint name
356
- thread_id: Optional thread ID (uses current if None)
357
- """
358
- thread_id = thread_id or threading.get_ident()
359
- if name not in self.checkpoints:
360
- self.checkpoints[name] = []
361
- self.checkpoints[name].append((thread_id, time.time()))
362
-
363
- def wait_for_checkpoint(self, name: str, count: int, timeout: float = 5.0) -> bool:
364
- """
365
- Wait for N threads to reach a checkpoint.
366
-
367
- Args:
368
- name: Checkpoint name
369
- count: Number of threads to wait for
370
- timeout: Maximum wait time
371
-
372
- Returns:
373
- True if checkpoint reached, False if timeout
374
- """
375
- start = time.time()
376
- while time.time() - start < timeout:
377
- if name in self.checkpoints and len(self.checkpoints[name]) >= count:
378
- return True
379
- time.sleep(0.01)
380
- return False
381
-
382
- def get_order(self, checkpoint: str) -> list[int]:
383
- """
384
- Get order in which threads reached checkpoint.
385
-
386
- Args:
387
- checkpoint: Checkpoint name
388
-
389
- Returns:
390
- List of thread IDs in order
391
- """
392
- if checkpoint not in self.checkpoints:
393
- return []
394
- return [tid for tid, _ in sorted(self.checkpoints[checkpoint], key=lambda x: x[1])]
395
-
396
- def clear(self):
397
- """Clear all checkpoints."""
398
- self.checkpoints.clear()
399
-
400
- return ThreadSynchronizer()
401
-
402
-
403
- @pytest.fixture
404
- def deadlock_detector():
405
- """
406
- Helper for detecting potential deadlocks in tests.
407
-
408
- Returns:
409
- Deadlock detection helper.
410
- """
411
- class DeadlockDetector:
412
- def __init__(self):
413
- self.locks_held = {} # thread_id -> set of locks
414
- self.lock = threading.Lock()
415
-
416
- def acquire(self, lock_name: str, thread_id: Optional[int] = None):
417
- """Record lock acquisition."""
418
- thread_id = thread_id or threading.get_ident()
419
- with self.lock:
420
- if thread_id not in self.locks_held:
421
- self.locks_held[thread_id] = set()
422
- self.locks_held[thread_id].add(lock_name)
423
-
424
- def release(self, lock_name: str, thread_id: Optional[int] = None):
425
- """Record lock release."""
426
- thread_id = thread_id or threading.get_ident()
427
- with self.lock:
428
- if thread_id in self.locks_held:
429
- self.locks_held[thread_id].discard(lock_name)
430
-
431
- def check_circular_wait(self) -> bool:
432
- """
433
- Check for potential circular wait conditions.
434
-
435
- Returns:
436
- True if potential deadlock detected
437
- """
438
- # Simplified check - in practice would need wait-for graph
439
- with self.lock:
440
- # Check if multiple threads hold multiple locks
441
- multi_lock_threads = [
442
- tid for tid, locks in self.locks_held.items()
443
- if len(locks) > 1
444
- ]
445
- return len(multi_lock_threads) > 1
446
-
447
- def get_held_locks(self) -> dict[int, set[str]]:
448
- """Get current lock holdings."""
449
- with self.lock:
450
- return self.locks_held.copy()
451
-
452
- return DeadlockDetector()
453
-
454
-
455
- @pytest.fixture
456
- def thread_exception_handler():
457
- """
458
- Capture exceptions from threads for testing.
459
-
460
- Returns:
461
- Exception handler for threads.
462
- """
463
- class ThreadExceptionHandler:
464
- def __init__(self):
465
- self.exceptions = []
466
- self.lock = threading.Lock()
467
-
468
- def handle(self, func: Callable) -> Callable:
469
- """
470
- Wrap function to capture exceptions.
471
-
472
- Args:
473
- func: Function to wrap
474
-
475
- Returns:
476
- Wrapped function
477
- """
478
- def wrapper(*args, **kwargs):
479
- try:
480
- return func(*args, **kwargs)
481
- except Exception as e:
482
- with self.lock:
483
- self.exceptions.append({
484
- 'thread': threading.current_thread().name,
485
- 'exception': e,
486
- 'time': time.time()
487
- })
488
- raise
489
-
490
- return wrapper
491
-
492
- def get_exceptions(self) -> list[dict]:
493
- """Get all captured exceptions."""
494
- with self.lock:
495
- return self.exceptions.copy()
496
-
497
- def assert_no_exceptions(self):
498
- """Assert no exceptions were raised."""
499
- with self.lock:
500
- if self.exceptions:
501
- raise AssertionError(f"Thread exceptions occurred: {self.exceptions}")
502
-
503
- return ThreadExceptionHandler()
504
-
9
+ # Re-export all fixtures from specialized modules
10
+ from provide.foundation.testing.threading.basic_fixtures import (
11
+ mock_thread,
12
+ test_thread,
13
+ thread_local_storage,
14
+ thread_pool,
15
+ )
16
+ from provide.foundation.testing.threading.data_fixtures import (
17
+ thread_safe_counter,
18
+ thread_safe_list,
19
+ )
20
+ from provide.foundation.testing.threading.execution_fixtures import (
21
+ concurrent_executor,
22
+ deadlock_detector,
23
+ thread_exception_handler,
24
+ thread_synchronizer,
25
+ )
26
+ from provide.foundation.testing.threading.sync_fixtures import (
27
+ thread_barrier,
28
+ thread_condition,
29
+ thread_event,
30
+ )
505
31
 
506
32
  __all__ = [
33
+ # Basic threading fixtures
507
34
  "test_thread",
508
35
  "thread_pool",
36
+ "mock_thread",
37
+ "thread_local_storage",
38
+ # Synchronization fixtures
509
39
  "thread_barrier",
510
- "thread_safe_list",
511
- "thread_safe_counter",
512
40
  "thread_event",
513
41
  "thread_condition",
514
- "mock_thread",
515
- "thread_local_storage",
42
+ # Thread-safe data structures
43
+ "thread_safe_list",
44
+ "thread_safe_counter",
45
+ # Execution and testing helpers
516
46
  "concurrent_executor",
517
47
  "thread_synchronizer",
518
48
  "deadlock_detector",
519
49
  "thread_exception_handler",
520
- ]
50
+ ]
@@ -0,0 +1,100 @@
1
+ """
2
+ Thread synchronization test fixtures.
3
+
4
+ Fixtures for thread barriers, events, conditions, and other synchronization primitives.
5
+ """
6
+
7
+ import threading
8
+
9
+ import pytest
10
+
11
+
12
+ @pytest.fixture
13
+ def thread_barrier():
14
+ """
15
+ Create a barrier for thread synchronization.
16
+
17
+ Returns:
18
+ Function to create barriers for N threads.
19
+ """
20
+ barriers = []
21
+
22
+ def _create_barrier(
23
+ n_threads: int, timeout: float | None = None
24
+ ) -> threading.Barrier:
25
+ """
26
+ Create a barrier for synchronizing threads.
27
+
28
+ Args:
29
+ n_threads: Number of threads to synchronize
30
+ timeout: Optional timeout for barrier
31
+
32
+ Returns:
33
+ Barrier instance
34
+ """
35
+ barrier = threading.Barrier(n_threads, timeout=timeout)
36
+ barriers.append(barrier)
37
+ return barrier
38
+
39
+ yield _create_barrier
40
+
41
+ # Cleanup: abort all barriers
42
+ for barrier in barriers:
43
+ try:
44
+ barrier.abort()
45
+ except threading.BrokenBarrierError:
46
+ pass
47
+
48
+
49
+ @pytest.fixture
50
+ def thread_event():
51
+ """
52
+ Create thread events for signaling.
53
+
54
+ Returns:
55
+ Function to create thread events.
56
+ """
57
+ events = []
58
+
59
+ def _create_event() -> threading.Event:
60
+ """Create a thread event."""
61
+ event = threading.Event()
62
+ events.append(event)
63
+ return event
64
+
65
+ yield _create_event
66
+
67
+ # Cleanup: set all events to release waiting threads
68
+ for event in events:
69
+ event.set()
70
+
71
+
72
+ @pytest.fixture
73
+ def thread_condition():
74
+ """
75
+ Create condition variables for thread coordination.
76
+
77
+ Returns:
78
+ Function to create condition variables.
79
+ """
80
+
81
+ def _create_condition(lock: threading.Lock | None = None) -> threading.Condition:
82
+ """
83
+ Create a condition variable.
84
+
85
+ Args:
86
+ lock: Optional lock to use (creates new if None)
87
+
88
+ Returns:
89
+ Condition variable
90
+ """
91
+ return threading.Condition(lock)
92
+
93
+ return _create_condition
94
+
95
+
96
+ __all__ = [
97
+ "thread_barrier",
98
+ "thread_condition",
99
+ "thread_event",
100
+ ]
@@ -6,27 +6,27 @@ time-dependent code across any project that depends on provide.foundation.
6
6
  """
7
7
 
8
8
  from provide.foundation.testing.time.fixtures import (
9
+ advance_time,
10
+ benchmark_timer,
9
11
  freeze_time,
12
+ mock_datetime,
10
13
  mock_sleep,
11
14
  mock_sleep_with_callback,
15
+ rate_limiter_mock,
12
16
  time_machine,
13
- timer,
14
- mock_datetime,
15
17
  time_travel,
16
- rate_limiter_mock,
17
- benchmark_timer,
18
- advance_time,
18
+ timer,
19
19
  )
20
20
 
21
21
  __all__ = [
22
+ "advance_time",
23
+ "benchmark_timer",
22
24
  "freeze_time",
25
+ "mock_datetime",
23
26
  "mock_sleep",
24
27
  "mock_sleep_with_callback",
28
+ "rate_limiter_mock",
25
29
  "time_machine",
26
- "timer",
27
- "mock_datetime",
28
30
  "time_travel",
29
- "rate_limiter_mock",
30
- "benchmark_timer",
31
- "advance_time",
32
- ]
31
+ "timer",
32
+ ]