python-utils 2.5.6__py3-none-any.whl → 4.0.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.
- python_utils/__about__.py +35 -7
- python_utils/__init__.py +241 -0
- python_utils/_aliases.py +53 -0
- python_utils/aio.py +133 -0
- python_utils/containers.py +637 -0
- python_utils/converters.py +265 -85
- python_utils/decorators.py +216 -6
- python_utils/exceptions.py +47 -0
- python_utils/formatters.py +72 -16
- python_utils/generators.py +126 -0
- python_utils/import_.py +64 -26
- python_utils/logger.py +352 -29
- python_utils/loguru.py +53 -0
- python_utils/terminal.py +127 -67
- python_utils/time.py +371 -18
- python_utils/types.py +179 -0
- python_utils-4.0.0.dist-info/METADATA +389 -0
- python_utils-4.0.0.dist-info/RECORD +21 -0
- {python_utils-2.5.6.dist-info → python_utils-4.0.0.dist-info}/WHEEL +1 -3
- python_utils-2.5.6.dist-info/METADATA +0 -122
- python_utils-2.5.6.dist-info/RECORD +0 -15
- python_utils-2.5.6.dist-info/top_level.txt +0 -1
- /python_utils/{compat.py → py.typed} +0 -0
- {python_utils-2.5.6.dist-info → python_utils-4.0.0.dist-info/licenses}/LICENSE +0 -0
python_utils/time.py
CHANGED
|
@@ -1,14 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
This module provides utility functions for handling time-related operations.
|
|
3
|
+
|
|
4
|
+
Functions::
|
|
5
|
+
|
|
6
|
+
timedelta_to_seconds: Convert a timedelta to seconds (microseconds as
|
|
7
|
+
fraction).
|
|
8
|
+
delta_to_seconds: Convert a timedelta or numeric interval to seconds.
|
|
9
|
+
delta_to_seconds_or_none: Convert a timedelta to seconds or return None.
|
|
10
|
+
format_time: Format a timestamp (timedelta, datetime, or seconds).
|
|
11
|
+
timeout_generator: Generate items from an iterable until a timeout.
|
|
12
|
+
aio_timeout_generator: Async generate items from an iterable until a
|
|
13
|
+
timeout.
|
|
14
|
+
aio_generator_timeout_detector: Detect if an async generator has stalled.
|
|
15
|
+
aio_generator_timeout_detector_decorator: Decorator for the detector.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# pyright: reportUnnecessaryIsInstance=false
|
|
19
|
+
import collections.abc
|
|
2
20
|
import datetime
|
|
21
|
+
import functools
|
|
22
|
+
import itertools
|
|
23
|
+
import time
|
|
24
|
+
import typing
|
|
25
|
+
|
|
26
|
+
import python_utils
|
|
27
|
+
from python_utils import _aliases, exceptions
|
|
28
|
+
|
|
29
|
+
#: Item type produced by the time/timeout generators.
|
|
30
|
+
_T = typing.TypeVar('_T')
|
|
31
|
+
#: Parameter specification for the timeout-detector decorator's target.
|
|
32
|
+
_P = typing.ParamSpec('_P')
|
|
3
33
|
|
|
4
34
|
|
|
5
|
-
|
|
6
|
-
#
|
|
35
|
+
#: The Unix epoch (1970-01-01) as a naive ``datetime``, used as a reference.
|
|
36
|
+
# There might be a better way to get the epoch with tzinfo; please open a
|
|
37
|
+
# pull request if you know one.
|
|
7
38
|
epoch = datetime.datetime(year=1970, month=1, day=1)
|
|
8
39
|
|
|
9
40
|
|
|
10
|
-
def timedelta_to_seconds(delta):
|
|
11
|
-
|
|
41
|
+
def timedelta_to_seconds(delta: datetime.timedelta) -> _aliases.Number:
|
|
42
|
+
"""Convert a timedelta to seconds with the microseconds as fraction.
|
|
12
43
|
|
|
13
44
|
Note that this method has become largely obsolete with the
|
|
14
45
|
`timedelta.total_seconds()` method introduced in Python 2.7.
|
|
@@ -22,7 +53,7 @@ def timedelta_to_seconds(delta):
|
|
|
22
53
|
'1.000001'
|
|
23
54
|
>>> '%.6f' % timedelta_to_seconds(timedelta(microseconds=1))
|
|
24
55
|
'0.000001'
|
|
25
|
-
|
|
56
|
+
"""
|
|
26
57
|
# Only convert to float if needed
|
|
27
58
|
if delta.microseconds:
|
|
28
59
|
total = delta.microseconds * 1e-6
|
|
@@ -33,8 +64,56 @@ def timedelta_to_seconds(delta):
|
|
|
33
64
|
return total
|
|
34
65
|
|
|
35
66
|
|
|
36
|
-
def
|
|
37
|
-
|
|
67
|
+
def delta_to_seconds(interval: _aliases.delta_type) -> _aliases.Number:
|
|
68
|
+
"""
|
|
69
|
+
Convert a timedelta to seconds.
|
|
70
|
+
|
|
71
|
+
>>> delta_to_seconds(datetime.timedelta(seconds=1))
|
|
72
|
+
1
|
|
73
|
+
>>> delta_to_seconds(datetime.timedelta(seconds=1, microseconds=1))
|
|
74
|
+
1.000001
|
|
75
|
+
>>> delta_to_seconds(1)
|
|
76
|
+
1
|
|
77
|
+
>>> delta_to_seconds('whatever') # doctest: +ELLIPSIS
|
|
78
|
+
Traceback (most recent call last):
|
|
79
|
+
...
|
|
80
|
+
TypeError: Unknown type ...
|
|
81
|
+
"""
|
|
82
|
+
if isinstance(interval, datetime.timedelta):
|
|
83
|
+
return timedelta_to_seconds(interval)
|
|
84
|
+
elif isinstance(interval, (int, float)):
|
|
85
|
+
return interval
|
|
86
|
+
else:
|
|
87
|
+
raise TypeError(f'Unknown type {type(interval)}: {interval!r}')
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def delta_to_seconds_or_none(
|
|
91
|
+
interval: _aliases.delta_type | None,
|
|
92
|
+
) -> _aliases.Number | None:
|
|
93
|
+
"""Convert a timedelta to seconds, passing ``None`` through unchanged.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
interval: A timedelta or a number of seconds, or ``None``.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
The interval in seconds, or ``None`` when ``interval`` is ``None``.
|
|
100
|
+
|
|
101
|
+
>>> delta_to_seconds_or_none(datetime.timedelta(seconds=2))
|
|
102
|
+
2
|
|
103
|
+
>>> delta_to_seconds_or_none(None) is None
|
|
104
|
+
True
|
|
105
|
+
"""
|
|
106
|
+
if interval is None:
|
|
107
|
+
return None
|
|
108
|
+
else:
|
|
109
|
+
return delta_to_seconds(interval)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def format_time(
|
|
113
|
+
timestamp: _aliases.timestamp_type,
|
|
114
|
+
precision: datetime.timedelta = datetime.timedelta(seconds=1),
|
|
115
|
+
) -> str:
|
|
116
|
+
"""Formats timedelta/datetime/seconds.
|
|
38
117
|
|
|
39
118
|
>>> format_time('1')
|
|
40
119
|
'0:00:01'
|
|
@@ -55,13 +134,15 @@ def format_time(timestamp, precision=datetime.timedelta(seconds=1)):
|
|
|
55
134
|
...
|
|
56
135
|
TypeError: Unknown type ...
|
|
57
136
|
|
|
58
|
-
|
|
137
|
+
"""
|
|
59
138
|
precision_seconds = precision.total_seconds()
|
|
60
139
|
|
|
61
|
-
if isinstance(timestamp,
|
|
140
|
+
if isinstance(timestamp, str):
|
|
141
|
+
timestamp = float(timestamp)
|
|
142
|
+
|
|
143
|
+
if isinstance(timestamp, (int, float)):
|
|
62
144
|
try:
|
|
63
|
-
|
|
64
|
-
timestamp = datetime.timedelta(seconds=castfunc(timestamp))
|
|
145
|
+
timestamp = datetime.timedelta(seconds=timestamp)
|
|
65
146
|
except OverflowError: # pragma: no cover
|
|
66
147
|
timestamp = None
|
|
67
148
|
|
|
@@ -82,11 +163,8 @@ def format_time(timestamp, precision=datetime.timedelta(seconds=1)):
|
|
|
82
163
|
seconds = seconds - (seconds % precision_seconds)
|
|
83
164
|
|
|
84
165
|
try: # pragma: no cover
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
else:
|
|
88
|
-
dt = datetime.datetime.utcfromtimestamp(seconds)
|
|
89
|
-
except ValueError: # pragma: no cover
|
|
166
|
+
dt = datetime.datetime.fromtimestamp(seconds)
|
|
167
|
+
except (ValueError, OSError): # pragma: no cover
|
|
90
168
|
dt = datetime.datetime.max
|
|
91
169
|
return str(dt)
|
|
92
170
|
elif isinstance(timestamp, datetime.date):
|
|
@@ -94,4 +172,279 @@ def format_time(timestamp, precision=datetime.timedelta(seconds=1)):
|
|
|
94
172
|
elif timestamp is None:
|
|
95
173
|
return '--:--:--'
|
|
96
174
|
else:
|
|
97
|
-
raise TypeError('Unknown type
|
|
175
|
+
raise TypeError(f'Unknown type {type(timestamp)}: {timestamp!r}')
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@typing.overload
|
|
179
|
+
def _to_iterable(
|
|
180
|
+
iterable: collections.abc.Callable[[], collections.abc.AsyncIterable[_T]]
|
|
181
|
+
| collections.abc.AsyncIterable[_T],
|
|
182
|
+
) -> collections.abc.AsyncIterable[_T]:
|
|
183
|
+
"""Async overload: async iterable or factory in, async iterable out."""
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@typing.overload
|
|
187
|
+
def _to_iterable(
|
|
188
|
+
iterable: collections.abc.Callable[[], collections.abc.Iterable[_T]]
|
|
189
|
+
| collections.abc.Iterable[_T],
|
|
190
|
+
) -> collections.abc.Iterable[_T]:
|
|
191
|
+
"""Sync overload: sync iterable or factory in, sync iterable out."""
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _to_iterable(
|
|
195
|
+
iterable: collections.abc.Iterable[_T]
|
|
196
|
+
| collections.abc.Callable[[], collections.abc.Iterable[_T]]
|
|
197
|
+
| collections.abc.AsyncIterable[_T]
|
|
198
|
+
| collections.abc.Callable[[], collections.abc.AsyncIterable[_T]],
|
|
199
|
+
) -> collections.abc.Iterable[_T] | collections.abc.AsyncIterable[_T]:
|
|
200
|
+
"""Return ``iterable``, calling it first if it is a zero-arg callable."""
|
|
201
|
+
if callable(iterable):
|
|
202
|
+
return iterable()
|
|
203
|
+
else:
|
|
204
|
+
return iterable
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def timeout_generator(
|
|
208
|
+
timeout: _aliases.delta_type,
|
|
209
|
+
interval: _aliases.delta_type = datetime.timedelta(seconds=1),
|
|
210
|
+
iterable: collections.abc.Iterable[_T]
|
|
211
|
+
| collections.abc.Callable[
|
|
212
|
+
[], collections.abc.Iterable[_T]
|
|
213
|
+
] = itertools.count, # type: ignore[assignment]
|
|
214
|
+
interval_multiplier: float = 1.0,
|
|
215
|
+
maximum_interval: _aliases.delta_type | None = None,
|
|
216
|
+
) -> collections.abc.Iterable[_T]:
|
|
217
|
+
"""
|
|
218
|
+
Generator that walks through the given iterable (a counter by default)
|
|
219
|
+
until the float_timeout is reached with a configurable float_interval
|
|
220
|
+
between items.
|
|
221
|
+
|
|
222
|
+
This can be used to limit the time spent on a slow operation. This can be
|
|
223
|
+
useful for testing slow APIs so you get a small sample of the data in a
|
|
224
|
+
reasonable amount of time.
|
|
225
|
+
|
|
226
|
+
>>> for i in timeout_generator(0.1, 0.06):
|
|
227
|
+
... # Put your slow code here
|
|
228
|
+
... print(i)
|
|
229
|
+
0
|
|
230
|
+
1
|
|
231
|
+
2
|
|
232
|
+
>>> timeout = datetime.timedelta(seconds=0.1)
|
|
233
|
+
>>> interval = datetime.timedelta(seconds=0.06)
|
|
234
|
+
>>> for i in timeout_generator(timeout, interval, itertools.count()):
|
|
235
|
+
... print(i)
|
|
236
|
+
0
|
|
237
|
+
1
|
|
238
|
+
2
|
|
239
|
+
>>> for i in timeout_generator(1, interval=0.1, iterable='ab'):
|
|
240
|
+
... print(i)
|
|
241
|
+
a
|
|
242
|
+
b
|
|
243
|
+
|
|
244
|
+
>>> timeout = datetime.timedelta(seconds=0.1)
|
|
245
|
+
>>> interval = datetime.timedelta(seconds=0.06)
|
|
246
|
+
>>> for i in timeout_generator(timeout, interval, interval_multiplier=2):
|
|
247
|
+
... print(i)
|
|
248
|
+
0
|
|
249
|
+
1
|
|
250
|
+
2
|
|
251
|
+
"""
|
|
252
|
+
float_interval: float = delta_to_seconds(interval)
|
|
253
|
+
float_maximum_interval: float | None = delta_to_seconds_or_none(
|
|
254
|
+
maximum_interval
|
|
255
|
+
)
|
|
256
|
+
iterable_ = _to_iterable(iterable)
|
|
257
|
+
|
|
258
|
+
end = delta_to_seconds(timeout) + time.perf_counter()
|
|
259
|
+
for item in iterable_:
|
|
260
|
+
yield item
|
|
261
|
+
|
|
262
|
+
if time.perf_counter() >= end:
|
|
263
|
+
break
|
|
264
|
+
|
|
265
|
+
time.sleep(float_interval)
|
|
266
|
+
|
|
267
|
+
float_interval *= interval_multiplier
|
|
268
|
+
if float_maximum_interval:
|
|
269
|
+
float_interval = min(float_interval, float_maximum_interval)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def aio_timeout_generator(
|
|
273
|
+
timeout: _aliases.delta_type, # noqa: ASYNC109
|
|
274
|
+
interval: _aliases.delta_type = datetime.timedelta(seconds=1),
|
|
275
|
+
iterable: collections.abc.AsyncIterable[_T]
|
|
276
|
+
| collections.abc.Callable[..., collections.abc.AsyncIterable[_T]]
|
|
277
|
+
| None = None,
|
|
278
|
+
interval_multiplier: float = 1.0,
|
|
279
|
+
maximum_interval: _aliases.delta_type | None = None,
|
|
280
|
+
) -> collections.abc.AsyncGenerator[_T, None]:
|
|
281
|
+
"""
|
|
282
|
+
Async generator that walks through the given async iterable (a counter by
|
|
283
|
+
default) until the float_timeout is reached with a configurable
|
|
284
|
+
float_interval between items.
|
|
285
|
+
|
|
286
|
+
The interval_exponent automatically increases the float_timeout with each
|
|
287
|
+
run. Note that if the float_interval is less than 1, 1/interval_exponent
|
|
288
|
+
will be used so the float_interval is always growing. To double the
|
|
289
|
+
float_interval with each run, specify 2.
|
|
290
|
+
|
|
291
|
+
Doctests and asyncio are not friends, so no examples. But this function is
|
|
292
|
+
effectively the same as the `timeout_generator` but it uses `async for`
|
|
293
|
+
instead.
|
|
294
|
+
"""
|
|
295
|
+
# Imported lazily so that importing `python_utils.time` for its
|
|
296
|
+
# synchronous helpers (e.g. ``format_time``) does not pull in ``asyncio``.
|
|
297
|
+
import asyncio
|
|
298
|
+
|
|
299
|
+
from python_utils import aio
|
|
300
|
+
|
|
301
|
+
if iterable is None:
|
|
302
|
+
iterable = typing.cast(
|
|
303
|
+
collections.abc.Callable[[], collections.abc.AsyncIterable[_T]],
|
|
304
|
+
aio.acount,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
float_interval: float = delta_to_seconds(interval)
|
|
308
|
+
float_maximum_interval: float | None = delta_to_seconds_or_none(
|
|
309
|
+
maximum_interval
|
|
310
|
+
)
|
|
311
|
+
iterable_ = _to_iterable(iterable)
|
|
312
|
+
|
|
313
|
+
end = delta_to_seconds(timeout) + time.perf_counter()
|
|
314
|
+
async for item in iterable_: # pragma: no branch
|
|
315
|
+
yield item
|
|
316
|
+
|
|
317
|
+
if time.perf_counter() >= end:
|
|
318
|
+
break
|
|
319
|
+
|
|
320
|
+
await asyncio.sleep(float_interval)
|
|
321
|
+
|
|
322
|
+
float_interval *= interval_multiplier
|
|
323
|
+
if float_maximum_interval: # pragma: no branch
|
|
324
|
+
float_interval = min(float_interval, float_maximum_interval)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
async def aio_generator_timeout_detector(
|
|
328
|
+
generator: collections.abc.AsyncGenerator[_T, None],
|
|
329
|
+
timeout: _aliases.delta_type | None = None, # noqa: ASYNC109
|
|
330
|
+
total_timeout: _aliases.delta_type | None = None,
|
|
331
|
+
on_timeout: collections.abc.Callable[
|
|
332
|
+
[
|
|
333
|
+
collections.abc.AsyncGenerator[_T, None],
|
|
334
|
+
_aliases.delta_type | None,
|
|
335
|
+
_aliases.delta_type | None,
|
|
336
|
+
BaseException,
|
|
337
|
+
],
|
|
338
|
+
typing.Any,
|
|
339
|
+
]
|
|
340
|
+
| None = exceptions.reraise,
|
|
341
|
+
**on_timeout_kwargs: collections.abc.Mapping[str, typing.Any],
|
|
342
|
+
) -> collections.abc.AsyncGenerator[_T, None]:
|
|
343
|
+
"""
|
|
344
|
+
This function is used to detect if an asyncio generator has not yielded
|
|
345
|
+
an element for a set amount of time.
|
|
346
|
+
|
|
347
|
+
The `on_timeout` argument is called with the `generator`, `timeout`,
|
|
348
|
+
`total_timeout`, `exception` and the extra `**kwargs` to this function as
|
|
349
|
+
arguments.
|
|
350
|
+
If `on_timeout` is not specified, the exception is reraised.
|
|
351
|
+
If `on_timeout` is `None`, the exception is silently ignored and the
|
|
352
|
+
generator will finish as normal.
|
|
353
|
+
"""
|
|
354
|
+
# Imported lazily so importing `python_utils.time` stays asyncio-free.
|
|
355
|
+
import asyncio
|
|
356
|
+
|
|
357
|
+
if total_timeout is None:
|
|
358
|
+
total_timeout_end = None
|
|
359
|
+
else:
|
|
360
|
+
total_timeout_end = time.perf_counter() + delta_to_seconds(
|
|
361
|
+
total_timeout
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
timeout_s = python_utils.delta_to_seconds_or_none(timeout)
|
|
365
|
+
|
|
366
|
+
while True:
|
|
367
|
+
try:
|
|
368
|
+
if total_timeout_end and time.perf_counter() >= total_timeout_end:
|
|
369
|
+
raise asyncio.TimeoutError( # noqa: TRY301
|
|
370
|
+
'Total timeout reached'
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if timeout_s:
|
|
374
|
+
yield await asyncio.wait_for(generator.__anext__(), timeout_s)
|
|
375
|
+
else:
|
|
376
|
+
yield await generator.__anext__()
|
|
377
|
+
|
|
378
|
+
except asyncio.TimeoutError as exception: # noqa: PERF203
|
|
379
|
+
if on_timeout is not None:
|
|
380
|
+
await on_timeout(
|
|
381
|
+
generator,
|
|
382
|
+
timeout,
|
|
383
|
+
total_timeout,
|
|
384
|
+
exception,
|
|
385
|
+
**on_timeout_kwargs,
|
|
386
|
+
)
|
|
387
|
+
break
|
|
388
|
+
|
|
389
|
+
except StopAsyncIteration:
|
|
390
|
+
break
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def aio_generator_timeout_detector_decorator(
|
|
394
|
+
timeout: _aliases.delta_type | None = None,
|
|
395
|
+
total_timeout: _aliases.delta_type | None = None,
|
|
396
|
+
on_timeout: collections.abc.Callable[
|
|
397
|
+
[
|
|
398
|
+
collections.abc.AsyncGenerator[typing.Any, None],
|
|
399
|
+
_aliases.delta_type | None,
|
|
400
|
+
_aliases.delta_type | None,
|
|
401
|
+
BaseException,
|
|
402
|
+
],
|
|
403
|
+
typing.Any,
|
|
404
|
+
]
|
|
405
|
+
| None = exceptions.reraise,
|
|
406
|
+
**on_timeout_kwargs: collections.abc.Mapping[str, typing.Any],
|
|
407
|
+
) -> collections.abc.Callable[
|
|
408
|
+
[collections.abc.Callable[_P, collections.abc.AsyncGenerator[_T, None]]],
|
|
409
|
+
collections.abc.Callable[_P, collections.abc.AsyncGenerator[_T, None]],
|
|
410
|
+
]:
|
|
411
|
+
"""Wrap a generator function with ``aio_generator_timeout_detector``.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
timeout: Per-item timeout; if a single yield takes longer,
|
|
415
|
+
``on_timeout`` fires. ``None`` disables the per-item check.
|
|
416
|
+
total_timeout: Overall timeout across the whole generator.
|
|
417
|
+
on_timeout: Callback invoked on a timeout; defaults to re-raising.
|
|
418
|
+
**on_timeout_kwargs: Extra keyword arguments passed to ``on_timeout``.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
A decorator that wraps an async-generator function so every call is
|
|
422
|
+
guarded against stalls.
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
def _timeout_detector_decorator(
|
|
426
|
+
generator: collections.abc.Callable[
|
|
427
|
+
_P, collections.abc.AsyncGenerator[_T, None]
|
|
428
|
+
],
|
|
429
|
+
) -> collections.abc.Callable[
|
|
430
|
+
_P, collections.abc.AsyncGenerator[_T, None]
|
|
431
|
+
]:
|
|
432
|
+
"""Wrap ``generator`` so each call is timeout-guarded."""
|
|
433
|
+
|
|
434
|
+
@functools.wraps(generator)
|
|
435
|
+
def wrapper(
|
|
436
|
+
*args: _P.args,
|
|
437
|
+
**kwargs: _P.kwargs,
|
|
438
|
+
) -> collections.abc.AsyncGenerator[_T, None]:
|
|
439
|
+
"""Forward the call to ``aio_generator_timeout_detector``."""
|
|
440
|
+
return aio_generator_timeout_detector(
|
|
441
|
+
generator(*args, **kwargs),
|
|
442
|
+
timeout,
|
|
443
|
+
total_timeout,
|
|
444
|
+
on_timeout,
|
|
445
|
+
**on_timeout_kwargs,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return wrapper
|
|
449
|
+
|
|
450
|
+
return _timeout_detector_decorator
|
python_utils/types.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides type definitions and utility functions for type hinting.
|
|
3
|
+
|
|
4
|
+
It includes:
|
|
5
|
+
- Shorthand for commonly used types such as Optional and Union.
|
|
6
|
+
- Type aliases for various data structures and common types.
|
|
7
|
+
- Importing all types from the `typing` and `typing_extensions` modules.
|
|
8
|
+
- Importing specific types from the `types` module.
|
|
9
|
+
|
|
10
|
+
The module also configures Pyright to ignore wildcard import warnings.
|
|
11
|
+
"""
|
|
12
|
+
# pyright: reportWildcardImportFromLibrary=false
|
|
13
|
+
# ruff: noqa: F405
|
|
14
|
+
|
|
15
|
+
# Kept as public module attributes for backwards compatibility:
|
|
16
|
+
# `from python_utils.types import datetime, decimal` worked in 3.x.
|
|
17
|
+
import datetime as datetime
|
|
18
|
+
import decimal as decimal
|
|
19
|
+
from re import Match, Pattern
|
|
20
|
+
from types import * # pragma: no cover # noqa: F403
|
|
21
|
+
from typing import * # pragma: no cover # noqa: F403
|
|
22
|
+
|
|
23
|
+
# `import *` does not import these on all Python versions. `O` / `U` are kept
|
|
24
|
+
# as backwards-compatible shorthands; new code should prefer `X | None`.
|
|
25
|
+
from typing import (
|
|
26
|
+
IO,
|
|
27
|
+
BinaryIO,
|
|
28
|
+
Optional as O, # noqa: N817
|
|
29
|
+
TextIO,
|
|
30
|
+
Union as U, # noqa: N817
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
from typing_extensions import * # type: ignore[no-redef,assignment] # noqa: F403
|
|
34
|
+
|
|
35
|
+
# Lightweight aliases live in a stdlib-only module so importers don't pull in
|
|
36
|
+
# typing_extensions; re-exported here to keep the public surface unchanged.
|
|
37
|
+
# The redundant `X as X` form marks these as intentional re-exports so strict
|
|
38
|
+
# type checkers and ruff see `python_utils.types.X` without adding them to
|
|
39
|
+
# `__all__` (which would change `from python_utils.types import *`).
|
|
40
|
+
from ._aliases import (
|
|
41
|
+
DecimalNumber as DecimalNumber,
|
|
42
|
+
ExceptionType as ExceptionType,
|
|
43
|
+
ExceptionsType as ExceptionsType,
|
|
44
|
+
Number as Number,
|
|
45
|
+
OptionalScope as OptionalScope,
|
|
46
|
+
Scope as Scope,
|
|
47
|
+
StringTypes as StringTypes,
|
|
48
|
+
delta_type as delta_type,
|
|
49
|
+
timestamp_type as timestamp_type,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
'IO',
|
|
54
|
+
'TYPE_CHECKING',
|
|
55
|
+
# ABCs (from collections.abc).
|
|
56
|
+
'AbstractSet',
|
|
57
|
+
# The types from the typing module.
|
|
58
|
+
# Super-special typing primitives.
|
|
59
|
+
'Annotated',
|
|
60
|
+
'Any',
|
|
61
|
+
# One-off things.
|
|
62
|
+
'AnyStr',
|
|
63
|
+
'AsyncContextManager',
|
|
64
|
+
'AsyncGenerator',
|
|
65
|
+
'AsyncGeneratorType',
|
|
66
|
+
'AsyncIterable',
|
|
67
|
+
'AsyncIterator',
|
|
68
|
+
'Awaitable',
|
|
69
|
+
# Other concrete types.
|
|
70
|
+
'BinaryIO',
|
|
71
|
+
'BuiltinFunctionType',
|
|
72
|
+
'BuiltinMethodType',
|
|
73
|
+
'ByteString',
|
|
74
|
+
'Callable',
|
|
75
|
+
# Concrete collection types.
|
|
76
|
+
'ChainMap',
|
|
77
|
+
'ClassMethodDescriptorType',
|
|
78
|
+
'ClassVar',
|
|
79
|
+
'CodeType',
|
|
80
|
+
'Collection',
|
|
81
|
+
'Concatenate',
|
|
82
|
+
'Container',
|
|
83
|
+
'ContextManager',
|
|
84
|
+
'Coroutine',
|
|
85
|
+
'CoroutineType',
|
|
86
|
+
'Counter',
|
|
87
|
+
'DecimalNumber',
|
|
88
|
+
'DefaultDict',
|
|
89
|
+
'Deque',
|
|
90
|
+
'Dict',
|
|
91
|
+
'DynamicClassAttribute',
|
|
92
|
+
'Final',
|
|
93
|
+
'ForwardRef',
|
|
94
|
+
'FrameType',
|
|
95
|
+
'FrozenSet',
|
|
96
|
+
# Types from the `types` module.
|
|
97
|
+
'FunctionType',
|
|
98
|
+
'Generator',
|
|
99
|
+
'GeneratorType',
|
|
100
|
+
'Generic',
|
|
101
|
+
'GetSetDescriptorType',
|
|
102
|
+
'Hashable',
|
|
103
|
+
'ItemsView',
|
|
104
|
+
'Iterable',
|
|
105
|
+
'Iterator',
|
|
106
|
+
'KeysView',
|
|
107
|
+
'LambdaType',
|
|
108
|
+
'List',
|
|
109
|
+
'Literal',
|
|
110
|
+
'Mapping',
|
|
111
|
+
'MappingProxyType',
|
|
112
|
+
'MappingView',
|
|
113
|
+
'Match',
|
|
114
|
+
'MemberDescriptorType',
|
|
115
|
+
'MethodDescriptorType',
|
|
116
|
+
'MethodType',
|
|
117
|
+
'MethodWrapperType',
|
|
118
|
+
'ModuleType',
|
|
119
|
+
'MutableMapping',
|
|
120
|
+
'MutableSequence',
|
|
121
|
+
'MutableSet',
|
|
122
|
+
'NamedTuple', # Not really a type.
|
|
123
|
+
'NewType',
|
|
124
|
+
'NoReturn',
|
|
125
|
+
'Number',
|
|
126
|
+
'O',
|
|
127
|
+
'Optional',
|
|
128
|
+
'OptionalScope',
|
|
129
|
+
'OrderedDict',
|
|
130
|
+
'ParamSpec',
|
|
131
|
+
'ParamSpecArgs',
|
|
132
|
+
'ParamSpecKwargs',
|
|
133
|
+
'Pattern',
|
|
134
|
+
'Protocol',
|
|
135
|
+
# Structural checks, a.k.a. protocols.
|
|
136
|
+
'Reversible',
|
|
137
|
+
'Sequence',
|
|
138
|
+
'Set',
|
|
139
|
+
'SimpleNamespace',
|
|
140
|
+
'Sized',
|
|
141
|
+
'SupportsAbs',
|
|
142
|
+
'SupportsBytes',
|
|
143
|
+
'SupportsComplex',
|
|
144
|
+
'SupportsFloat',
|
|
145
|
+
'SupportsIndex',
|
|
146
|
+
'SupportsIndex',
|
|
147
|
+
'SupportsInt',
|
|
148
|
+
'SupportsRound',
|
|
149
|
+
'Text',
|
|
150
|
+
'TextIO',
|
|
151
|
+
'TracebackType',
|
|
152
|
+
'TracebackType',
|
|
153
|
+
'Tuple',
|
|
154
|
+
'Type',
|
|
155
|
+
'TypeAlias',
|
|
156
|
+
'TypeGuard',
|
|
157
|
+
'TypeVar',
|
|
158
|
+
'TypedDict', # Not really a type.
|
|
159
|
+
'U',
|
|
160
|
+
'Union',
|
|
161
|
+
'ValuesView',
|
|
162
|
+
'WrapperDescriptorType',
|
|
163
|
+
'cast',
|
|
164
|
+
'coroutine',
|
|
165
|
+
'delta_type',
|
|
166
|
+
'final',
|
|
167
|
+
'get_args',
|
|
168
|
+
'get_origin',
|
|
169
|
+
'get_type_hints',
|
|
170
|
+
'is_typeddict',
|
|
171
|
+
'new_class',
|
|
172
|
+
'no_type_check',
|
|
173
|
+
'no_type_check_decorator',
|
|
174
|
+
'overload',
|
|
175
|
+
'prepare_class',
|
|
176
|
+
'resolve_bases',
|
|
177
|
+
'runtime_checkable',
|
|
178
|
+
'timestamp_type',
|
|
179
|
+
]
|