ez-a-sync 0.32.9__cp311-cp311-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.

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 +144 -0
  4. a_sync/_smart.c +22623 -0
  5. a_sync/_smart.cpython-311-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 +652 -0
  9. a_sync/_typing.py +258 -0
  10. a_sync/a_sync/__init__.py +60 -0
  11. a_sync/a_sync/_descriptor.c +19745 -0
  12. a_sync/a_sync/_descriptor.cpython-311-i386-linux-musl.so +0 -0
  13. a_sync/a_sync/_descriptor.pyi +34 -0
  14. a_sync/a_sync/_descriptor.pyx +422 -0
  15. a_sync/a_sync/_flags.c +5687 -0
  16. a_sync/a_sync/_flags.cpython-311-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 +13718 -0
  20. a_sync/a_sync/_helpers.cpython-311-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 +10672 -0
  25. a_sync/a_sync/_kwargs.cpython-311-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 +11350 -0
  30. a_sync/a_sync/abstract.cpython-311-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 +14066 -0
  34. a_sync/a_sync/base.cpython-311-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 +562 -0
  39. a_sync/a_sync/flags.c +4471 -0
  40. a_sync/a_sync/flags.cpython-311-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 +37747 -0
  45. a_sync/a_sync/function.cpython-311-i386-linux-musl.so +0 -0
  46. a_sync/a_sync/function.pxd +24 -0
  47. a_sync/a_sync/function.pyi +556 -0
  48. a_sync/a_sync/function.pyx +1363 -0
  49. a_sync/a_sync/method.c +28387 -0
  50. a_sync/a_sync/method.cpython-311-i386-linux-musl.so +0 -0
  51. a_sync/a_sync/method.pxd +2 -0
  52. a_sync/a_sync/method.pyi +524 -0
  53. a_sync/a_sync/method.pyx +999 -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 +15262 -0
  60. a_sync/a_sync/modifiers/manager.cpython-311-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 +296 -0
  64. a_sync/a_sync/modifiers/semaphores.py +173 -0
  65. a_sync/a_sync/property.c +27952 -0
  66. a_sync/a_sync/property.cpython-311-i386-linux-musl.so +0 -0
  67. a_sync/a_sync/property.pyi +376 -0
  68. a_sync/a_sync/property.pyx +813 -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 +20542 -0
  74. a_sync/async_property/cached.cpython-311-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 +36410 -0
  79. a_sync/async_property/proxy.cpython-311-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 +18198 -0
  86. a_sync/asyncio/as_completed.cpython-311-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 +15032 -0
  91. a_sync/asyncio/create_task.cpython-311-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 +268 -0
  95. a_sync/asyncio/gather.c +15735 -0
  96. a_sync/asyncio/gather.cpython-311-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 +12334 -0
  100. a_sync/asyncio/igather.cpython-311-i386-linux-musl.so +0 -0
  101. a_sync/asyncio/igather.pxd +1 -0
  102. a_sync/asyncio/igather.pyi +7 -0
  103. a_sync/asyncio/igather.pyx +175 -0
  104. a_sync/asyncio/sleep.c +8916 -0
  105. a_sync/asyncio/sleep.cpython-311-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 +15156 -0
  109. a_sync/debugging.cpython-311-i386-linux-musl.so +0 -0
  110. a_sync/debugging.pyi +73 -0
  111. a_sync/debugging.pyx +107 -0
  112. a_sync/exceptions.c +12952 -0
  113. a_sync/exceptions.cpython-311-i386-linux-musl.so +0 -0
  114. a_sync/exceptions.pyi +376 -0
  115. a_sync/exceptions.pyx +443 -0
  116. a_sync/executor.py +575 -0
  117. a_sync/functools.c +11489 -0
  118. a_sync/functools.cpython-311-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 +37567 -0
  124. a_sync/iter.cpython-311-i386-linux-musl.so +0 -0
  125. a_sync/iter.pxd +9 -0
  126. a_sync/iter.pyi +366 -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 +14737 -0
  131. a_sync/primitives/_debug.cpython-311-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 +10569 -0
  136. a_sync/primitives/_loggable.cpython-311-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 +16972 -0
  143. a_sync/primitives/locks/counter.cpython-311-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 +260 -0
  147. a_sync/primitives/locks/event.c +16127 -0
  148. a_sync/primitives/locks/event.cpython-311-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 +183 -0
  152. a_sync/primitives/locks/prio_semaphore.c +24084 -0
  153. a_sync/primitives/locks/prio_semaphore.cpython-311-i386-linux-musl.so +0 -0
  154. a_sync/primitives/locks/prio_semaphore.pxd +24 -0
  155. a_sync/primitives/locks/prio_semaphore.pyi +217 -0
  156. a_sync/primitives/locks/prio_semaphore.pyx +554 -0
  157. a_sync/primitives/locks/semaphore.c +25816 -0
  158. a_sync/primitives/locks/semaphore.cpython-311-i386-linux-musl.so +0 -0
  159. a_sync/primitives/locks/semaphore.pxd +21 -0
  160. a_sync/primitives/locks/semaphore.pyi +196 -0
  161. a_sync/primitives/locks/semaphore.pyx +452 -0
  162. a_sync/primitives/queue.py +1018 -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 +916 -0
  167. a_sync/utils/__init__.py +105 -0
  168. a_sync/utils/iterators.py +297 -0
  169. a_sync/utils/repr.c +14354 -0
  170. a_sync/utils/repr.cpython-311-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.32.9.dist-info/METADATA +367 -0
  174. ez_a_sync-0.32.9.dist-info/RECORD +177 -0
  175. ez_a_sync-0.32.9.dist-info/WHEEL +5 -0
  176. ez_a_sync-0.32.9.dist-info/licenses/LICENSE.txt +17 -0
  177. ez_a_sync-0.32.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,554 @@
