dycw-utilities 0.125.24__py3-none-any.whl → 0.125.25__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.125.24
3
+ Version: 0.125.25
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
- utilities/__init__.py,sha256=WpvBRRRg9E0dopLOUcZu11h6cdS3FNrVVkS3L8r2LCU,61
1
+ utilities/__init__.py,sha256=3MYZAqKLfaJb4ielcVRGJ7gJj-yObA5kgfPLoc-wN1Q,61
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
- utilities/asyncio.py,sha256=qdhfPAKgeJEOKDN2xQbR35dpvYZSWUUqLhHNJEd-pv0,51733
3
+ utilities/asyncio.py,sha256=K5Kj7rsM0nA17-b7d7mrNgPR1U_NbkfQmTruq5LBLRA,51778
4
4
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
5
5
  utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
6
6
  utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
@@ -66,7 +66,7 @@ utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
66
66
  utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
67
67
  utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
68
68
  utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
69
- utilities/slack_sdk.py,sha256=Q8UakiB7qo6SUfaBDB0j1N4b8MuFzaD9lG5HGq7rtuw,3200
69
+ utilities/slack_sdk.py,sha256=h2DiVkcFyYcT5zzZOAo6CSith5BBlHUbXeOJSL1neK8,5948
70
70
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
71
71
  utilities/sqlalchemy.py,sha256=p8vsHaNRoeq5zJouIKyp9piFM26wtm5yR4DkzCMFDSw,35471
72
72
  utilities/sqlalchemy_polars.py,sha256=s7hQNep2O5DTgIRXyN_JRQma7a4DAtNd25tshaZW8iw,15490
@@ -90,7 +90,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
90
90
  utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
91
91
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
92
92
  utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
93
- dycw_utilities-0.125.24.dist-info/METADATA,sha256=PTMh4H329ZNF8vQUXJ4Z62OIxQ_NrfmL2jBxEhjvuS0,12852
94
- dycw_utilities-0.125.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
95
- dycw_utilities-0.125.24.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
- dycw_utilities-0.125.24.dist-info/RECORD,,
93
+ dycw_utilities-0.125.25.dist-info/METADATA,sha256=TYTHhscXtNic_ikhuiW7WQN30GUx2y-ay4RXYCt3qFI,12852
94
+ dycw_utilities-0.125.25.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
95
+ dycw_utilities-0.125.25.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
+ dycw_utilities-0.125.25.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.125.24"
3
+ __version__ = "0.125.25"
utilities/asyncio.py CHANGED
@@ -901,6 +901,7 @@ class Looper(Generic[_T]):
901
901
  timeout: Duration | None | Sentinel = sentinel,
902
902
  timeout_error: type[Exception] | Sentinel = sentinel,
903
903
  _debug: bool | Sentinel = sentinel,
904
+ **kwargs: Any,
904
905
  ) -> Self:
905
906
  """Replace elements of the looper."""
906
907
  return replace_non_sentinel(
@@ -913,6 +914,7 @@ class Looper(Generic[_T]):
913
914
  timeout=timeout,
914
915
  timeout_error=timeout_error,
915
916
  _debug=_debug,
917
+ **kwargs,
916
918
  )
917
919
 
918
920
  def request_restart(self) -> None:
utilities/slack_sdk.py CHANGED
@@ -3,14 +3,20 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from http import HTTPStatus
5
5
  from logging import NOTSET, Handler, LogRecord
6
- from typing import TYPE_CHECKING, override
6
+ from typing import TYPE_CHECKING, Any, Self, override
7
7
 
8
8
  from slack_sdk.webhook.async_client import AsyncWebhookClient
9
9
 
10
- from utilities.asyncio import InfiniteQueueLooper, timeout_dur
10
+ from utilities.asyncio import (
11
+ InfiniteQueueLooper,
12
+ Looper,
13
+ LooperTimeoutError,
14
+ timeout_dur,
15
+ )
11
16
  from utilities.datetime import MINUTE, SECOND, datetime_duration_to_float
12
17
  from utilities.functools import cache
