dycw-utilities 0.122.1__py3-none-any.whl → 0.124.0__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.
- {dycw_utilities-0.122.1.dist-info → dycw_utilities-0.124.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.122.1.dist-info → dycw_utilities-0.124.0.dist-info}/RECORD +9 -9
- utilities/__init__.py +1 -1
- utilities/asyncio.py +191 -61
- utilities/redis.py +2 -2
- utilities/slack_sdk.py +6 -10
- utilities/sqlalchemy.py +2 -1
- {dycw_utilities-0.122.1.dist-info → dycw_utilities-0.124.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.122.1.dist-info → dycw_utilities-0.124.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,7 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=HeZfuK-rk7XFCrcSKl_fSwvax77ZgizR9BO2AjEIbLs,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
|
4
|
-
utilities/asyncio.py,sha256=
|
4
|
+
utilities/asyncio.py,sha256=joGmwv-WiDLYoK4q41TtVgfIT23s8Ok46jT_yEQGjaM,28240
|
5
5
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
6
6
|
utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
|
7
7
|
utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
|
@@ -59,15 +59,15 @@ utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq
|
|
59
59
|
utilities/python_dotenv.py,sha256=iWcnpXbH7S6RoXHiLlGgyuH6udCupAcPd_gQ0eAenQ0,3190
|
60
60
|
utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
|
61
61
|
utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
|
62
|
-
utilities/redis.py,sha256=
|
62
|
+
utilities/redis.py,sha256=fMVKsVCjCv63m_JgoOKqj3wHVzzHR0s4suvZ-mBy1gk,26615
|
63
63
|
utilities/reprlib.py,sha256=Re9bk3n-kC__9DxQmRlevqFA86pE6TtVfWjUgpbVOv0,1849
|
64
64
|
utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
|
65
65
|
utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
|
66
66
|
utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
|
67
67
|
utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
|
68
|
-
utilities/slack_sdk.py,sha256=
|
68
|
+
utilities/slack_sdk.py,sha256=Q8UakiB7qo6SUfaBDB0j1N4b8MuFzaD9lG5HGq7rtuw,3200
|
69
69
|
utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
|
70
|
-
utilities/sqlalchemy.py,sha256=
|
70
|
+
utilities/sqlalchemy.py,sha256=p8vsHaNRoeq5zJouIKyp9piFM26wtm5yR4DkzCMFDSw,35471
|
71
71
|
utilities/sqlalchemy_polars.py,sha256=wjJpoUo-yO9E2ujpG_06vV5r2OdvBiQ4yvV6wKCa2Tk,15605
|
72
72
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
73
73
|
utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
|
@@ -88,7 +88,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
88
88
|
utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
|
89
89
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
90
90
|
utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
|
91
|
-
dycw_utilities-0.
|
92
|
-
dycw_utilities-0.
|
93
|
-
dycw_utilities-0.
|
94
|
-
dycw_utilities-0.
|
91
|
+
dycw_utilities-0.124.0.dist-info/METADATA,sha256=-1BZhlwOBb6JkkxdOqznS-XXxhlcvdZfGawiKrjB7Fc,12943
|
92
|
+
dycw_utilities-0.124.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
93
|
+
dycw_utilities-0.124.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
94
|
+
dycw_utilities-0.124.0.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/asyncio.py
CHANGED
@@ -8,6 +8,7 @@ from asyncio import (
|
|
8
8
|
PriorityQueue,
|
9
9
|
Queue,
|
10
10
|
QueueEmpty,
|
11
|
+
QueueFull,
|
11
12
|
Semaphore,
|
12
13
|
StreamReader,
|
13
14
|
Task,
|
@@ -27,6 +28,7 @@ from contextlib import (
|
|
27
28
|
)
|
28
29
|
from dataclasses import dataclass, field
|
29
30
|
from io import StringIO
|
31
|
+
from itertools import chain
|
30
32
|
from logging import getLogger
|
31
33
|
from subprocess import PIPE
|
32
34
|
from sys import stderr, stdout
|
@@ -43,6 +45,8 @@ from typing import (
|
|
43
45
|
override,
|
44
46
|
)
|
45
47
|
|
48
|
+
from typing_extensions import deprecated
|
49
|
+
|
46
50
|
from utilities.datetime import (
|
47
51
|
MINUTE,
|
48
52
|
SECOND,
|
@@ -53,7 +57,6 @@ from utilities.datetime import (
|
|
53
57
|
)
|
54
58
|
from utilities.errors import ImpossibleCaseError, repr_error
|
55
59
|
from utilities.functions import ensure_int, ensure_not_none, get_class_name
|
56
|
-
from utilities.reprlib import get_repr
|
57
60
|
from utilities.sentinel import Sentinel, sentinel
|
58
61
|
from utilities.types import (
|
59
62
|
Coroutine1,
|
@@ -67,6 +70,7 @@ from utilities.types import (
|
|
67
70
|
if TYPE_CHECKING:
|
68
71
|
from asyncio import _CoroutineLike
|
69
72
|
from asyncio.subprocess import Process
|
73
|
+
from collections import deque
|
70
74
|
from collections.abc import AsyncIterator, Sequence
|
71
75
|
from contextvars import Context
|
72
76
|
from types import TracebackType
|
@@ -77,6 +81,164 @@ if TYPE_CHECKING:
|
|
77
81
|
_T = TypeVar("_T")
|
78
82
|
|
79
83
|
|
84
|
+
class EnhancedQueue(Queue[_T]):
|
85
|
+
"""An asynchronous deque."""
|
86
|
+
|
87
|
+
@override
|
88
|
+
def __init__(self, maxsize: int = 0) -> None:
|
89
|
+
super().__init__(maxsize=maxsize)
|
90
|
+
self._finished: Event
|
91
|
+
self._getters: deque[Any]
|
92
|
+
self._putters: deque[Any]
|
93
|
+
self._queue: deque[_T]
|
94
|
+
self._unfinished_tasks: int
|
95
|
+
|
96
|
+
@override
|
97
|
+
@deprecated("Use `get_left`/`get_right` instead")
|
98
|
+
async def get(self) -> _T:
|
99
|
+
raise RuntimeError # pragma: no cover
|
100
|
+
|
101
|
+
@override
|
102
|
+
@deprecated("Use `get_left_nowait`/`get_right_nowait` instead")
|
103
|
+
def get_nowait(self) -> _T:
|
104
|
+
raise RuntimeError # pragma: no cover
|
105
|
+
|
106
|
+
@override
|
107
|
+
@deprecated("Use `put_left`/`put_right` instead")
|
108
|
+
async def put(self, item: _T) -> None:
|
109
|
+
raise RuntimeError(item) # pragma: no cover
|
110
|
+
|
111
|
+
@override
|
112
|
+
@deprecated("Use `put_left_nowait`/`put_right_nowait` instead")
|
113
|
+
def put_nowait(self, item: _T) -> None:
|
114
|
+
raise RuntimeError(item) # pragma: no cover
|
115
|
+
|
116
|
+
# get all
|
117
|
+
|
118
|
+
async def get_all(self, *, reverse: bool = False) -> Sequence[_T]:
|
119
|
+
"""Remove and return all items from the queue."""
|
120
|
+
first = await (self.get_right() if reverse else self.get_left())
|
121
|
+
return list(chain([first], self.get_all_nowait(reverse=reverse)))
|
122
|
+
|
123
|
+
def get_all_nowait(self, *, reverse: bool = False) -> Sequence[_T]:
|
124
|
+
"""Remove and return all items from the queue without blocking."""
|
125
|
+
items: Sequence[_T] = []
|
126
|
+
while True:
|
127
|
+
try:
|
128
|
+
items.append(
|
129
|
+
self.get_right_nowait() if reverse else self.get_left_nowait()
|
130
|
+
)
|
131
|
+
except QueueEmpty:
|
132
|
+
return items
|
133
|
+
|
134
|
+
# get left/right
|
135
|
+
|
136
|
+
async def get_left(self) -> _T:
|
137
|
+
"""Remove and return an item from the start of the queue."""
|
138
|
+
return await self._get_left_or_right(self._get)
|
139
|
+
|
140
|
+
async def get_right(self) -> _T:
|
141
|
+
"""Remove and return an item from the end of the queue."""
|
142
|
+
return await self._get_left_or_right(self._get_right)
|
143
|
+
|
144
|
+
def get_left_nowait(self) -> _T:
|
145
|
+
"""Remove and return an item from the start of the queue without blocking."""
|
146
|
+
return self._get_left_or_right_nowait(self._get)
|
147
|
+
|
148
|
+
def get_right_nowait(self) -> _T:
|
149
|
+
"""Remove and return an item from the end of the queue without blocking."""
|
150
|
+
return self._get_left_or_right_nowait(self._get_right)
|
151
|
+
|
152
|
+
# put left/right
|
153
|
+
|
154
|
+
async def put_left(self, *items: _T) -> None:
|
155
|
+
"""Put items into the queue at the start."""
|
156
|
+
return await self._put_left_or_right(self._put_left, *items)
|
157
|
+
|
158
|
+
async def put_right(self, *items: _T) -> None:
|
159
|
+
"""Put items into the queue at the end."""
|
160
|
+
return await self._put_left_or_right(self._put, *items)
|
161
|
+
|
162
|
+
def put_left_nowait(self, *items: _T) -> None:
|
163
|
+
"""Put items into the queue at the start without blocking."""
|
164
|
+
self._put_left_or_right_nowait(self._put_left, *items)
|
165
|
+
|
166
|
+
def put_right_nowait(self, *items: _T) -> None:
|
167
|
+
"""Put items into the queue at the end without blocking."""
|
168
|
+
self._put_left_or_right_nowait(self._put, *items)
|
169
|
+
|
170
|
+
# private
|
171
|
+
|
172
|
+
def _put_left(self, item: _T) -> None:
|
173
|
+
self._queue.appendleft(item)
|
174
|
+
|
175
|
+
def _get_right(self) -> _T:
|
176
|
+
return self._queue.pop()
|
177
|
+
|
178
|
+
async def _get_left_or_right(self, getter_use: Callable[[], _T], /) -> _T:
|
179
|
+
while self.empty(): # pragma: no cover
|
180
|
+
getter = self._get_loop().create_future() # pyright: ignore[reportAttributeAccessIssue]
|
181
|
+
self._getters.append(getter)
|
182
|
+
try:
|
183
|
+
await getter
|
184
|
+
except:
|
185
|
+
getter.cancel()
|
186
|
+
with suppress(ValueError):
|
187
|
+
self._getters.remove(getter)
|
188
|
+
if not self.empty() and not getter.cancelled():
|
189
|
+
self._wakeup_next(self._getters) # pyright: ignore[reportAttributeAccessIssue]
|
190
|
+
raise
|
191
|
+
return getter_use()
|
192
|
+
|
193
|
+
def _get_left_or_right_nowait(self, getter: Callable[[], _T], /) -> _T:
|
194
|
+
if self.empty():
|
195
|
+
raise QueueEmpty
|
196
|
+
item = getter()
|
197
|
+
self._wakeup_next(self._putters) # pyright: ignore[reportAttributeAccessIssue]
|
198
|
+
return item
|
199
|
+
|
200
|
+
async def _put_left_or_right(
|
201
|
+
self, putter_use: Callable[[_T], None], /, *items: _T
|
202
|
+
) -> None:
|
203
|
+
"""Put an item into the queue."""
|
204
|
+
for item in items:
|
205
|
+
await self._put_left_or_right_one(putter_use, item)
|
206
|
+
|
207
|
+
async def _put_left_or_right_one(
|
208
|
+
self, putter_use: Callable[[_T], None], item: _T, /
|
209
|
+
) -> None:
|
210
|
+
"""Put an item into the queue."""
|
211
|
+
while self.full(): # pragma: no cover
|
212
|
+
putter = self._get_loop().create_future() # pyright: ignore[reportAttributeAccessIssue]
|
213
|
+
self._putters.append(putter)
|
214
|
+
try:
|
215
|
+
await putter
|
216
|
+
except:
|
217
|
+
putter.cancel()
|
218
|
+
with suppress(ValueError):
|
219
|
+
self._putters.remove(putter)
|
220
|
+
if not self.full() and not putter.cancelled():
|
221
|
+
self._wakeup_next(self._putters) # pyright: ignore[reportAttributeAccessIssue]
|
222
|
+
raise
|
223
|
+
return putter_use(item)
|
224
|
+
|
225
|
+
def _put_left_or_right_nowait(
|
226
|
+
self, putter: Callable[[_T], None], /, *items: _T
|
227
|
+
) -> None:
|
228
|
+
for item in items:
|
229
|
+
self._put_left_or_right_nowait_one(putter, item)
|
230
|
+
|
231
|
+
def _put_left_or_right_nowait_one(
|
232
|
+
self, putter: Callable[[_T], None], item: _T, /
|
233
|
+
) -> None:
|
234
|
+
if self.full(): # pragma: no cover
|
235
|
+
raise QueueFull
|
236
|
+
putter(item)
|
237
|
+
self._unfinished_tasks += 1
|
238
|
+
self._finished.clear()
|
239
|
+
self._wakeup_next(self._getters) # pyright: ignore[reportAttributeAccessIssue]
|
240
|
+
|
241
|
+
|
80
242
|
##
|
81
243
|
|
82
244
|
|
@@ -87,7 +249,6 @@ class EnhancedTaskGroup(TaskGroup):
|
|
87
249
|
_timeout: Duration | None
|
88
250
|
_error: type[Exception]
|
89
251
|
_stack: AsyncExitStack
|
90
|
-
_stack_entered: bool
|
91
252
|
_timeout_cm: _AsyncGeneratorContextManager[None] | None
|
92
253
|
|
93
254
|
@override
|
@@ -103,7 +264,6 @@ class EnhancedTaskGroup(TaskGroup):
|
|
103
264
|
self._timeout = timeout
|
104
265
|
self._error = error
|
105
266
|
self._stack = AsyncExitStack()
|
106
|
-
self._stack_entered = False # TOOD: no need
|
107
267
|
self._timeout_cm = None
|
108
268
|
|
109
269
|
@override
|
@@ -136,8 +296,10 @@ class EnhancedTaskGroup(TaskGroup):
|
|
136
296
|
coroutine = self._wrap_with_timeout(coroutine)
|
137
297
|
return super().create_task(coroutine, name=name, context=context)
|
138
298
|
|
139
|
-
|
140
|
-
|
299
|
+
def create_task_context(self, cm: AbstractAsyncContextManager[_T], /) -> Task[_T]:
|
300
|
+
"""Have the TaskGroup start an asynchronous context manager."""
|
301
|
+
_ = self._stack.push_async_callback(cm.__aexit__, None, None, None)
|
302
|
+
return self.create_task(cm.__aenter__())
|
141
303
|
|
142
304
|
async def _wrap_with_semaphore(
|
143
305
|
self, semaphore: Semaphore, coroutine: _CoroutineLike[_T], /
|
@@ -178,18 +340,12 @@ class InfiniteLooper(ABC, Generic[THashable]):
|
|
178
340
|
|
179
341
|
async def __aenter__(self) -> Self:
|
180
342
|
"""Context manager entry."""
|
181
|
-
if
|
182
|
-
_ = await self._stack.__aenter__()
|
343
|
+
if self._depth == 0:
|
183
344
|
self._task = create_task(self._run_looper())
|
184
345
|
if self._await_upon_aenter:
|
185
346
|
with suppress(CancelledError):
|
186
347
|
await self._task
|
187
|
-
|
188
|
-
...
|
189
|
-
else:
|
190
|
-
raise ImpossibleCaseError( # pragma: no cover
|
191
|
-
case=[f"{self._task=}", f"{self._depth=}"]
|
192
|
-
)
|
348
|
+
_ = await self._stack.__aenter__()
|
193
349
|
self._depth += 1
|
194
350
|
return self
|
195
351
|
|
@@ -201,18 +357,16 @@ class InfiniteLooper(ABC, Generic[THashable]):
|
|
201
357
|
) -> None:
|
202
358
|
"""Context manager exit."""
|
203
359
|
_ = (exc_type, exc_value, traceback)
|
204
|
-
|
205
|
-
|
206
|
-
case=[f"{self._task=}", f"{self._depth=}"]
|
207
|
-
)
|
208
|
-
self._depth -= 1
|
209
|
-
if self._depth == 0:
|
210
|
-
_ = await self._stack.__aexit__(exc_type, exc_value, traceback)
|
360
|
+
self._depth = max(self._depth - 1, 0)
|
361
|
+
if (self._depth == 0) and (self._task is not None):
|
211
362
|
with suppress(CancelledError):
|
212
363
|
await self._task
|
213
364
|
self._task = None
|
214
|
-
|
365
|
+
try:
|
215
366
|
await self._teardown()
|
367
|
+
except Exception as error: # noqa: BLE001
|
368
|
+
self._error_upon_teardown(error)
|
369
|
+
_ = await self._stack.__aexit__(exc_type, exc_value, traceback)
|
216
370
|
|
217
371
|
async def stop(self) -> None:
|
218
372
|
"""Stop the service."""
|
@@ -436,14 +590,13 @@ class _InfiniteLooperDefaultEventError(InfiniteLooperError):
|
|
436
590
|
class InfiniteQueueLooper(InfiniteLooper[THashable], Generic[THashable, _T]):
|
437
591
|
"""An infinite loop which processes a queue."""
|
438
592
|
|
439
|
-
queue_type: type[Queue[_T]] = field(default=Queue, repr=False)
|
440
593
|
_await_upon_aenter: bool = field(default=False, init=False, repr=False)
|
441
|
-
_queue:
|
594
|
+
_queue: EnhancedQueue[_T] = field(init=False, repr=False)
|
442
595
|
|
443
596
|
@override
|
444
597
|
def __post_init__(self) -> None:
|
445
598
|
super().__post_init__()
|
446
|
-
self._queue =
|
599
|
+
self._queue = EnhancedQueue()
|
447
600
|
|
448
601
|
def __len__(self) -> int:
|
449
602
|
return self._queue.qsize()
|
@@ -451,55 +604,32 @@ class InfiniteQueueLooper(InfiniteLooper[THashable], Generic[THashable, _T]):
|
|
451
604
|
@override
|
452
605
|
async def _core(self) -> None:
|
453
606
|
"""Run the core part of the loop."""
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
except Exception as error: # noqa: BLE001
|
458
|
-
raise InfiniteQueueLooperError(
|
459
|
-
looper=self, items=items, error=error
|
460
|
-
) from None
|
607
|
+
first = await self._queue.get_left()
|
608
|
+
self._queue.put_left_nowait(first)
|
609
|
+
await self._process_queue()
|
461
610
|
|
462
611
|
@abstractmethod
|
463
|
-
async def
|
464
|
-
"""Process the
|
612
|
+
async def _process_queue(self) -> None:
|
613
|
+
"""Process the queue."""
|
465
614
|
|
466
615
|
def empty(self) -> bool:
|
467
616
|
"""Check if the queue is empty."""
|
468
617
|
return self._queue.empty()
|
469
618
|
|
470
|
-
def
|
471
|
-
"""Put items into the queue."""
|
472
|
-
|
619
|
+
def put_left_nowait(self, *items: _T) -> None:
|
620
|
+
"""Put items into the queue at the start without blocking."""
|
621
|
+
self._queue.put_left_nowait(*items) # pragma: no cover
|
622
|
+
|
623
|
+
def put_right_nowait(self, *items: _T) -> None:
|
624
|
+
"""Put items into the queue at the end without blocking."""
|
625
|
+
self._queue.put_right_nowait(*items) # pragma: no cover
|
473
626
|
|
474
627
|
async def run_until_empty(self) -> None:
|
475
628
|
"""Run until the queue is empty."""
|
476
629
|
while not self.empty():
|
477
|
-
await self.
|
630
|
+
await self._process_queue()
|
478
631
|
await self.stop()
|
479
632
|
|
480
|
-
@override
|
481
|
-
def _error_upon_core(self, error: Exception, /) -> None:
|
482
|
-
"""Handle any errors upon running the core function."""
|
483
|
-
if self.logger is not None:
|
484
|
-
if isinstance(error, InfiniteQueueLooperError):
|
485
|
-
getLogger(name=self.logger).error(
|
486
|
-
"%r encountered %s whilst processing %d item(s) %s; sleeping %s...",
|
487
|
-
get_class_name(self),
|
488
|
-
repr_error(error.error),
|
489
|
-
len(error.items),
|
490
|
-
get_repr(error.items),
|
491
|
-
self._sleep_restart_desc,
|
492
|
-
)
|
493
|
-
else:
|
494
|
-
super()._error_upon_core(error) # pragma: no cover
|
495
|
-
|
496
|
-
|
497
|
-
@dataclass(kw_only=True, slots=True)
|
498
|
-
class InfiniteQueueLooperError(Exception, Generic[_T]):
|
499
|
-
looper: InfiniteQueueLooper[Any, Any]
|
500
|
-
items: Sequence[_T]
|
501
|
-
error: Exception
|
502
|
-
|
503
633
|
|
504
634
|
##
|
505
635
|
|
@@ -723,11 +853,11 @@ async def timeout_dur(
|
|
723
853
|
|
724
854
|
|
725
855
|
__all__ = [
|
856
|
+
"EnhancedQueue",
|
726
857
|
"EnhancedTaskGroup",
|
727
858
|
"InfiniteLooper",
|
728
859
|
"InfiniteLooperError",
|
729
860
|
"InfiniteQueueLooper",
|
730
|
-
"InfiniteQueueLooperError",
|
731
861
|
"StreamCommandOutput",
|
732
862
|
"UniquePriorityQueue",
|
733
863
|
"UniqueQueue",
|
utilities/redis.py
CHANGED
@@ -597,8 +597,8 @@ class Publisher(InfiniteQueueLooper[None, tuple[str, EncodableT]]):
|
|
597
597
|
timeout: Duration = _PUBLISH_TIMEOUT
|
598
598
|
|
599
599
|
@override
|
600
|
-
async def
|
601
|
-
for item in
|
600
|
+
async def _process_queue(self) -> None:
|
601
|
+
for item in self._queue.get_all_nowait(): # skipif-ci-and-not-linux
|
602
602
|
channel, data = item
|
603
603
|
_ = await publish(
|
604
604
|
self.redis,
|
utilities/slack_sdk.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from asyncio import Queue
|
4
3
|
from dataclasses import dataclass
|
5
4
|
from http import HTTPStatus
|
6
5
|
from logging import NOTSET, Handler, LogRecord
|
@@ -47,15 +46,12 @@ class SlackHandler(Handler, InfiniteQueueLooper[None, str]):
|
|
47
46
|
level: int = NOTSET,
|
48
47
|
sleep_core: Duration = _SLEEP,
|
49
48
|
sleep_restart: Duration = _SLEEP,
|
50
|
-
queue_type: type[Queue[str]] = Queue,
|
51
49
|
sender: Callable[[str, str], Coroutine1[None]] = _send_adapter,
|
52
50
|
timeout: Duration = _TIMEOUT,
|
53
51
|
) -> None:
|
54
|
-
InfiniteQueueLooper.__init__( # InfiniteQueueLooper first
|
55
|
-
self, queue_type=queue_type
|
56
|
-
)
|
52
|
+
InfiniteQueueLooper.__init__(self) # InfiniteQueueLooper first
|
57
53
|
InfiniteQueueLooper.__post_init__(self)
|
58
|
-
Handler.__init__(self, level=level)
|
54
|
+
Handler.__init__(self, level=level) # Handler next
|
59
55
|
self.url = url
|
60
56
|
self.sender = sender
|
61
57
|
self.timeout = timeout
|
@@ -65,14 +61,14 @@ class SlackHandler(Handler, InfiniteQueueLooper[None, str]):
|
|
65
61
|
@override
|
66
62
|
def emit(self, record: LogRecord) -> None:
|
67
63
|
try:
|
68
|
-
self.
|
64
|
+
self.put_right_nowait(self.format(record))
|
69
65
|
except Exception: # noqa: BLE001 # pragma: no cover
|
70
66
|
self.handleError(record)
|
71
67
|
|
72
68
|
@override
|
73
|
-
async def
|
74
|
-
|
75
|
-
text = "\n".join(
|
69
|
+
async def _process_queue(self) -> None:
|
70
|
+
messages = self._queue.get_all_nowait()
|
71
|
+
text = "\n".join(messages)
|
76
72
|
async with timeout_dur(duration=self.timeout):
|
77
73
|
await self.sender(self.url, text)
|
78
74
|
|
utilities/sqlalchemy.py
CHANGED
@@ -623,7 +623,8 @@ class Upserter(InfiniteQueueLooper[None, _InsertItem]):
|
|
623
623
|
error_insert: type[Exception] = TimeoutError
|
624
624
|
|
625
625
|
@override
|
626
|
-
async def
|
626
|
+
async def _process_queue(self) -> None:
|
627
|
+
items = self._queue.get_all_nowait()
|
627
628
|
await upsert_items(
|
628
629
|
self.engine,
|
629
630
|
*items,
|
File without changes
|
File without changes
|