1
+ # cython: boundscheck=False
2
+ """
3
+ This module provides priority-based semaphore implementations. These semaphores allow
4
+ waiters to be assigned priorities, ensuring that higher priority waiters are
5
+ processed before lower priority ones.
6
+ """
7
+
8
+ import asyncio
9
+ import collections
10
+ import heapq
11
+ from logging import getLogger
12
+ from typing import List, Literal, Optional, Protocol, Set, Type, TypeVar
13
+
14
+ from a_sync.primitives.locks.semaphore cimport Semaphore
15
+
16
+ # cdef asyncio
17
+ cdef object Future = asyncio.Future
18
+ del asyncio
19
+
20
+ # cdef collections
21
+ cdef object deque = collections.deque
22
+ del collections
23
+
24
+ # cdef heapq
25
+ cdef object heappush = heapq.heappush
26
+ cdef object heappop = heapq.heappop
27
+ del heapq
28
+
29
+ # cdef logging
30
+ cdef public object logger = getLogger(__name__)
31
+ cdef object c_logger = logger
32
+ cdef object DEBUG = 10
33
+ cdef object _logger_log = logger._log
34
+ cdef object _logger_is_enabled = logger.isEnabledFor
35
+ del getLogger
36
+
37
+ class Priority(Protocol):
38
+ def __lt__(self, other) -> bool: ...
39
+
40
+
41
+ PT = TypeVar("PT", bound=Priority)
42
+
43
+ CM = TypeVar("CM", bound="_AbstractPrioritySemaphoreContextManager[Priority]")
44
+
45
+
46
+ cdef class _AbstractPrioritySemaphore(Semaphore):
47
+ """
48
+ A semaphore that allows prioritization of waiters.
49
+
50
+ This semaphore manages waiters with associated priorities, ensuring that waiters with higher
51
+ priorities are processed before those with lower priorities. Subclasses must define the
52
+ `_top_priority` attribute to specify the default top priority behavior.
53
+
54
+ The `_context_manager_class` attribute should specify the class used for managing semaphore contexts.
55
+
56
+ See Also:
57
+ :class:`PrioritySemaphore` for an implementation using numeric priorities.
58
+ """
59
+
60
+ def __cinit__(self) -> None:
61
+ self._context_managers = {}
62
+ """A dictionary mapping priorities to their context managers."""
63
+
64
+ self._Semaphore__waiters = []
65
+ """A heap queue of context managers, sorted by priority."""
66
+
67
+ # NOTE: This should (hopefully) be temporary
68
+ self._potential_lost_waiters: List["Future[None]"] = []
69
+ """A list of futures representing waiters that might have been lost."""
70
+
71
+ def __init__(
72
+ self,
73
+ context_manager_class: Type[_AbstractPrioritySemaphoreContextManager],
74
+ top_priority: object,
75
+ value: int = 1,
76
+ *,
77
+ name: Optional[str] = None,
78
+ ) -> None:
79
+ """Initializes the priority semaphore.
80
+
81
+ Args:
82
+ value: The initial capacity of the semaphore.
83
+ name: An optional name for the semaphore, used for debugging.
84
+
85
+ Examples:
86
+ >>> semaphore = _AbstractPrioritySemaphore(5, name="test_semaphore")
87
+ """
88
+ # context manager class is some temporary hacky shit, just ignore this
89
+ Semaphore.__init__(self, value, name=name)
90
+
91
+ self._capacity = value
92
+ """The initial capacity of the semaphore."""
93
+
94
+ self._top_priority = top_priority
95
+ self._context_manager_class = context_manager_class
96
+
97
+ def __repr__(self) -> str:
98
+ """Returns a string representation of the semaphore."""
99
+ return f"<{self.__class__.__name__} name={self.name} capacity={self._capacity} value={self._Semaphore__value} waiters={self._count_waiters()}>"
100
+
101
+ async def __aenter__(self) -> None:
102
+ """Enters the semaphore context, acquiring it with the top priority.
103
+
104
+ This method is part of the asynchronous context management protocol.
105
+
106
+ Examples:
107
+ >>> semaphore = _AbstractPrioritySemaphore(5)
108
+ >>> async with semaphore:
109
+ ... await do_stuff()
110
+ """
111
+ await self.c_getitem(self._top_priority).acquire()
112
+
113
+ async def __aexit__(self, *_) -> None:
114
+ """Exits the semaphore context, releasing it with the top priority.
115
+
116
+ This method is part of the asynchronous context management protocol.
117
+
118
+ Examples:
119
+ >>> semaphore = _AbstractPrioritySemaphore(5)
120
+ >>> async with semaphore:
121
+ ... await do_stuff()
122
+ """
123
+ self.c_getitem(self._top_priority).release()
124
+
125
+ cpdef object acquire(self):
126
+ """Acquires the semaphore with the top priority.
127
+
128
+ This method overrides :meth:`Semaphore.acquire` to handle priority-based logic.
129
+
130
+ Examples:
131
+ >>> semaphore = _AbstractPrioritySemaphore(5)
132
+ >>> await semaphore.acquire()
133
+ """
134
+ return self.c_getitem(self._top_priority).acquire()
135
+
136
+ def __getitem__(
137
+ self, priority: Optional[PT]
138
+ ) -> "_AbstractPrioritySemaphoreContextManager[PT]":
139
+ """Gets the context manager for a given priority.
140
+
141
+ Args:
142
+ priority: The priority for which to get the context manager. If None, uses the top priority.
143
+
144
+ Returns:
145
+ The context manager associated with the given priority.
146
+
147
+ Examples:
148
+ >>> semaphore = _AbstractPrioritySemaphore(5)
149
+ >>> context_manager = semaphore[priority]
150
+ """
151
+ return self.c_getitem(priority)
152
+
153
+ cdef _AbstractPrioritySemaphoreContextManager c_getitem(self, object priority):
154
+ cdef _AbstractPrioritySemaphoreContextManager context_manager
155
+ cdef dict[object, _AbstractPrioritySemaphoreContextManager] context_managers
156
+
157
+ context_managers = self._context_managers
158
+ priority = self._top_priority if priority is None else priority
159
+ context_manager = context_managers.get(priority)
160
+ if context_manager is None:
161
+ context_manager = self._context_manager_class(
162
+ self, priority, name=self.name
163
+ )
164
+ heappush(self._Semaphore__waiters, context_manager)
165
+ context_managers[priority] = context_manager
166
+ return context_manager
167
+
168
+ cpdef bint locked(self):
169
+ """Checks if the semaphore is locked.
170
+
171
+ Returns:
172
+ True if the semaphore cannot be acquired immediately, False otherwise.
173
+
174
+ Examples:
175
+ >>> semaphore = _AbstractPrioritySemaphore(5)
176
+ >>> semaphore.locked()
177
+ """
178
+ cdef list waiters
179
+ if self._Semaphore__value == 0:
180
+ return True
181
+ for cm in self._context_managers.values():
182
+ waiters = (<Semaphore>cm).__waiters
183
+ for waiter in waiters:
184
+ if _is_not_cancelled(waiter):
185
+ return True
186
+ return False
187
+
188
+ cdef dict[object, Py_ssize_t] _count_waiters(self):
189
+ """Counts the number of waiters for each priority.
190
+
191
+ Returns:
192
+ A dictionary mapping each priority to the number of waiters.
193
+
194
+ Examples:
195
+ >>> semaphore = _AbstractPrioritySemaphore(5)
196
+ >>> semaphore._count_waiters()
197
+ """
198
+ cdef _AbstractPrioritySemaphoreContextManager manager
199
+ cdef list[_AbstractPrioritySemaphoreContextManager] waiters = self._Semaphore__waiters
200
+ return {manager._priority: len(manager._Semaphore__waiters) for manager in sorted(waiters)}
201
+
202
+ cpdef void _wake_up_next(self):
203
+ """Wakes up the next waiter in line.
204
+
205
+ This method handles the waking of waiters based on priority. It includes an emergency
206
+ procedure to handle potential lost waiters, ensuring that no waiter is left indefinitely
207
+ waiting.
208
+
209
+ The emergency procedure is a temporary measure to address potential issues with lost waiters.
210
+
211
+ Examples:
212
+ >>> semaphore = _AbstractPrioritySemaphore(5)
213
+ >>> semaphore._wake_up_next()
214
+ """
215
+ cdef _AbstractPrioritySemaphoreContextManager manager
216
+ cdef Py_ssize_t start_len, end_len
217
+ cdef bint woke_up
218
+
219
+ cdef list self_waiters = self._Semaphore__waiters
220
+ cdef list potential_lost_waiters = self._potential_lost_waiters
221
+ cdef bint debug_logs = _logger_is_enabled(DEBUG)
222
+ while self_waiters:
223
+ manager = heappop(self_waiters)
224
+ if len(manager) == 0:
225
+ # There are no more waiters, get rid of the empty manager
226
+ if debug_logs:
227
+ log_debug(
228
+ "manager %s has no more waiters, popping from %s",
229
+ (manager._repr_no_parent_(), self),
230
+ )
231
+ self._context_managers.pop(manager._priority)
232
+ continue
233
+
234
+ woke_up = False
235
+ start_len = len(manager)
236
+
237
+ manager_waiters = manager._Semaphore__waiters
238
+ if debug_logs:
239
+ log_debug("waking up next for %s", (manager._repr_no_parent_(), ))
240
+ if not manager_waiters:
241
+ log_debug("not manager._Semaphore__waiters", ())
242
+
243
+ while manager_waiters:
244
+ waiter = manager_waiters.popleft()
245
+ potential_lost_waiters.remove(waiter)
246
+ if _is_not_done(waiter):
247
+ waiter.set_result(None)
248
+ woke_up = True
249
+ if debug_logs:
250
+ log_debug("woke up %s", (waiter, ))
251
+ break
252
+
253
+ if not woke_up:
254
+ self._context_managers.pop(manager._priority)
255
+ continue
256
+
257
+ end_len = len(manager)
258
+
259
+ assert start_len > end_len, f"start {start_len} end {end_len}"
260
+
261
+ if end_len:
262
+ # There are still waiters, put the manager back
263
+ heappush(self_waiters, manager)
264
+ else:
265
+ # There are no more waiters, get rid of the empty manager
266
+ self._context_managers.pop(manager._priority)
267
+ return
268
+
269
+ # emergency procedure (hopefully temporary):
270
+ if not debug_logs:
271
+ while potential_lost_waiters:
272
+ waiter = potential_lost_waiters.pop(0)
273
+ if _is_not_done(waiter):
274
+ waiter.set_result(None)
275
+ return
276
+ return
277
+
278
+ while potential_lost_waiters:
279
+ waiter = potential_lost_waiters.pop(0)
280
+ log_debug("we found a lost waiter %s", (waiter, ))
281
+ if _is_not_done(waiter):
282
+ waiter.set_result(None)
283
+ log_debug("woke up lost waiter %s", (waiter, ))
284
+ return
285
+
286
+ log_debug("%s has no waiters to wake", (self, ))
287
+
288
+
289
+ cdef class _AbstractPrioritySemaphoreContextManager(Semaphore):
290
+ """
291
+ A context manager for priority semaphore waiters.
292
+
293
+ This context manager is associated with a specific priority and handles
294
+ the acquisition and release of the semaphore for waiters with that priority.
295
+ """
296
+
297
+ def __init__(
298
+ self,
299
+ parent: _AbstractPrioritySemaphore,
300
+ priority: PT,
301
+ name: Optional[str] = None,
302
+ ) -> None:
303
+ """Initializes the context manager for a specific priority.
304
+
305
+ Args:
306
+ parent: The parent semaphore.
307
+ priority: The priority associated with this context manager.
308
+ name: An optional name for the context manager, used for debugging.
309
+
310
+ Examples:
311
+ >>> parent_semaphore = _AbstractPrioritySemaphore(5)
312
+ >>> context_manager = _AbstractPrioritySemaphoreContextManager(parent_semaphore, priority=1)
313
+ """
314
+
315
+ Semaphore.__init__(self, 0, name=name)
316
+
317
+ self._parent = parent
318
+ """The parent semaphore."""
319
+
320
+ self._priority = priority
321
+ """The priority associated with this context manager."""
322
+
323
+ def __repr__(self) -> str:
324
+ """Returns a string representation of the context manager."""
325
+ return f"<{self.__class__.__name__} parent={self._parent} {self._priority_name}={self._priority} waiters={len(self)}>"
326
+
327
+ cpdef str _repr_no_parent_(self):
328
+ """Returns a string representation of the context manager without the parent."""
329
+ return f"<{self.__class__.__name__} parent_name={self._parent.name} {self._priority_name}={self._priority} waiters={len(self)}>"
330
+
331
+ def __richcmp__(self, _AbstractPrioritySemaphoreContextManager other, int op) -> bint:
332
+ """Rich comparison special method. Compares this context manager with another based on priority.
333
+
334
+ Args:
335
+ other: The other context manager to compare with.
336
+ op: The operation being performed. One of:
337
+ 0 -> Py_LT (a < b)
338
+ 1 -> Py_LE (a <= b)
339
+ 2 -> Py_EQ (a == b)
340
+ 3 -> Py_NE (a != b)
341
+ 4 -> Py_GT (a > b)
342
+ 5 -> Py_GE (a >= b)
343
+
344
+ Returns:
345
+ The boolean result of the comparison.
346
+
347
+ Raises:
348
+ TypeError: If the other object is not an instance of :class:`~_AbstractPrioritySemaphoreContextManager`.
349
+
350
+ Examples:
351
+ >>> cm1 = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
352
+ >>> cm2 = _AbstractPrioritySemaphoreContextManager(parent, priority=2)
353
+ >>> cm1 < cm2
354
+ """
355
+ if op == 0: # Py_LT
356
+ return self._priority < other._priority
357
+ elif op == 1: # Py_LE
358
+ return self._priority <= other._priority
359
+ elif op == 2: # Py_EQ
360
+ return self is other
361
+ elif op == 3: # Py_NE
362
+ return self is not other
363
+ elif op == 4: # Py_GT
364
+ return self._priority > other._priority
365
+ elif op == 5: # Py_GE
366
+ return self._priority >= other._priority
367
+ return NotImplemented
368
+
369
+ cpdef object acquire(self):
370
+ """Acquires the semaphore for this context manager.
371
+
372
+ If the internal counter is larger than zero on entry,
373
+ decrement it by one and return True immediately. If it is
374
+ zero on entry, block, waiting until some other coroutine has
375
+ called release() to make it larger than 0, and then return
376
+ True.
377
+
378
+ This method overrides :meth:`Semaphore.acquire` to handle priority-based logic.
379
+
380
+ Examples:
381
+ >>> context_manager = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
382
+ >>> await context_manager.acquire()
383
+ """
384
+ if self._parent._Semaphore__value <= 0:
385
+ self._c_ensure_debug_daemon((),{})
386
+ return self.__acquire()
387
+
388
+ async def __acquire(self) -> Literal[True]:
389
+ cdef object fut
390
+ cdef _AbstractPrioritySemaphore parent = self._parent
391
+ while parent._Semaphore__value <= 0:
392
+ if self._Semaphore__waiters is None:
393
+ self._Semaphore__waiters = deque()
394
+ fut = self._c_get_loop().create_future()
395
+ self._Semaphore__waiters.append(fut)
396
+ parent._potential_lost_waiters.append(fut)
397
+ try:
398
+ await fut
399
+ except:
400
+ # See the similar code in Queue.get.
401
+ fut.cancel()
402
+ if parent._Semaphore__value > 0 and _is_not_cancelled(fut):
403
+ parent._wake_up_next()
404
+ raise
405
+ parent._Semaphore__value -= 1
406
+ return True
407
+
408
+ cpdef void release(self):
409
+ """Releases the semaphore for this context manager.
410
+
411
+ This method overrides :meth:`Semaphore.release` to handle priority-based logic.
412
+
413
+ Examples:
414
+ >>> context_manager = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
415
+ >>> context_manager.release()
416
+ """
417
+ self._parent.release()
418
+
419
+
420
+ cdef inline bint _is_not_done(fut: Future):
421
+ return <str>fut._state == "PENDING"
422
+
423
+ cdef inline bint _is_not_cancelled(fut: Future):
424
+ return <str>fut._state != "CANCELLED"
425
+
426
+
427
+ cdef class _PrioritySemaphoreContextManager(_AbstractPrioritySemaphoreContextManager):
428
+ """Context manager for numeric priority semaphores."""
429
+
430
+ def __cinit__(self):
431
+ self._priority_name = "priority"
432
+ # Semaphore.__cinit__(self)
433
+ self._Semaphore__waiters = deque()
434
+ self._decorated: Set[str] = set()
435
+
436
+ def __richcmp__(self, _PrioritySemaphoreContextManager other, int op) -> bint:
437
+ """Rich comparison special method. Compares this context manager with another based on priority.
438
+
439
+ Args:
440
+ other: The other context manager to compare with.
441
+ op: The operation being performed. One of:
442
+ 0 -> Py_LT (a < b)
443
+ 1 -> Py_LE (a <= b)
444
+ 2 -> Py_EQ (a == b)
445
+ 3 -> Py_NE (a != b)
446
+ 4 -> Py_GT (a > b)
447
+ 5 -> Py_GE (a >= b)
448
+
449
+ Returns:
450
+ The boolean result of the comparison.
451
+
452
+ Raises:
453
+ TypeError: If the other object is not an instance of :class:`~_PrioritySemaphoreContextManager`.
454
+
455
+ Examples:
456
+ >>> cm1 = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
457
+ >>> cm2 = _AbstractPrioritySemaphoreContextManager(parent, priority=2)
458
+ >>> cm1 < cm2
459
+ """
460
+ if op == 0: # Py_LT
461
+ return <int>self._priority < <int>other._priority
462
+ elif op == 1: # Py_LE
463
+ return <int>self._priority <= <int>other._priority
464
+ elif op == 2: # Py_EQ
465
+ return self is other
466
+ elif op == 3: # Py_NE
467
+ return self is not other
468
+ elif op == 4: # Py_GT
469
+ return <int>self._priority > <int>other._priority
470
+ elif op == 5: # Py_GE
471
+ return <int>self._priority >= <int>other._priority
472
+ return NotImplemented
473
+
474
+ cdef class PrioritySemaphore(_AbstractPrioritySemaphore):
475
+ """Semaphore that uses numeric priorities for waiters.
476
+
477
+ This class extends :class:`_AbstractPrioritySemaphore` and provides a concrete implementation
478
+ using numeric priorities. The `_context_manager_class` is set to :class:`_PrioritySemaphoreContextManager`,
479
+ and the `_top_priority` is set to -1, which is the highest priority.
480
+
481
+ Examples:
482
+ The primary way to use this semaphore is by specifying a priority.
483
+
484
+ >>> priority_semaphore = PrioritySemaphore(10)
485
+ >>> async with priority_semaphore[priority]:
486
+ ... await do_stuff()
487
+
488
+ You can also enter and exit this semaphore without specifying a priority, and it will use the top priority by default:
489
+
490
+ >>> priority_semaphore = PrioritySemaphore(10)
491
+ >>> async with priority_semaphore:
492
+ ... await do_stuff()
493
+
494
+ See Also:
495
+ :class:`_AbstractPrioritySemaphore` for the base class implementation.
496
+ """
497
+
498
+ def __cinit__(self):
499
+ # _AbstractPrioritySemaphore.__cinit__(self)
500
+
501
+ self._context_managers = {}
502
+ """A dictionary mapping priorities to their context managers."""
503
+
504
+ self._Semaphore__waiters = []
505
+ """A heap queue of context managers, sorted by priority."""
506
+
507
+ # NOTE: This should (hopefully) be temporary
508
+ self._potential_lost_waiters: List["Future[None]"] = []
509
+ """A list of futures representing waiters that might have been lost."""
510
+
511
+ def __init__(
512
+ self,
513
+ value: int = 1,
514
+ *,
515
+ name: Optional[str] = None,
516
+ ) -> None:
517
+ # context manager class is some temporary hacky shit, just ignore this
518
+ _AbstractPrioritySemaphore.__init__(self, _PrioritySemaphoreContextManager, -1, value, name=name)
519
+
520
+ def __getitem__(
521
+ self, priority: Optional[PT]
522
+ ) -> "_PrioritySemaphoreContextManager[PT]":
523
+ """Gets the context manager for a given priority.
524
+
525
+ Args:
526
+ priority: The priority for which to get the context manager. If None, uses the top priority.
527
+
528
+ Returns:
529
+ The context manager associated with the given priority.
530
+
531
+ Examples:
532
+ >>> semaphore = _AbstractPrioritySemaphore(5)
533
+ >>> context_manager = semaphore[priority]
534
+ """
535
+ return self.c_getitem(priority or -1)
536
+
537
+ cdef _PrioritySemaphoreContextManager c_getitem(self, object priority):
538
+ if self._Semaphore__value is None:
539
+ raise ValueError(self._Semaphore__value)
540
+
541
+ cdef dict[int, _PrioritySemaphoreContextManager] context_managers
542
+ cdef _PrioritySemaphoreContextManager context_manager
543
+
544
+ context_managers = self._context_managers
545
+ context_manager = context_managers.get(priority)
546
+ if context_manager is None:
547
+ context_manager = _PrioritySemaphoreContextManager(self, <int>priority, name=self.name)
548
+ heappush(self._Semaphore__waiters, context_manager)
549
+ context_managers[<int>priority] = context_manager
550
+ return context_manager
551
+
552
+
553
+ cdef void log_debug(str message, tuple args):
554
+ _logger_log(DEBUG, message, args)