ez-a-sync 0.22.14__py3-none-any.whl → 0.22.16__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.
Potentially problematic release.
This version of ez-a-sync might be problematic. Click here for more details.
- a_sync/ENVIRONMENT_VARIABLES.py +37 -5
- a_sync/__init__.py +53 -12
- a_sync/_smart.py +231 -28
- a_sync/_typing.py +112 -15
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +248 -38
- a_sync/a_sync/_flags.py +78 -9
- a_sync/a_sync/_helpers.py +46 -13
- a_sync/a_sync/_kwargs.py +33 -8
- a_sync/a_sync/_meta.py +149 -28
- a_sync/a_sync/abstract.py +150 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +85 -14
- a_sync/a_sync/decorator.py +441 -139
- a_sync/a_sync/function.py +709 -147
- a_sync/a_sync/method.py +437 -110
- a_sync/a_sync/modifiers/__init__.py +85 -5
- a_sync/a_sync/modifiers/cache/__init__.py +116 -17
- a_sync/a_sync/modifiers/cache/memory.py +130 -20
- a_sync/a_sync/modifiers/limiter.py +101 -22
- a_sync/a_sync/modifiers/manager.py +142 -16
- a_sync/a_sync/modifiers/semaphores.py +121 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +44 -19
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +140 -1
- a_sync/asyncio/as_completed.py +213 -79
- a_sync/asyncio/create_task.py +70 -20
- a_sync/asyncio/gather.py +125 -58
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +248 -26
- a_sync/executor.py +164 -69
- a_sync/future.py +1227 -168
- a_sync/iter.py +173 -56
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +72 -18
- a_sync/primitives/_loggable.py +41 -10
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +107 -38
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +262 -63
- a_sync/primitives/locks/semaphore.py +138 -89
- a_sync/primitives/queue.py +601 -60
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +313 -112
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +170 -50
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +250 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +22 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +264 -19
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +31 -13
- tests/test_future.py +45 -8
- tests/test_gather.py +8 -2
- tests/test_helpers.py +2 -0
- tests/test_iter.py +55 -13
- tests/test_limiter.py +5 -3
- tests/test_meta.py +23 -9
- tests/test_modified.py +4 -1
- tests/test_semaphore.py +15 -8
- tests/test_singleton.py +28 -11
- tests/test_task.py +162 -36
- ez_a_sync-0.22.14.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides priority-based semaphore implementations. These semaphores allow
|
|
3
|
+
waiters to be assigned priorities, ensuring that higher priority waiters are
|
|
4
|
+
processed before lower priority ones.
|
|
5
|
+
"""
|
|
1
6
|
|
|
2
7
|
import asyncio
|
|
3
8
|
import heapq
|
|
@@ -12,84 +17,208 @@ logger = logging.getLogger(__name__)
|
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
class Priority(Protocol):
|
|
15
|
-
def __lt__(self, other) -> bool:
|
|
16
|
-
|
|
20
|
+
def __lt__(self, other) -> bool: ...
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
PT = TypeVar("PT", bound=Priority)
|
|
24
|
+
|
|
25
|
+
CM = TypeVar("CM", bound="_AbstractPrioritySemaphoreContextManager[Priority]")
|
|
17
26
|
|
|
18
|
-
PT = TypeVar('PT', bound=Priority)
|
|
19
|
-
|
|
20
|
-
CM = TypeVar('CM', bound="_AbstractPrioritySemaphoreContextManager[Priority]")
|
|
21
27
|
|
|
22
28
|
class _AbstractPrioritySemaphore(Semaphore, Generic[PT, CM]):
|
|
29
|
+
"""
|
|
30
|
+
A semaphore that allows prioritization of waiters.
|
|
31
|
+
|
|
32
|
+
This semaphore manages waiters with associated priorities, ensuring that waiters with higher
|
|
33
|
+
priorities are processed before those with lower priorities. Subclasses must define the
|
|
34
|
+
`_top_priority` property to specify the default top priority behavior.
|
|
35
|
+
|
|
36
|
+
The `_context_manager_class` property should return the class used for managing semaphore contexts.
|
|
37
|
+
|
|
38
|
+
See Also:
|
|
39
|
+
:class:`PrioritySemaphore` for an implementation using numeric priorities.
|
|
40
|
+
"""
|
|
41
|
+
|
|
23
42
|
name: Optional[str]
|
|
24
43
|
_value: int
|
|
25
44
|
_waiters: List["_AbstractPrioritySemaphoreContextManager[PT]"] # type: ignore [assignment]
|
|
26
|
-
|
|
45
|
+
_context_managers: Dict[PT, "_AbstractPrioritySemaphoreContextManager[PT]"]
|
|
46
|
+
__slots__ = (
|
|
47
|
+
"name",
|
|
48
|
+
"_value",
|
|
49
|
+
"_waiters",
|
|
50
|
+
"_context_managers",
|
|
51
|
+
"_capacity",
|
|
52
|
+
"_potential_lost_waiters",
|
|
53
|
+
)
|
|
27
54
|
|
|
28
55
|
@property
|
|
29
|
-
def _context_manager_class(
|
|
56
|
+
def _context_manager_class(
|
|
57
|
+
self,
|
|
58
|
+
) -> Type["_AbstractPrioritySemaphoreContextManager[PT]"]:
|
|
30
59
|
raise NotImplementedError
|
|
31
|
-
|
|
60
|
+
|
|
32
61
|
@property
|
|
33
62
|
def _top_priority(self) -> PT:
|
|
34
|
-
|
|
63
|
+
"""Defines the top priority for the semaphore.
|
|
64
|
+
|
|
65
|
+
Subclasses must implement this property to specify the default top priority.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
NotImplementedError: If not implemented in a subclass.
|
|
69
|
+
"""
|
|
35
70
|
raise NotImplementedError
|
|
36
71
|
|
|
37
72
|
def __init__(self, value: int = 1, *, name: Optional[str] = None) -> None:
|
|
38
|
-
|
|
73
|
+
"""Initializes the priority semaphore.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
value: The initial capacity of the semaphore.
|
|
77
|
+
name: An optional name for the semaphore, used for debugging.
|
|
78
|
+
|
|
79
|
+
Examples:
|
|
80
|
+
>>> semaphore = _AbstractPrioritySemaphore(5, name="test_semaphore")
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
self._context_managers = {}
|
|
84
|
+
"""A dictionary mapping priorities to their context managers."""
|
|
85
|
+
|
|
39
86
|
self._capacity = value
|
|
87
|
+
"""The initial capacity of the semaphore."""
|
|
88
|
+
|
|
40
89
|
super().__init__(value, name=name)
|
|
41
90
|
self._waiters = []
|
|
91
|
+
"""A heap queue of context managers, sorted by priority."""
|
|
92
|
+
|
|
42
93
|
# NOTE: This should (hopefully) be temporary
|
|
43
94
|
self._potential_lost_waiters: List["asyncio.Future[None]"] = []
|
|
95
|
+
"""A list of futures representing waiters that might have been lost."""
|
|
44
96
|
|
|
45
97
|
def __repr__(self) -> str:
|
|
98
|
+
"""Returns a string representation of the semaphore."""
|
|
46
99
|
return f"<{self.__class__.__name__} name={self.name} capacity={self._capacity} value={self._value} waiters={self._count_waiters()}>"
|
|
47
100
|
|
|
48
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
|
+
"""
|
|
49
111
|
await self[self._top_priority].acquire()
|
|
50
112
|
|
|
51
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
|
+
"""
|
|
52
123
|
self[self._top_priority].release()
|
|
53
|
-
|
|
124
|
+
|
|
54
125
|
async def acquire(self) -> Literal[True]:
|
|
126
|
+
"""Acquires the semaphore with the top priority.
|
|
127
|
+
|
|
128
|
+
Examples:
|
|
129
|
+
>>> semaphore = _AbstractPrioritySemaphore(5)
|
|
130
|
+
>>> await semaphore.acquire()
|
|
131
|
+
"""
|
|
55
132
|
return await self[self._top_priority].acquire()
|
|
56
|
-
|
|
57
|
-
def __getitem__(
|
|
133
|
+
|
|
134
|
+
def __getitem__(
|
|
135
|
+
self, priority: Optional[PT]
|
|
136
|
+
) -> "_AbstractPrioritySemaphoreContextManager[PT]":
|
|
137
|
+
"""Gets the context manager for a given priority.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
priority: The priority for which to get the context manager. If None, uses the top priority.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The context manager associated with the given priority.
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
>>> semaphore = _AbstractPrioritySemaphore(5)
|
|
147
|
+
>>> context_manager = semaphore[priority]
|
|
148
|
+
"""
|
|
58
149
|
priority = self._top_priority if priority is None else priority
|
|
59
150
|
if priority not in self._context_managers:
|
|
60
|
-
context_manager = self._context_manager_class(
|
|
151
|
+
context_manager = self._context_manager_class(
|
|
152
|
+
self, priority, name=self.name
|
|
153
|
+
)
|
|
61
154
|
heapq.heappush(self._waiters, context_manager) # type: ignore [misc]
|
|
62
155
|
self._context_managers[priority] = context_manager
|
|
63
156
|
return self._context_managers[priority]
|
|
64
157
|
|
|
65
158
|
def locked(self) -> bool:
|
|
66
|
-
"""
|
|
159
|
+
"""Checks if the semaphore is locked.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
True if the semaphore cannot be acquired immediately, False otherwise.
|
|
163
|
+
|
|
164
|
+
Examples:
|
|
165
|
+
>>> semaphore = _AbstractPrioritySemaphore(5)
|
|
166
|
+
>>> semaphore.locked()
|
|
167
|
+
"""
|
|
67
168
|
return self._value == 0 or (
|
|
68
169
|
any(
|
|
69
|
-
cm._waiters and any(not w.cancelled() for w in cm._waiters)
|
|
170
|
+
cm._waiters and any(not w.cancelled() for w in cm._waiters)
|
|
70
171
|
for cm in (self._context_managers.values() or ())
|
|
71
172
|
)
|
|
72
173
|
)
|
|
73
|
-
|
|
174
|
+
|
|
74
175
|
def _count_waiters(self) -> Dict[PT, int]:
|
|
75
|
-
|
|
76
|
-
|
|
176
|
+
"""Counts the number of waiters for each priority.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
A dictionary mapping each priority to the number of waiters.
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
>>> semaphore = _AbstractPrioritySemaphore(5)
|
|
183
|
+
>>> semaphore._count_waiters()
|
|
184
|
+
"""
|
|
185
|
+
return {
|
|
186
|
+
manager._priority: len(manager.waiters)
|
|
187
|
+
for manager in sorted(self._waiters, key=lambda m: m._priority)
|
|
188
|
+
}
|
|
189
|
+
|
|
77
190
|
def _wake_up_next(self) -> None:
|
|
191
|
+
"""Wakes up the next waiter in line.
|
|
192
|
+
|
|
193
|
+
This method handles the waking of waiters based on priority. It includes an emergency
|
|
194
|
+
procedure to handle potential lost waiters, ensuring that no waiter is left indefinitely
|
|
195
|
+
waiting.
|
|
196
|
+
|
|
197
|
+
The emergency procedure is a temporary measure to address potential issues with lost waiters.
|
|
198
|
+
|
|
199
|
+
Examples:
|
|
200
|
+
>>> semaphore = _AbstractPrioritySemaphore(5)
|
|
201
|
+
>>> semaphore._wake_up_next()
|
|
202
|
+
"""
|
|
78
203
|
while self._waiters:
|
|
79
204
|
manager = heapq.heappop(self._waiters)
|
|
80
205
|
if len(manager) == 0:
|
|
81
206
|
# There are no more waiters, get rid of the empty manager
|
|
82
|
-
logger.debug(
|
|
207
|
+
logger.debug(
|
|
208
|
+
"manager %s has no more waiters, popping from %s",
|
|
209
|
+
manager._repr_no_parent_(),
|
|
210
|
+
self,
|
|
211
|
+
)
|
|
83
212
|
self._context_managers.pop(manager._priority)
|
|
84
213
|
continue
|
|
85
214
|
logger.debug("waking up next for %s", manager._repr_no_parent_())
|
|
86
|
-
|
|
215
|
+
|
|
87
216
|
woke_up = False
|
|
88
217
|
start_len = len(manager)
|
|
89
|
-
|
|
218
|
+
|
|
90
219
|
if not manager._waiters:
|
|
91
|
-
logger.debug(
|
|
92
|
-
|
|
220
|
+
logger.debug("not manager._waiters")
|
|
221
|
+
|
|
93
222
|
while manager._waiters:
|
|
94
223
|
waiter = manager._waiters.popleft()
|
|
95
224
|
self._potential_lost_waiters.remove(waiter)
|
|
@@ -98,15 +227,15 @@ class _AbstractPrioritySemaphore(Semaphore, Generic[PT, CM]):
|
|
|
98
227
|
logger.debug("woke up %s", waiter)
|
|
99
228
|
woke_up = True
|
|
100
229
|
break
|
|
101
|
-
|
|
230
|
+
|
|
102
231
|
if not woke_up:
|
|
103
232
|
self._context_managers.pop(manager._priority)
|
|
104
233
|
continue
|
|
105
|
-
|
|
234
|
+
|
|
106
235
|
end_len = len(manager)
|
|
107
|
-
|
|
236
|
+
|
|
108
237
|
assert start_len > end_len, f"start {start_len} end {end_len}"
|
|
109
|
-
|
|
238
|
+
|
|
110
239
|
if end_len:
|
|
111
240
|
# There are still waiters, put the manager back
|
|
112
241
|
heapq.heappush(self._waiters, manager) # type: ignore [misc]
|
|
@@ -114,60 +243,113 @@ class _AbstractPrioritySemaphore(Semaphore, Generic[PT, CM]):
|
|
|
114
243
|
# There are no more waiters, get rid of the empty manager
|
|
115
244
|
self._context_managers.pop(manager._priority)
|
|
116
245
|
return
|
|
117
|
-
|
|
118
|
-
# emergency procedure (hopefully temporary):
|
|
246
|
+
|
|
247
|
+
# emergency procedure (hopefully temporary):
|
|
119
248
|
while self._potential_lost_waiters:
|
|
120
249
|
waiter = self._potential_lost_waiters.pop(0)
|
|
121
|
-
logger.debug(
|
|
250
|
+
logger.debug("we found a lost waiter %s", waiter)
|
|
122
251
|
if not waiter.done():
|
|
123
252
|
waiter.set_result(None)
|
|
124
253
|
logger.debug("woke up lost waiter %s", waiter)
|
|
125
254
|
return
|
|
126
255
|
logger.debug("%s has no waiters to wake", self)
|
|
127
256
|
|
|
257
|
+
|
|
128
258
|
class _AbstractPrioritySemaphoreContextManager(Semaphore, Generic[PT]):
|
|
259
|
+
"""
|
|
260
|
+
A context manager for priority semaphore waiters.
|
|
261
|
+
|
|
262
|
+
This context manager is associated with a specific priority and handles
|
|
263
|
+
the acquisition and release of the semaphore for waiters with that priority.
|
|
264
|
+
"""
|
|
265
|
+
|
|
129
266
|
_loop: asyncio.AbstractEventLoop
|
|
130
267
|
_waiters: Deque[asyncio.Future] # type: ignore [assignment]
|
|
131
268
|
__slots__ = "_parent", "_priority"
|
|
132
|
-
|
|
269
|
+
|
|
133
270
|
@property
|
|
134
271
|
def _priority_name(self) -> str:
|
|
135
272
|
raise NotImplementedError
|
|
136
|
-
|
|
137
|
-
def __init__(
|
|
273
|
+
|
|
274
|
+
def __init__(
|
|
275
|
+
self,
|
|
276
|
+
parent: _AbstractPrioritySemaphore,
|
|
277
|
+
priority: PT,
|
|
278
|
+
name: Optional[str] = None,
|
|
279
|
+
) -> None:
|
|
280
|
+
"""Initializes the context manager for a specific priority.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
parent: The parent semaphore.
|
|
284
|
+
priority: The priority associated with this context manager.
|
|
285
|
+
name: An optional name for the context manager, used for debugging.
|
|
286
|
+
|
|
287
|
+
Examples:
|
|
288
|
+
>>> parent_semaphore = _AbstractPrioritySemaphore(5)
|
|
289
|
+
>>> context_manager = _AbstractPrioritySemaphoreContextManager(parent_semaphore, priority=1)
|
|
290
|
+
"""
|
|
291
|
+
|
|
138
292
|
self._parent = parent
|
|
293
|
+
"""The parent semaphore."""
|
|
294
|
+
|
|
139
295
|
self._priority = priority
|
|
296
|
+
"""The priority associated with this context manager."""
|
|
297
|
+
|
|
140
298
|
super().__init__(0, name=name)
|
|
141
299
|
|
|
142
300
|
def __repr__(self) -> str:
|
|
301
|
+
"""Returns a string representation of the context manager."""
|
|
143
302
|
return f"<{self.__class__.__name__} parent={self._parent} {self._priority_name}={self._priority} waiters={len(self)}>"
|
|
144
|
-
|
|
303
|
+
|
|
145
304
|
def _repr_no_parent_(self) -> str:
|
|
305
|
+
"""Returns a string representation of the context manager without the parent."""
|
|
146
306
|
return f"<{self.__class__.__name__} parent_name={self._parent.name} {self._priority_name}={self._priority} waiters={len(self)}>"
|
|
147
|
-
|
|
307
|
+
|
|
148
308
|
def __lt__(self, other) -> bool:
|
|
309
|
+
"""Compares this context manager with another based on priority.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
other: The other context manager to compare with.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
True if this context manager has a lower priority than the other, False otherwise.
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
TypeError: If the other object is not of the same type.
|
|
319
|
+
|
|
320
|
+
Examples:
|
|
321
|
+
>>> cm1 = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
|
|
322
|
+
>>> cm2 = _AbstractPrioritySemaphoreContextManager(parent, priority=2)
|
|
323
|
+
>>> cm1 < cm2
|
|
324
|
+
"""
|
|
149
325
|
if type(other) is not type(self):
|
|
150
326
|
raise TypeError(f"{other} is not type {self.__class__.__name__}")
|
|
151
327
|
return self._priority < other._priority
|
|
152
|
-
|
|
328
|
+
|
|
153
329
|
@cached_property
|
|
154
330
|
def loop(self) -> asyncio.AbstractEventLoop:
|
|
331
|
+
"""Gets the event loop associated with this context manager."""
|
|
155
332
|
return self._loop or asyncio.get_event_loop()
|
|
156
|
-
|
|
333
|
+
|
|
157
334
|
@property
|
|
158
|
-
def waiters
|
|
335
|
+
def waiters(self) -> Deque[asyncio.Future]:
|
|
336
|
+
"""Gets the deque of waiters for this context manager."""
|
|
159
337
|
if self._waiters is None:
|
|
160
338
|
self._waiters = deque()
|
|
161
339
|
return self._waiters
|
|
162
|
-
|
|
340
|
+
|
|
163
341
|
async def acquire(self) -> Literal[True]:
|
|
164
|
-
"""
|
|
342
|
+
"""Acquires the semaphore for this context manager.
|
|
165
343
|
|
|
166
344
|
If the internal counter is larger than zero on entry,
|
|
167
|
-
decrement it by one and return True immediately.
|
|
345
|
+
decrement it by one and return True immediately. If it is
|
|
168
346
|
zero on entry, block, waiting until some other coroutine has
|
|
169
347
|
called release() to make it larger than 0, and then return
|
|
170
348
|
True.
|
|
349
|
+
|
|
350
|
+
Examples:
|
|
351
|
+
>>> context_manager = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
|
|
352
|
+
>>> await context_manager.acquire()
|
|
171
353
|
"""
|
|
172
354
|
if self._parent._value <= 0:
|
|
173
355
|
self._ensure_debug_daemon()
|
|
@@ -185,31 +367,48 @@ class _AbstractPrioritySemaphoreContextManager(Semaphore, Generic[PT]):
|
|
|
185
367
|
raise
|
|
186
368
|
self._parent._value -= 1
|
|
187
369
|
return True
|
|
370
|
+
|
|
188
371
|
def release(self) -> None:
|
|
372
|
+
"""Releases the semaphore for this context manager.
|
|
373
|
+
|
|
374
|
+
Examples:
|
|
375
|
+
>>> context_manager = _AbstractPrioritySemaphoreContextManager(parent, priority=1)
|
|
376
|
+
>>> context_manager.release()
|
|
377
|
+
"""
|
|
189
378
|
self._parent.release()
|
|
190
|
-
|
|
191
|
-
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class _PrioritySemaphoreContextManager(
|
|
382
|
+
_AbstractPrioritySemaphoreContextManager[Numeric]
|
|
383
|
+
):
|
|
384
|
+
"""Context manager for numeric priority semaphores."""
|
|
385
|
+
|
|
192
386
|
_priority_name = "priority"
|
|
193
387
|
|
|
388
|
+
|
|
194
389
|
class PrioritySemaphore(_AbstractPrioritySemaphore[Numeric, _PrioritySemaphoreContextManager]): # type: ignore [type-var]
|
|
390
|
+
"""Semaphore that uses numeric priorities for waiters.
|
|
391
|
+
|
|
392
|
+
This class extends :class:`_AbstractPrioritySemaphore` and provides a concrete implementation
|
|
393
|
+
using numeric priorities. The `_context_manager_class` is set to :class:`_PrioritySemaphoreContextManager`,
|
|
394
|
+
and the `_top_priority` is set to -1, which is the highest priority.
|
|
395
|
+
|
|
396
|
+
Examples:
|
|
397
|
+
The primary way to use this semaphore is by specifying a priority.
|
|
398
|
+
|
|
399
|
+
>>> priority_semaphore = PrioritySemaphore(10)
|
|
400
|
+
>>> async with priority_semaphore[priority]:
|
|
401
|
+
... await do_stuff()
|
|
402
|
+
|
|
403
|
+
You can also enter and exit this semaphore without specifying a priority, and it will use the top priority by default:
|
|
404
|
+
|
|
405
|
+
>>> priority_semaphore = PrioritySemaphore(10)
|
|
406
|
+
>>> async with priority_semaphore:
|
|
407
|
+
... await do_stuff()
|
|
408
|
+
|
|
409
|
+
See Also:
|
|
410
|
+
:class:`_AbstractPrioritySemaphore` for the base class implementation.
|
|
411
|
+
"""
|
|
412
|
+
|
|
195
413
|
_context_manager_class = _PrioritySemaphoreContextManager
|
|
196
414
|
_top_priority = -1
|
|
197
|
-
"""
|
|
198
|
-
It's kinda like a regular Semaphore but you must give each waiter a priority:
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
priority_semaphore = PrioritySemaphore(10)
|
|
202
|
-
|
|
203
|
-
async with priority_semaphore[priority]:
|
|
204
|
-
await do_stuff()
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
You can aenter and aexit this semaphore without a priority and it will process those first. Like so:
|
|
208
|
-
|
|
209
|
-
```
|
|
210
|
-
priority_semaphore = PrioritySemaphore(10)
|
|
211
|
-
|
|
212
|
-
async with priority_semaphore:
|
|
213
|
-
await do_stuff()
|
|
214
|
-
```
|
|
215
|
-
"""
|