13
18
  from utilities.math import safe_round
19
+ from utilities.sentinel import Sentinel, sentinel
14
20
 
15
21
  if TYPE_CHECKING:
16
22
  from collections.abc import Callable
@@ -73,6 +79,90 @@ class SlackHandler(Handler, InfiniteQueueLooper[None, str]):
73
79
  await self.sender(self.url, text)
74
80
 
75
81
 
82
+ @dataclass(init=False, unsafe_hash=True)
83
+ class SlackHandlerService(Handler, Looper[str]):
84
+ """Service to send messages to Slack."""
85
+
86
+ @override
87
+ def __init__(
88
+ self,
89
+ *,
90
+ url: str,
91
+ auto_start: bool = False,
92
+ empty_upon_exit: bool = True,
93
+ freq: Duration = SECOND,
94
+ backoff: Duration = SECOND,
95
+ logger: str | None = None,
96
+ timeout: Duration | None = None,
97
+ timeout_error: type[Exception] = LooperTimeoutError,
98
+ _debug: bool = False,
99
+ level: int = NOTSET,
100
+ sender: Callable[[str, str], Coroutine1[None]] = _send_adapter,
101
+ send_timeout: Duration = SECOND,
102
+ ) -> None:
103
+ Looper.__init__( # Looper first
104
+ self,
105
+ auto_start=auto_start,
106
+ freq=freq,
107
+ empty_upon_exit=empty_upon_exit,
108
+ backoff=backoff,
109
+ logger=logger,
110
+ timeout=timeout,
111
+ timeout_error=timeout_error,
112
+ _debug=_debug,
113
+ )
114
+ Looper.__post_init__(self)
115
+ Handler.__init__(self, level=level) # Handler next
116
+ self.url = url
117
+ self.sender = sender
118
+ self.send_timeout = send_timeout
119
+
120
+ @override
121
+ def emit(self, record: LogRecord) -> None:
122
+ fmtted = self.format(record)
123
+ try:
124
+ self.put_right_nowait(fmtted)
125
+ except Exception: # noqa: BLE001 # pragma: no cover
126
+ self.handleError(record)
127
+
128
+ @override
129
+ async def core(self) -> None:
130
+ await super().core()
131
+ if self.empty():
132
+ return
133
+ text = "\n".join(self.get_all_nowait())
134
+ async with timeout_dur(duration=self.send_timeout):
135
+ await self.sender(self.url, text)
136
+
137
+ @override
138
+ def replace(
139
+ self,
140
+ *,
141
+ auto_start: bool | Sentinel = sentinel,
142
+ empty_upon_exit: bool | Sentinel = sentinel,
143
+ freq: Duration | Sentinel = sentinel,
144
+ backoff: Duration | Sentinel = sentinel,
145
+ logger: str | None | Sentinel = sentinel,
146
+ timeout: Duration | None | Sentinel = sentinel,
147
+ timeout_error: type[Exception] | Sentinel = sentinel,
148
+ _debug: bool | Sentinel = sentinel,
149
+ **kwargs: Any,
150
+ ) -> Self:
151
+ """Replace elements of the looper."""
152
+ return super().replace(
153
+ url=self.url,
154
+ auto_start=auto_start,
155
+ empty_upon_exit=empty_upon_exit,
156
+ freq=freq,
157
+ backoff=backoff,
158
+ logger=logger,
159
+ timeout=timeout,
160
+ timeout_error=timeout_error,
161
+ _debug=_debug,
162
+ **kwargs,
163
+ )
164
+
165
+
76
166
  ##
77
167
 
78
168
 
@@ -106,4 +196,4 @@ def _get_client(url: str, /, *, timeout: Duration = _TIMEOUT) -> AsyncWebhookCli
106
196
  return AsyncWebhookClient(url, timeout=timeout_use)
107
197
 
108
198
 
109
- __all__ = ["SendToSlackError", "SlackHandler", "send_to_slack"]
199
+ __all__ = ["SendToSlackError", "SlackHandler", "SlackHandlerService", "send_to_slack"]