ez-a-sync 0.33.4__cp313-cp313-musllinux_1_2_i686.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 (177) hide show
  1. a_sync/ENVIRONMENT_VARIABLES.py +42 -0
  2. a_sync/__init__.pxd +2 -0
  3. a_sync/__init__.py +145 -0
  4. a_sync/_smart.c +22830 -0
  5. a_sync/_smart.cpython-313-i386-linux-musl.so +0 -0
  6. a_sync/_smart.pxd +2 -0
  7. a_sync/_smart.pyi +202 -0
  8. a_sync/_smart.pyx +674 -0
  9. a_sync/_typing.py +258 -0
  10. a_sync/a_sync/__init__.py +60 -0
  11. a_sync/a_sync/_descriptor.c +20537 -0
  12. a_sync/a_sync/_descriptor.cpython-313-i386-linux-musl.so +0 -0
  13. a_sync/a_sync/_descriptor.pyi +33 -0
  14. a_sync/a_sync/_descriptor.pyx +422 -0
  15. a_sync/a_sync/_flags.c +6082 -0
  16. a_sync/a_sync/_flags.cpython-313-i386-linux-musl.so +0 -0
  17. a_sync/a_sync/_flags.pxd +3 -0
  18. a_sync/a_sync/_flags.pyx +92 -0
  19. a_sync/a_sync/_helpers.c +14529 -0
  20. a_sync/a_sync/_helpers.cpython-313-i386-linux-musl.so +0 -0
  21. a_sync/a_sync/_helpers.pxd +3 -0
  22. a_sync/a_sync/_helpers.pyi +10 -0
  23. a_sync/a_sync/_helpers.pyx +167 -0
  24. a_sync/a_sync/_kwargs.c +12202 -0
  25. a_sync/a_sync/_kwargs.cpython-313-i386-linux-musl.so +0 -0
  26. a_sync/a_sync/_kwargs.pxd +2 -0
  27. a_sync/a_sync/_kwargs.pyx +64 -0
  28. a_sync/a_sync/_meta.py +210 -0
  29. a_sync/a_sync/abstract.c +12420 -0
  30. a_sync/a_sync/abstract.cpython-313-i386-linux-musl.so +0 -0
  31. a_sync/a_sync/abstract.pyi +141 -0
  32. a_sync/a_sync/abstract.pyx +221 -0
  33. a_sync/a_sync/base.c +14940 -0
  34. a_sync/a_sync/base.cpython-313-i386-linux-musl.so +0 -0
  35. a_sync/a_sync/base.pyi +60 -0
  36. a_sync/a_sync/base.pyx +271 -0
  37. a_sync/a_sync/config.py +168 -0
  38. a_sync/a_sync/decorator.py +651 -0
  39. a_sync/a_sync/flags.c +5272 -0
  40. a_sync/a_sync/flags.cpython-313-i386-linux-musl.so +0 -0
  41. a_sync/a_sync/flags.pxd +72 -0
  42. a_sync/a_sync/flags.pyi +74 -0
  43. a_sync/a_sync/flags.pyx +72 -0
  44. a_sync/a_sync/function.c +37856 -0
  45. a_sync/a_sync/function.cpython-313-i386-linux-musl.so +0 -0
  46. a_sync/a_sync/function.pxd +28 -0
  47. a_sync/a_sync/function.pyi +571 -0
  48. a_sync/a_sync/function.pyx +1381 -0
  49. a_sync/a_sync/method.c +29662 -0
  50. a_sync/a_sync/method.cpython-313-i386-linux-musl.so +0 -0
  51. a_sync/a_sync/method.pxd +9 -0
  52. a_sync/a_sync/method.pyi +523 -0
  53. a_sync/a_sync/method.pyx +1023 -0
  54. a_sync/a_sync/modifiers/__init__.pxd +1 -0
  55. a_sync/a_sync/modifiers/__init__.py +101 -0
  56. a_sync/a_sync/modifiers/cache/__init__.py +160 -0
  57. a_sync/a_sync/modifiers/cache/memory.py +165 -0
  58. a_sync/a_sync/modifiers/limiter.py +132 -0
  59. a_sync/a_sync/modifiers/manager.c +16157 -0
  60. a_sync/a_sync/modifiers/manager.cpython-313-i386-linux-musl.so +0 -0
  61. a_sync/a_sync/modifiers/manager.pxd +5 -0
  62. a_sync/a_sync/modifiers/manager.pyi +219 -0
  63. a_sync/a_sync/modifiers/manager.pyx +299 -0
  64. a_sync/a_sync/modifiers/semaphores.py +173 -0
  65. a_sync/a_sync/property.c +27268 -0
  66. a_sync/a_sync/property.cpython-313-i386-linux-musl.so +0 -0
  67. a_sync/a_sync/property.pyi +376 -0
  68. a_sync/a_sync/property.pyx +819 -0
  69. a_sync/a_sync/singleton.py +63 -0
  70. a_sync/aliases.py +3 -0
  71. a_sync/async_property/__init__.pxd +1 -0
  72. a_sync/async_property/__init__.py +1 -0
  73. a_sync/async_property/cached.c +20397 -0
  74. a_sync/async_property/cached.cpython-313-i386-linux-musl.so +0 -0
  75. a_sync/async_property/cached.pxd +10 -0
  76. a_sync/async_property/cached.pyi +45 -0
  77. a_sync/async_property/cached.pyx +178 -0
  78. a_sync/async_property/proxy.c +34662 -0
  79. a_sync/async_property/proxy.cpython-313-i386-linux-musl.so +0 -0
  80. a_sync/async_property/proxy.pxd +2 -0
  81. a_sync/async_property/proxy.pyi +124 -0
  82. a_sync/async_property/proxy.pyx +474 -0
  83. a_sync/asyncio/__init__.pxd +6 -0
  84. a_sync/asyncio/__init__.py +164 -0
  85. a_sync/asyncio/as_completed.c +18849 -0
  86. a_sync/asyncio/as_completed.cpython-313-i386-linux-musl.so +0 -0
  87. a_sync/asyncio/as_completed.pxd +8 -0
  88. a_sync/asyncio/as_completed.pyi +109 -0
  89. a_sync/asyncio/as_completed.pyx +269 -0
  90. a_sync/asyncio/create_task.c +15912 -0
  91. a_sync/asyncio/create_task.cpython-313-i386-linux-musl.so +0 -0
  92. a_sync/asyncio/create_task.pxd +2 -0
  93. a_sync/asyncio/create_task.pyi +51 -0
  94. a_sync/asyncio/create_task.pyx +271 -0
  95. a_sync/asyncio/gather.c +16687 -0
  96. a_sync/asyncio/gather.cpython-313-i386-linux-musl.so +0 -0
  97. a_sync/asyncio/gather.pyi +107 -0
  98. a_sync/asyncio/gather.pyx +218 -0
  99. a_sync/asyncio/igather.c +13080 -0
  100. a_sync/asyncio/igather.cpython-313-i386-linux-musl.so +0 -0
  101. a_sync/asyncio/igather.pxd +1 -0
  102. a_sync/asyncio/igather.pyi +8 -0
  103. a_sync/asyncio/igather.pyx +183 -0
  104. a_sync/asyncio/sleep.c +9601 -0
  105. a_sync/asyncio/sleep.cpython-313-i386-linux-musl.so +0 -0
  106. a_sync/asyncio/sleep.pyi +14 -0
  107. a_sync/asyncio/sleep.pyx +49 -0
  108. a_sync/debugging.c +15370 -0
  109. a_sync/debugging.cpython-313-i386-linux-musl.so +0 -0
  110. a_sync/debugging.pyi +76 -0
  111. a_sync/debugging.pyx +107 -0
  112. a_sync/exceptions.c +13320 -0
  113. a_sync/exceptions.cpython-313-i386-linux-musl.so +0 -0
  114. a_sync/exceptions.pyi +376 -0
  115. a_sync/exceptions.pyx +446 -0
  116. a_sync/executor.py +619 -0
  117. a_sync/functools.c +12746 -0
  118. a_sync/functools.cpython-313-i386-linux-musl.so +0 -0
  119. a_sync/functools.pxd +7 -0
  120. a_sync/functools.pyi +33 -0
  121. a_sync/functools.pyx +139 -0
  122. a_sync/future.py +1497 -0
  123. a_sync/iter.c +37279 -0
  124. a_sync/iter.cpython-313-i386-linux-musl.so +0 -0
  125. a_sync/iter.pxd +11 -0
  126. a_sync/iter.pyi +370 -0
  127. a_sync/iter.pyx +981 -0
  128. a_sync/primitives/__init__.pxd +1 -0
  129. a_sync/primitives/__init__.py +53 -0
  130. a_sync/primitives/_debug.c +15765 -0
  131. a_sync/primitives/_debug.cpython-313-i386-linux-musl.so +0 -0
  132. a_sync/primitives/_debug.pxd +12 -0
  133. a_sync/primitives/_debug.pyi +52 -0
  134. a_sync/primitives/_debug.pyx +223 -0
  135. a_sync/primitives/_loggable.c +11538 -0
  136. a_sync/primitives/_loggable.cpython-313-i386-linux-musl.so +0 -0
  137. a_sync/primitives/_loggable.pxd +4 -0
  138. a_sync/primitives/_loggable.pyi +66 -0
  139. a_sync/primitives/_loggable.pyx +102 -0
  140. a_sync/primitives/locks/__init__.pxd +8 -0
  141. a_sync/primitives/locks/__init__.py +17 -0
  142. a_sync/primitives/locks/counter.c +17938 -0
  143. a_sync/primitives/locks/counter.cpython-313-i386-linux-musl.so +0 -0
  144. a_sync/primitives/locks/counter.pxd +12 -0
  145. a_sync/primitives/locks/counter.pyi +151 -0
  146. a_sync/primitives/locks/counter.pyx +267 -0
  147. a_sync/primitives/locks/event.c +17072 -0
  148. a_sync/primitives/locks/event.cpython-313-i386-linux-musl.so +0 -0
  149. a_sync/primitives/locks/event.pxd +22 -0
  150. a_sync/primitives/locks/event.pyi +43 -0
  151. a_sync/primitives/locks/event.pyx +185 -0
  152. a_sync/primitives/locks/prio_semaphore.c +25635 -0
  153. a_sync/primitives/locks/prio_semaphore.cpython-313-i386-linux-musl.so +0 -0
  154. a_sync/primitives/locks/prio_semaphore.pxd +25 -0
  155. a_sync/primitives/locks/prio_semaphore.pyi +217 -0
  156. a_sync/primitives/locks/prio_semaphore.pyx +597 -0
  157. a_sync/primitives/locks/semaphore.c +26553 -0
  158. a_sync/primitives/locks/semaphore.cpython-313-i386-linux-musl.so +0 -0
  159. a_sync/primitives/locks/semaphore.pxd +21 -0
  160. a_sync/primitives/locks/semaphore.pyi +197 -0
  161. a_sync/primitives/locks/semaphore.pyx +454 -0
  162. a_sync/primitives/queue.py +1026 -0
  163. a_sync/py.typed +0 -0
  164. a_sync/sphinx/__init__.py +3 -0
  165. a_sync/sphinx/ext.py +289 -0
  166. a_sync/task.py +934 -0
  167. a_sync/utils/__init__.py +105 -0
  168. a_sync/utils/iterators.py +297 -0
  169. a_sync/utils/repr.c +15866 -0
  170. a_sync/utils/repr.cpython-313-i386-linux-musl.so +0 -0
  171. a_sync/utils/repr.pyi +2 -0
  172. a_sync/utils/repr.pyx +73 -0
  173. ez_a_sync-0.33.4.dist-info/METADATA +368 -0
  174. ez_a_sync-0.33.4.dist-info/RECORD +177 -0
  175. ez_a_sync-0.33.4.dist-info/WHEEL +5 -0
  176. ez_a_sync-0.33.4.dist-info/licenses/LICENSE.txt +17 -0
  177. ez_a_sync-0.33.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,105 @@
