ez-a-sync 0.32.29__cp310-cp310-win32.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.

Potentially problematic release.


This version of ez-a-sync might be problematic. Click here for more details.

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 +22803 -0
  5. a_sync/_smart.cp310-win32.pyd +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 +20528 -0
  12. a_sync/a_sync/_descriptor.cp310-win32.pyd +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 +6074 -0
  16. a_sync/a_sync/_flags.cp310-win32.pyd +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 +14521 -0
  20. a_sync/a_sync/_helpers.cp310-win32.pyd +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 +12194 -0
  25. a_sync/a_sync/_kwargs.cp310-win32.pyd +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 +12411 -0
  30. a_sync/a_sync/abstract.cp310-win32.pyd +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 +14932 -0
  34. a_sync/a_sync/base.cp310-win32.pyd +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.cp310-win32.pyd +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 +37846 -0
  45. a_sync/a_sync/function.cp310-win32.pyd +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 +29774 -0
  50. a_sync/a_sync/method.cp310-win32.pyd +0 -0
  51. a_sync/a_sync/method.pxd +9 -0
  52. a_sync/a_sync/method.pyi +525 -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 +16149 -0
  60. a_sync/a_sync/modifiers/manager.cp310-win32.pyd +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 +27260 -0
  66. a_sync/a_sync/property.cp310-win32.pyd +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 +20386 -0
  74. a_sync/async_property/cached.cp310-win32.pyd +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 +34654 -0
  79. a_sync/async_property/proxy.cp310-win32.pyd +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 +18841 -0
  86. a_sync/asyncio/as_completed.cp310-win32.pyd +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 +15902 -0
  91. a_sync/asyncio/create_task.cp310-win32.pyd +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 +16679 -0
  96. a_sync/asyncio/gather.cp310-win32.pyd +0 -0
  97. a_sync/asyncio/gather.pyi +107 -0
  98. a_sync/asyncio/gather.pyx +218 -0
  99. a_sync/asyncio/igather.c +12676 -0
  100. a_sync/asyncio/igather.cp310-win32.pyd +0 -0
  101. a_sync/asyncio/igather.pxd +1 -0
  102. a_sync/asyncio/igather.pyi +7 -0
  103. a_sync/asyncio/igather.pyx +182 -0
  104. a_sync/asyncio/sleep.c +9593 -0
  105. a_sync/asyncio/sleep.cp310-win32.pyd +0 -0
  106. a_sync/asyncio/sleep.pyi +14 -0
  107. a_sync/asyncio/sleep.pyx +49 -0
  108. a_sync/debugging.c +15362 -0
  109. a_sync/debugging.cp310-win32.pyd +0 -0
  110. a_sync/debugging.pyi +76 -0
  111. a_sync/debugging.pyx +107 -0
  112. a_sync/exceptions.c +13312 -0
  113. a_sync/exceptions.cp310-win32.pyd +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 +12738 -0
  118. a_sync/functools.cp310-win32.pyd +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 +37271 -0
  124. a_sync/iter.cp310-win32.pyd +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 +15757 -0
  131. a_sync/primitives/_debug.cp310-win32.pyd +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 +11529 -0
  136. a_sync/primitives/_loggable.cp310-win32.pyd +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 +17679 -0
  143. a_sync/primitives/locks/counter.cp310-win32.pyd +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 +260 -0
  147. a_sync/primitives/locks/event.c +17063 -0
  148. a_sync/primitives/locks/event.cp310-win32.pyd +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 +25590 -0
  153. a_sync/primitives/locks/prio_semaphore.cp310-win32.pyd +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 +26509 -0
  158. a_sync/primitives/locks/semaphore.cp310-win32.pyd +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 +1022 -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 +932 -0
  167. a_sync/utils/__init__.py +105 -0
  168. a_sync/utils/iterators.py +297 -0
  169. a_sync/utils/repr.c +15799 -0
  170. a_sync/utils/repr.cp310-win32.pyd +0 -0
  171. a_sync/utils/repr.pyi +2 -0
  172. a_sync/utils/repr.pyx +73 -0
  173. ez_a_sync-0.32.29.dist-info/METADATA +367 -0
  174. ez_a_sync-0.32.29.dist-info/RECORD +177 -0
  175. ez_a_sync-0.32.29.dist-info/WHEEL +5 -0
  176. ez_a_sync-0.32.29.dist-info/licenses/LICENSE.txt +17 -0
  177. ez_a_sync-0.32.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,12 @@
