dycw-utilities 0.122.0__py3-none-any.whl → 0.123.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.122.0
3
+ Version: 0.123.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,7 +1,7 @@
1
- utilities/__init__.py,sha256=V19RNamTpOs4KxZ_wh7w8SsT-d-C24iIQHK07Pu3opA,60
1
+ utilities/__init__.py,sha256=AxdGMrtAz54a0Yk125XZ1e--h0FOtLVgpyniB0rxFGA,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=sVCGCGl5vGo1MztLcd46DiuWBR6XQW1CsGDyDdaevYg,22766
4
+ utilities/asyncio.py,sha256=PJR4WxQ4C5AlTyRcEd0_n2ZaIl37k3AirLBUytZ4Xcg,23267
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
@@ -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.122.0.dist-info/METADATA,sha256=VUsd4omUw1y-str8uasKFbLZUMA2Sr3HpSaq8f8coh0,12943
92
- dycw_utilities-0.122.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
- dycw_utilities-0.122.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.122.0.dist-info/RECORD,,
91
+ dycw_utilities-0.123.0.dist-info/METADATA,sha256=pcPzAE2-DZzAYxtEr2fvJS3yPFJnQ942lzEuFcXfr-E,12943
92
+ dycw_utilities-0.123.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
+ dycw_utilities-0.123.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
+ dycw_utilities-0.123.0.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.122.0"
3
+ __version__ = "0.123.0"
utilities/asyncio.py CHANGED
@@ -19,6 +19,7 @@ from asyncio import (
19
19
  )
20
20
  from collections.abc import Callable, Hashable, Iterable, Iterator, Mapping
21
21
  from contextlib import (
22
+ AbstractAsyncContextManager,
22
23
  AsyncExitStack,
23
24
  _AsyncGeneratorContextManager,
24
25
  asynccontextmanager,
@@ -85,6 +86,7 @@ class EnhancedTaskGroup(TaskGroup):
85
86
  _semaphore: Semaphore | None
86
87
  _timeout: Duration | None
87
88
  _error: type[Exception]
89
+ _stack: AsyncExitStack
88
90
  _timeout_cm: _AsyncGeneratorContextManager[None] | None
89
91
 
90
92
  @override
@@ -99,8 +101,24 @@ class EnhancedTaskGroup(TaskGroup):
99
101
  self._semaphore = None if max_tasks is None else Semaphore(max_tasks)
100
102
  self._timeout = timeout
101
103
  self._error = error
104
+ self._stack = AsyncExitStack()
102
105
  self._timeout_cm = None
103
106
 
107
+ @override
108
+ async def __aenter__(self) -> Self:
109
+ _ = await self._stack.__aenter__()
110
+ return await super().__aenter__()
111
+
112
+ @override
113
+ async def __aexit__(
114
+ self,
115
+ et: type[BaseException] | None,
116
+ exc: BaseException | None,
117
+ tb: TracebackType | None,
118
+ ) -> None:
119
+ _ = await self._stack.__aexit__(et, exc, tb)
120
+ _ = await super().__aexit__(et, exc, tb)
121
+
104
122
  @override
105
123
  def create_task(
106
124
  self,
@@ -116,6 +134,11 @@ class EnhancedTaskGroup(TaskGroup):
116
134
  coroutine = self._wrap_with_timeout(coroutine)
117
135
  return super().create_task(coroutine, name=name, context=context)
118
136
 
137
+ def create_task_context(self, cm: AbstractAsyncContextManager[_T], /) -> Task[_T]:
138
+ """Have the TaskGroup start an asynchronous context manager."""
139
+ _ = self._stack.push_async_callback(cm.__aexit__, None, None, None)
140
+ return self.create_task(cm.__aenter__())
141
+
119
142
  async def _wrap_with_semaphore(
120
143
  self, semaphore: Semaphore, coroutine: _CoroutineLike[_T], /
121
144
  ) -> _T:
@@ -155,18 +178,12 @@ class InfiniteLooper(ABC, Generic[THashable]):
155
178
 
156
179
  async def __aenter__(self) -> Self:
157
180
  """Context manager entry."""
158
- if (self._task is None) and (self._depth == 0):
159
- _ = await self._stack.__aenter__()
181
+ if self._depth == 0:
160
182
  self._task = create_task(self._run_looper())
161
183
  if self._await_upon_aenter:
162
184
  with suppress(CancelledError):
163
185
  await self._task
164
- elif (self._task is not None) and (self._depth >= 1):
165
- ...
166
- else:
167
- raise ImpossibleCaseError( # pragma: no cover
168
- case=[f"{self._task=}", f"{self._depth=}"]
169
- )
186
+ _ = await self._stack.__aenter__()
170
187
  self._depth += 1
171
188
  return self
172
189
 
@@ -178,18 +195,16 @@ class InfiniteLooper(ABC, Generic[THashable]):
178
195
  ) -> None:
179
196
  """Context manager exit."""
180
197
  _ = (exc_type, exc_value, traceback)
181
- if (self._task is None) or (self._depth == 0):
182
- raise ImpossibleCaseError( # pragma: no cover
183
- case=[f"{self._task=}", f"{self._depth=}"]
184
- )
185
- self._depth -= 1
186
- if self._depth == 0:
187
- _ = await self._stack.__aexit__(exc_type, exc_value, traceback)
198
+ self._depth = max(self._depth - 1, 0)
199
+ if (self._depth == 0) and (self._task is not None):
188
200
  with suppress(CancelledError):
189
201
  await self._task
190
202
  self._task = None
191
- with suppress(Exception):
203
+ try:
192
204
  await self._teardown()
205
+ except Exception as error: # noqa: BLE001
206
+ self._error_upon_teardown(error)
207
+ _ = await self._stack.__aexit__(exc_type, exc_value, traceback)
193
208
 
194
209
  async def stop(self) -> None:
195
210
  """Stop the service."""