1
+ """
2
+ This module initializes the utility functions for the a_sync library, including functions for handling asynchronous
3
+ iterators and implementing asynchronous versions of the built-in any and all functions.
4
+ """
5
+
6
+ from asyncio import as_completed, ensure_future
7
+
8
+ from a_sync.utils.iterators import as_yielded, exhaust_iterator, exhaust_iterators
9
+
10
+
11
+ __all__ = [
12
+ # "all",
13
+ # "any",
14
+ "as_yielded",
15
+ "exhaust_iterator",
16
+ "exhaust_iterators",
17
+ ]
18
+
19
+
20
+ async def any(*awaitables) -> bool:
21
+ """
22
+ Asynchronously evaluates whether any of the given awaitables evaluates to True.
23
+
24
+ This function returns True if any element in the asynchronous iterable is truthy.
25
+ It short-circuits on the first truthy value. If the iterable is empty, it returns False.
26
+
27
+ Args:
28
+ *awaitables: A variable length list of awaitable objects.
29
+
30
+ Returns:
31
+ bool: True if any element is truthy, False if all are falsy or the iterable is empty.
32
+
33
+ Example:
34
+ >>> async def is_odd(x):
35
+ ... await asyncio.sleep(0.1) # Simulate some async work
36
+ ... return x % 2 != 0
37
+ ...
38
+ >>> numbers = [2, 4, 6, 7]
39
+ >>> result = await any(*[is_odd(x) for x in numbers])
40
+ >>> result
41
+ True
42
+ >>> numbers = [2, 4, 6, 8]
43
+ >>> result = await any(*[is_odd(x) for x in numbers])
44
+ >>> result
45
+ False
46
+
47
+ Note:
48
+ This function will stop iterating as soon as it encounters a truthy value.
49
+ """
50
+ futs = list(map(ensure_future, awaitables))
51
+ for fut in as_completed(futs):
52
+ try:
53
+ result = bool(await fut)
54
+ except RuntimeError as e:
55
+ if str(e) == "cannot reuse already awaited coroutine":
56
+ raise RuntimeError(str(e), fut) from e
57
+ else:
58
+ raise
59
+ if result:
60
+ for fut in futs:
61
+ fut.cancel()
62
+ return True
63
+ return False
64
+
65
+
66
+ async def all(*awaitables) -> bool:
67
+ """
68
+ Asynchronously evaluates whether all of the given awaitables evaluate to True.
69
+
70
+ This function takes multiple awaitable objects and returns True if all of them evaluate to True. It cancels
71
+ the remaining awaitables once a False result is found.
72
+
73
+ Args:
74
+ *awaitables: A variable length list of awaitable objects.
75
+
76
+ Returns:
77
+ bool: True if all elements are truthy or the iterable is empty, False otherwise.
78
+
79
+ Example:
80
+ >>> async def is_even(x):
81
+ ... return x % 2 == 0
82
+ ...
83
+ >>> numbers = [2, 4, 6, 8]
84
+ >>> result = await all(*[is_even(x) for x in numbers])
85
+ >>> result
86
+ True
87
+ >>> numbers = [2, 3, 4, 6]
88
+ >>> result = await all(*[is_even(x) for x in numbers])
89
+ >>> result
90
+ False
91
+ """
92
+ futs = list(map(ensure_future, awaitables))
93
+ for fut in as_completed(futs):
94
+ try:
95
+ result = bool(await fut)
96
+ except RuntimeError as e:
97
+ if str(e) == "cannot reuse already awaited coroutine":
98
+ raise RuntimeError(str(e), fut) from e
99
+ else:
100
+ raise
101
+ if not result:
102
+ for fut in futs:
103
+ fut.cancel()
104
+ return False
105
+ return True
@@ -0,0 +1,297 @@
1
+ """
2
+ This module provides utility functions for handling and merging asynchronous iterators. It includes functions to
3
+ exhaust async iterators, merge multiple async iterators into a single async iterator, and manage the processing
4
+ flow of items in an asynchronous context.
5
+ """
6
+
7
+ import asyncio
8
+ import asyncio.futures
9
+ import traceback
10
+ from logging import DEBUG, getLogger
11
+ from types import TracebackType
12
+
13
+ from a_sync._typing import *
14
+ from a_sync.asyncio import create_task, igather
15
+ from a_sync.primitives.queue import Queue
16
+
17
+ logger = getLogger(__name__)
18
+
19
+
20
+ async def exhaust_iterator(
21
+ iterator: AsyncIterator[T], *, queue: Optional[asyncio.Queue] = None
22
+ ) -> None:
23
+ """
24
+ Asynchronously iterates over items from the given async iterator and optionally places them into a queue.
25
+
26
+ This function is a utility to exhaust an async iterator, with an option to forward the iterated items to a provided queue-like object.
27
+ The queue should have a `put_nowait` method. This is particularly useful when dealing with asynchronous operations that produce items
28
+ to be consumed by other parts of an application, enabling a producer-consumer pattern.
29
+
30
+ Args:
31
+ iterator (AsyncIterator[T]): The async iterator to exhaust.
32
+ queue (Optional[asyncio.Queue]): An optional queue-like object where iterated items will be placed.
33
+ The queue should support the `put_nowait` method. If None, items are simply consumed.
34
+
35
+ Example:
36
+ >>> async def example():
37
+ >>> await exhaust_iterator(some_async_iterator, queue=my_queue)
38
+
39
+ See Also:
40
+ - :func:`exhaust_iterators`
41
+ - :func:`as_yielded`
42
+ """
43
+ if queue is None:
44
+ async for thing in iterator:
45
+ pass
46
+ elif __logger_is_enabled(DEBUG):
47
+ async for thing in iterator:
48
+ log_debug("putting %s from %s to queue %s", thing, iterator, queue)
49
+ queue.put_nowait(thing)
50
+ else:
51
+ async for thing in iterator:
52
+ queue.put_nowait(thing)
53
+
54
+
55
+ async def exhaust_iterators(
56
+ iterators, *, queue: Optional[asyncio.Queue] = None, join: bool = False
57
+ ) -> None:
58
+ """
59
+ Asynchronously iterates over multiple async iterators concurrently and optionally places their items into a queue.
60
+
61
+ This function leverages :func:`asyncio.gather` to concurrently exhaust multiple async iterators. It's useful in scenarios where items
62
+ from multiple async sources need to be processed or collected together, supporting concurrent operations and efficient multitasking.
63
+
64
+ Args:
65
+ iterators: A sequence of async iterators to be exhausted concurrently.
66
+ queue (Optional[Queue]): An optional queue-like object where items from all iterators will be placed. If None, items are simply consumed.
67
+ join (Optional[bool]): If a queue was provided and join is True, this coroutine will continue to run until all queue items have been processed.
68
+
69
+ Raises:
70
+ ValueError: If `join` is True but no `queue` is provided.
71
+
72
+ Example:
73
+ >>> async def example():
74
+ >>> await exhaust_iterators([iterator1, iterator2], queue=my_queue, join=True)
75
+
76
+ See Also:
77
+ - :func:`exhaust_iterator`
78
+ - :func:`as_yielded`
79
+ """
80
+ if queue is None and join:
81
+ raise ValueError("You must provide a `queue` to use kwarg `join`")
82
+
83
+ for x in await igather(
84
+ (exhaust_iterator(iterator, queue=queue) for iterator in iterators),
85
+ return_exceptions=True,
86
+ ):
87
+ if isinstance(x, Exception):
88
+ # raise it with its original traceback instead of from here
89
+ raise x.with_traceback(x.__traceback__)
90
+
91
+ if queue is not None:
92
+ queue.put_nowait(_Done())
93
+ if join:
94
+ await queue.join()
95
+
96
+
97
+ T0 = TypeVar("T0")
98
+ T1 = TypeVar("T1")
99
+ T2 = TypeVar("T2")
100
+ T3 = TypeVar("T3")
101
+ T4 = TypeVar("T4")
102
+ T5 = TypeVar("T5")
103
+ T6 = TypeVar("T6")
104
+ T7 = TypeVar("T7")
105
+ T8 = TypeVar("T8")
106
+ T9 = TypeVar("T9")
107
+
108
+
109
+ @overload
110
+ def as_yielded(*iterators: AsyncIterator[T]) -> AsyncIterator[T]: ...
111
+ @overload
112
+ def as_yielded(
113
+ iterator0: AsyncIterator[T0],
114
+ iterator1: AsyncIterator[T1],
115
+ iterator2: AsyncIterator[T2],
116
+ iterator3: AsyncIterator[T3],
117
+ iterator4: AsyncIterator[T4],
118
+ iterator5: AsyncIterator[T5],
119
+ iterator6: AsyncIterator[T6],
120
+ iterator7: AsyncIterator[T7],
121
+ iterator8: AsyncIterator[T8],
122
+ iterator9: AsyncIterator[T9],
123
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9]]: ...
124
+ @overload
125
+ def as_yielded(
126
+ iterator0: AsyncIterator[T0],
127
+ iterator1: AsyncIterator[T1],
128
+ iterator2: AsyncIterator[T2],
129
+ iterator3: AsyncIterator[T3],
130
+ iterator4: AsyncIterator[T4],
131
+ iterator5: AsyncIterator[T5],
132
+ iterator6: AsyncIterator[T6],
133
+ iterator7: AsyncIterator[T7],
134
+ iterator8: AsyncIterator[T8],
135
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4, T5, T6, T7, T8]]: ...
136
+ @overload
137
+ def as_yielded(
138
+ iterator0: AsyncIterator[T0],
139
+ iterator1: AsyncIterator[T1],
140
+ iterator2: AsyncIterator[T2],
141
+ iterator3: AsyncIterator[T3],
142
+ iterator4: AsyncIterator[T4],
143
+ iterator5: AsyncIterator[T5],
144
+ iterator6: AsyncIterator[T6],
145
+ iterator7: AsyncIterator[T7],
146
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4, T5, T6, T7]]: ...
147
+ @overload
148
+ def as_yielded(
149
+ iterator0: AsyncIterator[T0],
150
+ iterator1: AsyncIterator[T1],
151
+ iterator2: AsyncIterator[T2],
152
+ iterator3: AsyncIterator[T3],
153
+ iterator4: AsyncIterator[T4],
154
+ iterator5: AsyncIterator[T5],
155
+ iterator6: AsyncIterator[T6],
156
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4, T5, T6]]: ...
157
+ @overload
158
+ def as_yielded(
159
+ iterator0: AsyncIterator[T0],
160
+ iterator1: AsyncIterator[T1],
161
+ iterator2: AsyncIterator[T2],
162
+ iterator3: AsyncIterator[T3],
163
+ iterator4: AsyncIterator[T4],
164
+ iterator5: AsyncIterator[T5],
165
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4, T5]]: ...
166
+ @overload
167
+ def as_yielded(
168
+ iterator0: AsyncIterator[T0],
169
+ iterator1: AsyncIterator[T1],
170
+ iterator2: AsyncIterator[T2],
171
+ iterator3: AsyncIterator[T3],
172
+ iterator4: AsyncIterator[T4],
173
+ ) -> AsyncIterator[Union[T0, T1, T2, T3, T4]]: ...
174
+ @overload
175
+ def as_yielded(
176
+ iterator0: AsyncIterator[T0],
177
+ iterator1: AsyncIterator[T1],
178
+ iterator2: AsyncIterator[T2],
179
+ iterator3: AsyncIterator[T3],
180
+ ) -> AsyncIterator[Union[T0, T1, T2, T3]]: ...
181
+ @overload
182
+ def as_yielded(
183
+ iterator0: AsyncIterator[T0],
184
+ iterator1: AsyncIterator[T1],
185
+ iterator2: AsyncIterator[T2],
186
+ ) -> AsyncIterator[Union[T0, T1, T2]]: ...
187
+ @overload
188
+ def as_yielded(
189
+ iterator0: AsyncIterator[T0], iterator1: AsyncIterator[T1]
190
+ ) -> AsyncIterator[Union[T0, T1]]: ...
191
+ @overload
192
+ def as_yielded(
193
+ iterator0: AsyncIterator[T0],
194
+ iterator1: AsyncIterator[T1],
195
+ iterator2: AsyncIterator[T2],
196
+ *iterators: AsyncIterator[T],
197
+ ) -> AsyncIterator[Union[T0, T1, T2, T]]: ...
198
+ async def as_yielded(*iterators: AsyncIterator[T]) -> AsyncIterator[T]: # type: ignore [misc]
199
+ """
200
+ Merges multiple async iterators into a single async iterator that yields items as they become available from any of the source iterators.
201
+
202
+ This function is designed to streamline the handling of multiple asynchronous data streams by consolidating them into a single asynchronous iteration context.
203
+ It enables concurrent fetching and processing of items from multiple sources, improving efficiency and simplifying code structure when dealing with asynchronous operations.
204
+
205
+ The merging process is facilitated by the :func:`exhaust_iterators` function, which concurrently processes the source iterators and places their items into a queue.
206
+ This mechanism ensures that the merged stream of items is delivered in an order determined by the availability of items from the source iterators, rather than their original sequence.
207
+
208
+ The function handles exceptions and ensures robustness and reliability by using asyncio tasks and queues. It manages edge cases such as early termination and exception management.
209
+ The :class:`_Done` sentinel class is used internally to signal the completion of processing.
210
+
211
+ Args:
212
+ *iterators: Variable length list of :class:`~collections.abc.AsyncIterator` objects to be merged.
213
+
214
+ Note:
215
+ This implementation leverages asyncio tasks and queues to efficiently manage the asynchronous iteration and merging process.
216
+ It handles edge cases such as early termination and exception management, ensuring robustness and reliability.
217
+ The :class:`_Done` sentinel class is used internally to signal the completion of processing.
218
+
219
+ Example:
220
+ >>> async def example():
221
+ >>> async for item in as_yielded(iterator1, iterator2):
222
+ >>> print(item)
223
+
224
+ See Also:
225
+ - :func:`exhaust_iterator`
226
+ - :func:`exhaust_iterators`
227
+ """
228
+ # hypothesis idea: _Done should never be exposed to user, works for all desired input types
229
+ queue: Queue[Union[T, _Done]] = Queue()
230
+
231
+ def _as_yielded_done_callback(t: asyncio.Task, queue: Queue[Union[T, _Done]] = queue) -> None:
232
+ if t.cancelled():
233
+ return
234
+ if e := t.exception():
235
+ traceback.extract_stack
236
+ traceback.clear_frames(e.__traceback__)
237
+ queue.put_nowait(_Done(e))
238
+
239
+ task = create_task(
240
+ coro=exhaust_iterators(iterators, queue=queue, join=True),
241
+ name=f"a_sync.as_yielded queue populating task for {iterators}",
242
+ )
243
+
244
+ task.add_done_callback(_as_yielded_done_callback)
245
+
246
+ while not task.done():
247
+ try:
248
+ items = await queue.get_all()
249
+ except asyncio.CancelledError:
250
+ # cleanup lingering objects and reraise
251
+ del task
252
+ del queue
253
+ raise
254
+ for item in items:
255
+ queue.task_done()
256
+ if isinstance(item, _Done):
257
+ assert queue.empty()
258
+ del task
259
+ del queue
260
+ if item._exc:
261
+ raise item._exc.with_traceback(item._tb) from item._exc.__cause__
262
+ return
263
+ yield item
264
+
265
+ # ensure it isn't done due to an internal exception
266
+ await task
267
+
268
+
269
+ class _Done:
270
+ """
271
+ A sentinel class used to signal the completion of processing in the :func:`as_yielded` function.
272
+
273
+ This class acts as a marker to indicate that all items have been processed and the asynchronous iteration can be concluded.
274
+ It is used internally within the implementation of :func:`as_yielded` to efficiently manage the termination of the iteration process once all source iterators have been exhausted.
275
+
276
+ Args:
277
+ exc (Optional[Exception]): An optional exception to be associated with the completion.
278
+ """
279
+
280
+ def __init__(self, exc: Optional[Exception] = None) -> None:
281
+ self._exc = exc
282
+
283
+ @property
284
+ def _tb(self) -> TracebackType:
285
+ """Returns the traceback associated with the exception, if any."""
286
+ return self._exc.__traceback__ # type: ignore [union-attr]
287
+
288
+
289
+ __all__ = ["as_yielded", "exhaust_iterator", "exhaust_iterators"]
290
+
291
+
292
+ __logger_log = logger._log
293
+ __logger_is_enabled = logger.isEnabledFor
294
+
295
+
296
+ def log_debug(msg: str, *args) -> None:
297
+ __logger_log(DEBUG, msg, args)