1
+
2
+ from a_sync.primitives._debug cimport _DebugDaemonMixin
3
+ from a_sync.primitives.locks.event cimport CythonEvent as Event
4
+
5
+ cdef class CounterLock(_DebugDaemonMixin):
6
+ cdef char* __name
7
+ cdef long long _value
8
+ cdef list _heap
9
+ cdef dict[long long, Event] _events
10
+ cpdef bint is_ready(self, long long v)
11
+ cdef inline bint c_is_ready(self, long long v)
12
+ cpdef void set(self, long long value)
@@ -0,0 +1,151 @@
1
+ from _typeshed import Incomplete
2
+ from a_sync.primitives._debug import _DebugDaemonMixin
3
+ from a_sync.primitives.locks import Event
4
+ from typing import Iterable, Optional
5
+
6
+ class CounterLock(_DebugDaemonMixin):
7
+ """
8
+ An async primitive that uses an internal counter to manage task synchronization.
9
+
10
+ A coroutine can `await counter.wait_for(3)` and it will wait until the internal counter >= 3.
11
+ If some other task executes `counter.value = 5` or `counter.set(5)`, the first coroutine will proceed as 5 >= 3.
12
+
13
+ The internal counter can only be set to a value greater than the current value.
14
+
15
+ See Also:
16
+ :class:`CounterLockCluster` for managing multiple :class:`CounterLock` instances.
17
+ """
18
+
19
+ is_ready: Incomplete
20
+ def __init__(self, start_value: int = 0, name: Optional[str] = None) -> None:
21
+ """
22
+ Initializes the :class:`CounterLock` with a starting value and an optional name.
23
+
24
+ Args:
25
+ start_value: The initial value of the counter.
26
+ name: An optional name for the counter, used in debug logs.
27
+
28
+ Examples:
29
+ >>> counter = CounterLock(start_value=0, name="example_counter")
30
+ >>> counter.value
31
+ 0
32
+ """
33
+
34
+ async def wait_for(self, value: int) -> bool:
35
+ """
36
+ Waits until the counter reaches or exceeds the specified value.
37
+
38
+ This method will ensure the debug daemon is running if the counter is not ready.
39
+
40
+ Args:
41
+ value: The value to wait for.
42
+
43
+ Examples:
44
+ >>> counter = CounterLock(start_value=0)
45
+ >>> await counter.wait_for(5) # This will block until counter.value >= 5
46
+
47
+ See Also:
48
+ :meth:`CounterLock.set` to set the counter value.
49
+ """
50
+
51
+ def set(self, value: int) -> None:
52
+ """
53
+ Sets the counter to the specified value.
54
+
55
+ This method internally uses the `value` property to enforce that the new value must be strictly greater than the current value.
56
+
57
+ Args:
58
+ value: The value to set the counter to. Must be strictly greater than the current value.
59
+
60
+ Raises:
61
+ ValueError: If the new value is less than or equal to the current value.
62
+
63
+ Examples:
64
+ >>> counter = CounterLock(start_value=0)
65
+ >>> counter.set(5)
66
+ >>> counter.value
67
+ 5
68
+
69
+ See Also:
70
+ :meth:`CounterLock.value` for direct value assignment.
71
+ """
72
+
73
+ @property
74
+ def value(self) -> int:
75
+ """
76
+ Gets the current value of the counter.
77
+
78
+ Examples:
79
+ >>> counter = CounterLock(start_value=0)
80
+ >>> counter.value
81
+ 0
82
+ """
83
+
84
+ @value.setter
85
+ def value(self, value: int) -> None:
86
+ """
87
+ Sets the counter to a new value, waking up any waiters if the value increases beyond the value they are awaiting.
88
+
89
+ Args:
90
+ value: The new value of the counter.
91
+
92
+ Raises:
93
+ ValueError: If the new value is less than the current value.
94
+
95
+ Examples:
96
+ >>> counter = CounterLock(start_value=0)
97
+ >>> counter.value = 5
98
+ >>> counter.value
99
+ 5
100
+ >>> counter.value = 3
101
+ Traceback (most recent call last):
102
+ ...
103
+ ValueError: You cannot decrease the value.
104
+ """
105
+
106
+ async def _debug_daemon(self) -> None:
107
+ """
108
+ Periodically logs debug information about the counter state and waiters.
109
+
110
+ This method is used internally to provide debugging information when debug logging is enabled.
111
+
112
+ This code will only run if `self.logger.isEnabledFor(logging.DEBUG)` is True. You do not need to include any level checks in your custom implementations.
113
+ """
114
+
115
+ class CounterLockCluster:
116
+ """
117
+ An asyncio primitive that represents a collection of :class:`CounterLock` objects.
118
+
119
+ `wait_for(i)` will wait until the value of all :class:`CounterLock` objects is >= i.
120
+
121
+ See Also:
122
+ :class:`CounterLock` for managing individual counters.
123
+ """
124
+
125
+ locks: Incomplete
126
+ def __init__(self, counter_locks: Iterable[CounterLock]) -> None:
127
+ """
128
+ Initializes the :class:`CounterLockCluster` with a collection of :class:`CounterLock` objects.
129
+
130
+ Args:
131
+ counter_locks: The :class:`CounterLock` objects to manage.
132
+
133
+ Examples:
134
+ >>> lock1 = CounterLock(start_value=0)
135
+ >>> lock2 = CounterLock(start_value=0)
136
+ >>> cluster = CounterLockCluster([lock1, lock2])
137
+ """
138
+
139
+ async def wait_for(self, value: int) -> bool:
140
+ """
141
+ Waits until the value of all :class:`CounterLock` objects in the cluster reaches or exceeds the specified value.
142
+
143
+ Args:
144
+ value: The value to wait for.
145
+
146
+ Examples:
147
+ >>> lock1 = CounterLock(start_value=0)
148
+ >>> lock2 = CounterLock(start_value=0)
149
+ >>> cluster = CounterLockCluster([lock1, lock2])
150
+ >>> await cluster.wait_for(5) # This will block until all locks have value >= 5
151
+ """
@@ -0,0 +1,260 @@
1
+ """
2
+ This module provides two specialized async flow management classes, :class:`CounterLock` and :class:`CounterLockCluster`.
3
+
4
+ These primitives manage synchronization of tasks that must wait for an internal counter to reach a specific value.
5
+ """
6
+
7
+ from asyncio import sleep
8
+ from heapq import heappop, heappush
9
+ from libc.string cimport strcpy
10
+ from libc.stdlib cimport malloc, free
11
+ from libc.time cimport time
12
+ from typing import Iterable
13
+
14
+ from a_sync.asyncio cimport cigather
15
+ from a_sync.primitives._debug cimport _DebugDaemonMixin
16
+ from a_sync.primitives.locks.event cimport CythonEvent as Event
17
+
18
+ cdef extern from "time.h":
19
+ ctypedef long time_t
20
+
21
+
22
+ cdef class CounterLock(_DebugDaemonMixin):
23
+ """
24
+ An async primitive that uses an internal counter to manage task synchronization.
25
+
26
+ A coroutine can `await counter.wait_for(3)` and it will wait until the internal counter >= 3.
27
+ If some other task executes `counter.value = 5` or `counter.set(5)`, the first coroutine will proceed as 5 >= 3.
28
+
29
+ The internal counter can only be set to a value greater than the current value.
30
+
31
+ See Also:
32
+ :class:`CounterLockCluster` for managing multiple :class:`CounterLock` instances.
33
+ """
34
+
35
+ def __cinit__(self):
36
+ self._events = {}
37
+ """A dictionary that maps each awaited value to an :class:`Event` that manages the waiters for that value."""
38
+
39
+ self._heap = []
40
+
41
+ def __init__(self, start_value: int = 0, str name = ""):
42
+ """
43
+ Initializes the :class:`CounterLock` with a starting value and an optional name.
44
+
45
+ Args:
46
+ start_value: The initial value of the counter.
47
+ name: An optional name for the counter, used in debug logs.
48
+
49
+ Examples:
50
+ >>> counter = CounterLock(start_value=0, name="example_counter")
51
+ >>> counter.value
52
+ 0
53
+ """
54
+ # we need a constant to coerce to char*
55
+ cdef bytes encoded_name = name.encode("utf-8")
56
+ cdef Py_ssize_t length = len(encoded_name)
57
+
58
+ # Allocate memory for the char* and add 1 for the null character
59
+ self.__name = <char*>malloc(length + 1)
60
+ """An optional name for the counter, used in debug logs."""
61
+
62
+ if self.__name == NULL:
63
+ raise MemoryError("Failed to allocate memory for __name.")
64
+ # Copy the bytes data into the char*
65
+ strcpy(self.__name, encoded_name)
66
+
67
+ self._value = start_value
68
+ """The current value of the counter."""
69
+
70
+ def __dealloc__(self):
71
+ # Free the memory allocated for __name
72
+ if self.__name is not NULL:
73
+ free(self.__name)
74
+
75
+ def __repr__(self) -> str:
76
+ """
77
+ Returns a string representation of the :class:`CounterLock` instance.
78
+
79
+ The representation includes the name, current value, and the number of waiters for each awaited value.
80
+
81
+ Examples:
82
+ >>> counter = CounterLock(start_value=0, name="example_counter")
83
+ >>> repr(counter)
84
+ '<CounterLock name=example_counter value=0 waiters={}>'
85
+ """
86
+ cdef dict[long long, Event] events = self._events
87
+ cdef dict[long long, Py_ssize_t] waiters = {v: len((<Event>events[v])._waiters) for v in self._heap}
88
+ cdef str name = self.__name.decode("utf-8")
89
+ if name:
90
+ return "<CounterLock name={} value={} waiters={}>".format(self.__name.decode("utf-8"), self._value, waiters)
91
+ else:
92
+ return "<CounterLock value={} waiters={}>".format(self._value, waiters)
93
+
94
+ cpdef bint is_ready(self, long long v):
95
+ """A function that indicates whether the current counter value is greater than or equal to a given value."""
96
+ return self._value >= v
97
+
98
+ cdef inline bint c_is_ready(self, long long v):
99
+ return self._value >= v
100
+
101
+ async def wait_for(self, long long value) -> bint:
102
+ """
103
+ Waits until the counter reaches or exceeds the specified value.
104
+
105
+ This method will ensure the debug daemon is running if the counter is not ready.
106
+
107
+ Args:
108
+ value: The value to wait for.
109
+
110
+ Examples:
111
+ >>> counter = CounterLock(start_value=0)
112
+ >>> await counter.wait_for(5) # This will block until counter.value >= 5
113
+
114
+ See Also:
115
+ :meth:`CounterLock.set` to set the counter value.
116
+ """
117
+ cdef Event event
118
+ if not self.c_is_ready(value):
119
+ event = self._events.get(value)
120
+ if event is None:
121
+ event = Event()
122
+ self._events[value] = event
123
+ heappush(self._heap, value)
124
+ self._c_ensure_debug_daemon((),{})
125
+ await event.c_wait()
126
+ return True
127
+
128
+ cpdef void set(self, long long value):
129
+ """
130
+ Sets the counter to the specified value.
131
+
132
+ This method internally uses the `value` property to enforce that the new value must be strictly greater than the current value.
133
+
134
+ Args:
135
+ value: The value to set the counter to. Must be strictly greater than the current value.
136
+
137
+ Raises:
138
+ ValueError: If the new value is less than or equal to the current value.
139
+
140
+ Examples:
141
+ >>> counter = CounterLock(start_value=0)
142
+ >>> counter.set(5)
143
+ >>> counter.value
144
+ 5
145
+
146
+ See Also:
147
+ :meth:`CounterLock.value` for direct value assignment.
148
+ """
149
+ cdef long long key
150
+ if value > self._value:
151
+ self._value = value
152
+ while self._heap:
153
+ key = heappop(self._heap)
154
+ if key <= self._value:
155
+ (<Event>self._events.pop(key)).set()
156
+ else:
157
+ heappush(self._heap, key)
158
+ return
159
+ elif value < self._value:
160
+ raise ValueError("You cannot decrease the value.")
161
+
162
+ @property
163
+ def value(self) -> int:
164
+ """
165
+ Gets the current value of the counter.
166
+
167
+ Examples:
168
+ >>> counter = CounterLock(start_value=0)
169
+ >>> counter.value
170
+ 0
171
+ """
172
+ return self._value
173
+
174
+ @value.setter
175
+ def value(self, long long value) -> None:
176
+ """
177
+ Sets the counter to a new value, waking up any waiters if the value increases beyond the value they are awaiting.
178
+
179
+ Args:
180
+ value: The new value of the counter.
181
+
182
+ Raises:
183
+ ValueError: If the new value is less than the current value.
184
+
185
+ Examples:
186
+ >>> counter = CounterLock(start_value=0)
187
+ >>> counter.value = 5
188
+ >>> counter.value
189
+ 5
190
+ >>> counter.value = 3
191
+ Traceback (most recent call last):
192
+ ...
193
+ ValueError: You cannot decrease the value.
194
+ """
195
+ self.set(value)
196
+
197
+ @property
198
+ def _name(self) -> str:
199
+ return self.__name.decode("utf-8")
200
+
201
+ async def _debug_daemon(self) -> None:
202
+ """
203
+ Periodically logs debug information about the counter state and waiters.
204
+
205
+ This method is used internally to provide debugging information when debug logging is enabled.
206
+
207
+ This code will only run if `self.logger.isEnabledFor(logging.DEBUG)` is True. You do not need to include any level checks in your custom implementations.
208
+ """
209
+ cdef time_t start, now
210
+ start = time(NULL)
211
+ await sleep(300)
212
+ while self._events:
213
+ now = time(NULL)
214
+ self.get_logger().debug(
215
+ "%s is still locked after %.2fm", self, (now - start) / 60
216
+ )
217
+ await sleep(300)
218
+
219
+
220
+ class CounterLockCluster:
221
+ """
222
+ An asyncio primitive that represents a collection of :class:`CounterLock` objects.
223
+
224
+ `wait_for(i)` will wait until the value of all :class:`CounterLock` objects is >= i.
225
+
226
+ See Also:
227
+ :class:`CounterLock` for managing individual counters.
228
+ """
229
+
230
+ __slots__ = ("locks",)
231
+
232
+ def __init__(self, counter_locks: Iterable[CounterLock]) -> None:
233
+ """
234
+ Initializes the :class:`CounterLockCluster` with a collection of :class:`CounterLock` objects.
235
+
236
+ Args:
237
+ counter_locks: The :class:`CounterLock` objects to manage.
238
+
239
+ Examples:
240
+ >>> lock1 = CounterLock(start_value=0)
241
+ >>> lock2 = CounterLock(start_value=0)
242
+ >>> cluster = CounterLockCluster([lock1, lock2])
243
+ """
244
+ self.locks = list(counter_locks)
245
+
246
+ async def wait_for(self, value: int) -> bint:
247
+ """
248
+ Waits until the value of all :class:`CounterLock` objects in the cluster reaches or exceeds the specified value.
249
+
250
+ Args:
251
+ value: The value to wait for.
252
+
253
+ Examples:
254
+ >>> lock1 = CounterLock(start_value=0)
255
+ >>> lock2 = CounterLock(start_value=0)
256
+ >>> cluster = CounterLockCluster([lock1, lock2])
257
+ >>> await cluster.wait_for(5) # This will block until all locks have value >= 5
258
+ """
259
+ await cigather(counter_lock.wait_for(value) for counter_lock in self.locks)
260
+ return True