ez-a-sync 0.22.13__py3-none-any.whl → 0.22.15__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 +4 -3
- a_sync/__init__.py +30 -12
- a_sync/_smart.py +132 -28
- a_sync/_typing.py +56 -12
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +74 -26
- a_sync/a_sync/_flags.py +14 -6
- a_sync/a_sync/_helpers.py +8 -7
- a_sync/a_sync/_kwargs.py +3 -2
- a_sync/a_sync/_meta.py +120 -28
- a_sync/a_sync/abstract.py +102 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +47 -13
- a_sync/a_sync/decorator.py +239 -117
- a_sync/a_sync/function.py +416 -146
- a_sync/a_sync/method.py +197 -59
- a_sync/a_sync/modifiers/__init__.py +47 -5
- a_sync/a_sync/modifiers/cache/__init__.py +46 -17
- a_sync/a_sync/modifiers/cache/memory.py +86 -20
- a_sync/a_sync/modifiers/limiter.py +52 -22
- a_sync/a_sync/modifiers/manager.py +98 -16
- a_sync/a_sync/modifiers/semaphores.py +48 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +1 -0
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +4 -1
- a_sync/asyncio/as_completed.py +177 -49
- a_sync/asyncio/create_task.py +31 -17
- a_sync/asyncio/gather.py +72 -52
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +78 -23
- a_sync/executor.py +120 -71
- a_sync/future.py +575 -158
- a_sync/iter.py +110 -50
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +13 -13
- a_sync/primitives/_loggable.py +5 -4
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +38 -36
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +182 -62
- a_sync/primitives/locks/semaphore.py +78 -77
- a_sync/primitives/queue.py +560 -58
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +262 -97
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +127 -43
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.15.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +112 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +7 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +66 -17
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +8 -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 +15 -10
- tests/test_task.py +126 -28
- ez_a_sync-0.22.13.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/top_level.txt +0 -0
a_sync/primitives/locks/event.py
CHANGED
|
@@ -8,14 +8,16 @@ import sys
|
|
|
8
8
|
from a_sync._typing import *
|
|
9
9
|
from a_sync.primitives._debug import _DebugDaemonMixin
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
class Event(asyncio.Event, _DebugDaemonMixin):
|
|
12
13
|
"""
|
|
13
14
|
An asyncio.Event with additional debug logging to help detect deadlocks.
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
This event class extends asyncio.Event by adding debug logging capabilities. It logs
|
|
16
|
-
detailed information about the event state and waiters, which can be useful for
|
|
17
|
+
detailed information about the event state and waiters, which can be useful for
|
|
17
18
|
diagnosing and debugging potential deadlocks.
|
|
18
19
|
"""
|
|
20
|
+
|
|
19
21
|
_value: bool
|
|
20
22
|
_loop: asyncio.AbstractEventLoop
|
|
21
23
|
_waiters: Deque["asyncio.Future[None]"]
|
|
@@ -23,7 +25,14 @@ class Event(asyncio.Event, _DebugDaemonMixin):
|
|
|
23
25
|
__slots__ = "_value", "_waiters", "_debug_daemon_interval"
|
|
24
26
|
else:
|
|
25
27
|
__slots__ = "_value", "_loop", "_waiters", "_debug_daemon_interval"
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
name: str = "",
|
|
32
|
+
debug_daemon_interval: int = 300,
|
|
33
|
+
*,
|
|
34
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
35
|
+
):
|
|
27
36
|
"""
|
|
28
37
|
Initializes the Event.
|
|
29
38
|
|
|
@@ -41,12 +50,14 @@ class Event(asyncio.Event, _DebugDaemonMixin):
|
|
|
41
50
|
if hasattr(self, "_loop"):
|
|
42
51
|
self._loop = self._loop or asyncio.get_event_loop()
|
|
43
52
|
self._debug_daemon_interval = debug_daemon_interval
|
|
53
|
+
|
|
44
54
|
def __repr__(self) -> str:
|
|
45
|
-
label = f
|
|
46
|
-
status =
|
|
55
|
+
label = f"name={self._name}" if self._name else "object"
|
|
56
|
+
status = "set" if self._value else "unset"
|
|
47
57
|
if self._waiters:
|
|
48
|
-
status += f
|
|
58
|
+
status += f", waiters:{len(self._waiters)}"
|
|
49
59
|
return f"<{self.__class__.__module__}.{self.__class__.__name__} {label} at {hex(id(self))} [{status}]>"
|
|
60
|
+
|
|
50
61
|
async def wait(self) -> Literal[True]:
|
|
51
62
|
"""
|
|
52
63
|
Wait until the event is set.
|
|
@@ -58,6 +69,7 @@ class Event(asyncio.Event, _DebugDaemonMixin):
|
|
|
58
69
|
return True
|
|
59
70
|
self._ensure_debug_daemon()
|
|
60
71
|
return await super().wait()
|
|
72
|
+
|
|
61
73
|
async def _debug_daemon(self) -> None:
|
|
62
74
|
"""
|
|
63
75
|
Periodically logs debug information about the event state and waiters.
|
|
@@ -68,4 +80,6 @@ class Event(asyncio.Event, _DebugDaemonMixin):
|
|
|
68
80
|
del self # no need to hold a reference here
|
|
69
81
|
await asyncio.sleep(self._debug_daemon_interval)
|
|
70
82
|
if (self := weakself()) and not self.is_set():
|
|
71
|
-
self.logger.debug(
|
|
83
|
+
self.logger.debug(
|
|
84
|
+
"Waiting for %s for %sm", self, round((time() - start) / 60, 2)
|
|
85
|
+
)
|
|
@@ -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,155 @@ 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. If no priority is specified,
|
|
34
|
+
the semaphore uses a default top priority.
|
|
35
|
+
"""
|
|
36
|
+
|
|
23
37
|
name: Optional[str]
|
|
24
38
|
_value: int
|
|
25
39
|
_waiters: List["_AbstractPrioritySemaphoreContextManager[PT]"] # type: ignore [assignment]
|
|
26
|
-
|
|
40
|
+
_context_managers: Dict[PT, "_AbstractPrioritySemaphoreContextManager[PT]"]
|
|
41
|
+
__slots__ = (
|
|
42
|
+
"name",
|
|
43
|
+
"_value",
|
|
44
|
+
"_waiters",
|
|
45
|
+
"_context_managers",
|
|
46
|
+
"_capacity",
|
|
47
|
+
"_potential_lost_waiters",
|
|
48
|
+
)
|
|
27
49
|
|
|
28
50
|
@property
|
|
29
|
-
def _context_manager_class(
|
|
51
|
+
def _context_manager_class(
|
|
52
|
+
self,
|
|
53
|
+
) -> Type["_AbstractPrioritySemaphoreContextManager[PT]"]:
|
|
30
54
|
raise NotImplementedError
|
|
31
|
-
|
|
55
|
+
|
|
32
56
|
@property
|
|
33
57
|
def _top_priority(self) -> PT:
|
|
34
58
|
# You can use this so you can set priorities with non numeric comparable values
|
|
35
59
|
raise NotImplementedError
|
|
36
60
|
|
|
37
61
|
def __init__(self, value: int = 1, *, name: Optional[str] = None) -> None:
|
|
38
|
-
|
|
62
|
+
"""Initializes the priority semaphore.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
value: The initial capacity of the semaphore.
|
|
66
|
+
name: An optional name for the semaphore, used for debugging.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
self._context_managers = {}
|
|
70
|
+
"""A dictionary mapping priorities to their context managers."""
|
|
71
|
+
|
|
39
72
|
self._capacity = value
|
|
73
|
+
"""The initial capacity of the semaphore."""
|
|
74
|
+
|
|
40
75
|
super().__init__(value, name=name)
|
|
41
76
|
self._waiters = []
|
|
77
|
+
"""A heap queue of context managers, sorted by priority."""
|
|
78
|
+
|
|
42
79
|
# NOTE: This should (hopefully) be temporary
|
|
43
80
|
self._potential_lost_waiters: List["asyncio.Future[None]"] = []
|
|
81
|
+
"""A list of futures representing waiters that might have been lost."""
|
|
44
82
|
|
|
45
83
|
def __repr__(self) -> str:
|
|
84
|
+
"""Returns a string representation of the semaphore."""
|
|
46
85
|
return f"<{self.__class__.__name__} name={self.name} capacity={self._capacity} value={self._value} waiters={self._count_waiters()}>"
|
|
47
86
|
|
|
48
87
|
async def __aenter__(self) -> None:
|
|
88
|
+
"""Enters the semaphore context, acquiring it with the top priority."""
|
|
49
89
|
await self[self._top_priority].acquire()
|
|
50
90
|
|
|
51
91
|
async def __aexit__(self, *_) -> None:
|
|
92
|
+
"""Exits the semaphore context, releasing it with the top priority."""
|
|
52
93
|
self[self._top_priority].release()
|
|
53
|
-
|
|
94
|
+
|
|
54
95
|
async def acquire(self) -> Literal[True]:
|
|
96
|
+
"""Acquires the semaphore with the top priority."""
|
|
55
97
|
return await self[self._top_priority].acquire()
|
|
56
|
-
|
|
57
|
-
def __getitem__(
|
|
98
|
+
|
|
99
|
+
def __getitem__(
|
|
100
|
+
self, priority: Optional[PT]
|
|
101
|
+
) -> "_AbstractPrioritySemaphoreContextManager[PT]":
|
|
102
|
+
"""Gets the context manager for a given priority.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
priority: The priority for which to get the context manager. If None, uses the top priority.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The context manager associated with the given priority.
|
|
109
|
+
"""
|
|
58
110
|
priority = self._top_priority if priority is None else priority
|
|
59
111
|
if priority not in self._context_managers:
|
|
60
|
-
context_manager = self._context_manager_class(
|
|
112
|
+
context_manager = self._context_manager_class(
|
|
113
|
+
self, priority, name=self.name
|
|
114
|
+
)
|
|
61
115
|
heapq.heappush(self._waiters, context_manager) # type: ignore [misc]
|
|
62
116
|
self._context_managers[priority] = context_manager
|
|
63
117
|
return self._context_managers[priority]
|
|
64
118
|
|
|
65
119
|
def locked(self) -> bool:
|
|
66
|
-
"""
|
|
120
|
+
"""Checks if the semaphore is locked.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
True if the semaphore cannot be acquired immediately, False otherwise.
|
|
124
|
+
"""
|
|
67
125
|
return self._value == 0 or (
|
|
68
126
|
any(
|
|
69
|
-
cm._waiters and any(not w.cancelled() for w in cm._waiters)
|
|
127
|
+
cm._waiters and any(not w.cancelled() for w in cm._waiters)
|
|
70
128
|
for cm in (self._context_managers.values() or ())
|
|
71
129
|
)
|
|
72
130
|
)
|
|
73
|
-
|
|
131
|
+
|
|
74
132
|
def _count_waiters(self) -> Dict[PT, int]:
|
|
75
|
-
|
|
76
|
-
|
|
133
|
+
"""Counts the number of waiters for each priority.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
A dictionary mapping each priority to the number of waiters.
|
|
137
|
+
"""
|
|
138
|
+
return {
|
|
139
|
+
manager._priority: len(manager.waiters)
|
|
140
|
+
for manager in sorted(self._waiters, key=lambda m: m._priority)
|
|
141
|
+
}
|
|
142
|
+
|
|
77
143
|
def _wake_up_next(self) -> None:
|
|
144
|
+
"""Wakes up the next waiter in line.
|
|
145
|
+
|
|
146
|
+
This method handles the waking of waiters based on priority. It includes an emergency
|
|
147
|
+
procedure to handle potential lost waiters, ensuring that no waiter is left indefinitely
|
|
148
|
+
waiting.
|
|
149
|
+
"""
|
|
78
150
|
while self._waiters:
|
|
79
151
|
manager = heapq.heappop(self._waiters)
|
|
80
152
|
if len(manager) == 0:
|
|
81
153
|
# There are no more waiters, get rid of the empty manager
|
|
82
|
-
logger.debug(
|
|
154
|
+
logger.debug(
|
|
155
|
+
"manager %s has no more waiters, popping from %s",
|
|
156
|
+
manager._repr_no_parent_(),
|
|
157
|
+
self,
|
|
158
|
+
)
|
|
83
159
|
self._context_managers.pop(manager._priority)
|
|
84
160
|
continue
|
|
85
161
|
logger.debug("waking up next for %s", manager._repr_no_parent_())
|
|
86
|
-
|
|
162
|
+
|
|
87
163
|
woke_up = False
|
|
88
164
|
start_len = len(manager)
|
|
89
|
-
|
|
165
|
+
|
|
90
166
|
if not manager._waiters:
|
|
91
|
-
logger.debug(
|
|
92
|
-
|
|
167
|
+
logger.debug("not manager._waiters")
|
|
168
|
+
|
|
93
169
|
while manager._waiters:
|
|
94
170
|
waiter = manager._waiters.popleft()
|
|
95
171
|
self._potential_lost_waiters.remove(waiter)
|
|
@@ -98,15 +174,15 @@ class _AbstractPrioritySemaphore(Semaphore, Generic[PT, CM]):
|
|
|
98
174
|
logger.debug("woke up %s", waiter)
|
|
99
175
|
woke_up = True
|
|
100
176
|
break
|
|
101
|
-
|
|
177
|
+
|
|
102
178
|
if not woke_up:
|
|
103
179
|
self._context_managers.pop(manager._priority)
|
|
104
180
|
continue
|
|
105
|
-
|
|
181
|
+
|
|
106
182
|
end_len = len(manager)
|
|
107
|
-
|
|
183
|
+
|
|
108
184
|
assert start_len > end_len, f"start {start_len} end {end_len}"
|
|
109
|
-
|
|
185
|
+
|
|
110
186
|
if end_len:
|
|
111
187
|
# There are still waiters, put the manager back
|
|
112
188
|
heapq.heappush(self._waiters, manager) # type: ignore [misc]
|
|
@@ -114,57 +190,94 @@ class _AbstractPrioritySemaphore(Semaphore, Generic[PT, CM]):
|
|
|
114
190
|
# There are no more waiters, get rid of the empty manager
|
|
115
191
|
self._context_managers.pop(manager._priority)
|
|
116
192
|
return
|
|
117
|
-
|
|
118
|
-
# emergency procedure (hopefully temporary):
|
|
193
|
+
|
|
194
|
+
# emergency procedure (hopefully temporary):
|
|
119
195
|
while self._potential_lost_waiters:
|
|
120
196
|
waiter = self._potential_lost_waiters.pop(0)
|
|
121
|
-
logger.debug(
|
|
197
|
+
logger.debug("we found a lost waiter %s", waiter)
|
|
122
198
|
if not waiter.done():
|
|
123
199
|
waiter.set_result(None)
|
|
124
200
|
logger.debug("woke up lost waiter %s", waiter)
|
|
125
201
|
return
|
|
126
202
|
logger.debug("%s has no waiters to wake", self)
|
|
127
203
|
|
|
204
|
+
|
|
128
205
|
class _AbstractPrioritySemaphoreContextManager(Semaphore, Generic[PT]):
|
|
206
|
+
"""
|
|
207
|
+
A context manager for priority semaphore waiters.
|
|
208
|
+
|
|
209
|
+
This context manager is associated with a specific priority and handles
|
|
210
|
+
the acquisition and release of the semaphore for waiters with that priority.
|
|
211
|
+
"""
|
|
212
|
+
|
|
129
213
|
_loop: asyncio.AbstractEventLoop
|
|
130
214
|
_waiters: Deque[asyncio.Future] # type: ignore [assignment]
|
|
131
215
|
__slots__ = "_parent", "_priority"
|
|
132
|
-
|
|
216
|
+
|
|
133
217
|
@property
|
|
134
218
|
def _priority_name(self) -> str:
|
|
135
219
|
raise NotImplementedError
|
|
136
|
-
|
|
137
|
-
def __init__(
|
|
220
|
+
|
|
221
|
+
def __init__(
|
|
222
|
+
self,
|
|
223
|
+
parent: _AbstractPrioritySemaphore,
|
|
224
|
+
priority: PT,
|
|
225
|
+
name: Optional[str] = None,
|
|
226
|
+
) -> None:
|
|
227
|
+
"""Initializes the context manager for a specific priority.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
parent: The parent semaphore.
|
|
231
|
+
priority: The priority associated with this context manager.
|
|
232
|
+
name: An optional name for the context manager, used for debugging.
|
|
233
|
+
"""
|
|
234
|
+
|
|
138
235
|
self._parent = parent
|
|
236
|
+
"""The parent semaphore."""
|
|
237
|
+
|
|
139
238
|
self._priority = priority
|
|
239
|
+
"""The priority associated with this context manager."""
|
|
240
|
+
|
|
140
241
|
super().__init__(0, name=name)
|
|
141
242
|
|
|
142
243
|
def __repr__(self) -> str:
|
|
244
|
+
"""Returns a string representation of the context manager."""
|
|
143
245
|
return f"<{self.__class__.__name__} parent={self._parent} {self._priority_name}={self._priority} waiters={len(self)}>"
|
|
144
|
-
|
|
246
|
+
|
|
145
247
|
def _repr_no_parent_(self) -> str:
|
|
248
|
+
"""Returns a string representation of the context manager without the parent."""
|
|
146
249
|
return f"<{self.__class__.__name__} parent_name={self._parent.name} {self._priority_name}={self._priority} waiters={len(self)}>"
|
|
147
|
-
|
|
250
|
+
|
|
148
251
|
def __lt__(self, other) -> bool:
|
|
252
|
+
"""Compares this context manager with another based on priority.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
other: The other context manager to compare with.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
True if this context manager has a lower priority than the other, False otherwise.
|
|
259
|
+
"""
|
|
149
260
|
if type(other) is not type(self):
|
|
150
261
|
raise TypeError(f"{other} is not type {self.__class__.__name__}")
|
|
151
262
|
return self._priority < other._priority
|
|
152
|
-
|
|
263
|
+
|
|
153
264
|
@cached_property
|
|
154
265
|
def loop(self) -> asyncio.AbstractEventLoop:
|
|
266
|
+
"""Gets the event loop associated with this context manager."""
|
|
155
267
|
return self._loop or asyncio.get_event_loop()
|
|
156
|
-
|
|
268
|
+
|
|
157
269
|
@property
|
|
158
|
-
def waiters
|
|
270
|
+
def waiters(self) -> Deque[asyncio.Future]:
|
|
271
|
+
"""Gets the deque of waiters for this context manager."""
|
|
159
272
|
if self._waiters is None:
|
|
160
273
|
self._waiters = deque()
|
|
161
274
|
return self._waiters
|
|
162
|
-
|
|
275
|
+
|
|
163
276
|
async def acquire(self) -> Literal[True]:
|
|
164
|
-
"""
|
|
277
|
+
"""Acquires the semaphore for this context manager.
|
|
165
278
|
|
|
166
279
|
If the internal counter is larger than zero on entry,
|
|
167
|
-
decrement it by one and return True immediately.
|
|
280
|
+
decrement it by one and return True immediately. If it is
|
|
168
281
|
zero on entry, block, waiting until some other coroutine has
|
|
169
282
|
called release() to make it larger than 0, and then return
|
|
170
283
|
True.
|
|
@@ -185,31 +298,38 @@ class _AbstractPrioritySemaphoreContextManager(Semaphore, Generic[PT]):
|
|
|
185
298
|
raise
|
|
186
299
|
self._parent._value -= 1
|
|
187
300
|
return True
|
|
301
|
+
|
|
188
302
|
def release(self) -> None:
|
|
303
|
+
"""Releases the semaphore for this context manager."""
|
|
189
304
|
self._parent.release()
|
|
190
|
-
|
|
191
|
-
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class _PrioritySemaphoreContextManager(
|
|
308
|
+
_AbstractPrioritySemaphoreContextManager[Numeric]
|
|
309
|
+
):
|
|
310
|
+
"""Context manager for numeric priority semaphores."""
|
|
311
|
+
|
|
192
312
|
_priority_name = "priority"
|
|
193
313
|
|
|
314
|
+
|
|
194
315
|
class PrioritySemaphore(_AbstractPrioritySemaphore[Numeric, _PrioritySemaphoreContextManager]): # type: ignore [type-var]
|
|
316
|
+
"""Semaphore that uses numeric priorities for waiters.
|
|
317
|
+
|
|
318
|
+
It's similar to a regular Semaphore but requires each waiter to have a priority:
|
|
319
|
+
|
|
320
|
+
Examples:
|
|
321
|
+
The primary way to use this semaphore is by specifying a priority.
|
|
322
|
+
|
|
323
|
+
>>> priority_semaphore = PrioritySemaphore(10)
|
|
324
|
+
>>> async with priority_semaphore[priority]:
|
|
325
|
+
... await do_stuff()
|
|
326
|
+
|
|
327
|
+
You can also enter and exit this semaphore without specifying a priority, and it will use the top priority by default:
|
|
328
|
+
|
|
329
|
+
>>> priority_semaphore = PrioritySemaphore(10)
|
|
330
|
+
>>> async with priority_semaphore:
|
|
331
|
+
... await do_stuff()
|
|
332
|
+
"""
|
|
333
|
+
|
|
195
334
|
_context_manager_class = _PrioritySemaphoreContextManager
|
|
196
335
|
_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
|
-
"""
|