dycw-utilities 0.146.2__py3-none-any.whl → 0.178.1__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 dycw-utilities might be problematic. Click here for more details.

Files changed (89) hide show
  1. dycw_utilities-0.178.1.dist-info/METADATA +34 -0
  2. dycw_utilities-0.178.1.dist-info/RECORD +105 -0
  3. dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
  4. {dycw_utilities-0.146.2.dist-info → dycw_utilities-0.178.1.dist-info}/entry_points.txt +1 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +10 -7
  7. utilities/asyncio.py +129 -50
  8. utilities/atomicwrites.py +1 -1
  9. utilities/atools.py +64 -4
  10. utilities/cachetools.py +9 -6
  11. utilities/click.py +144 -49
  12. utilities/concurrent.py +1 -1
  13. utilities/contextlib.py +4 -2
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +15 -28
  17. utilities/docker.py +387 -0
  18. utilities/enum.py +2 -2
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +8 -3
  21. utilities/fpdf2.py +2 -2
  22. utilities/functions.py +20 -297
  23. utilities/git.py +19 -0
  24. utilities/grp.py +28 -0
  25. utilities/hypothesis.py +361 -79
  26. utilities/importlib.py +17 -1
  27. utilities/inflect.py +1 -1
  28. utilities/iterables.py +33 -58
  29. utilities/jinja2.py +148 -0
  30. utilities/json.py +1 -1
  31. utilities/libcst.py +7 -7
  32. utilities/logging.py +131 -93
  33. utilities/math.py +8 -4
  34. utilities/more_itertools.py +4 -6
  35. utilities/operator.py +1 -1
  36. utilities/orjson.py +86 -34
  37. utilities/os.py +49 -2
  38. utilities/packaging.py +115 -0
  39. utilities/parse.py +2 -2
  40. utilities/pathlib.py +66 -34
  41. utilities/permissions.py +298 -0
  42. utilities/platform.py +5 -4
  43. utilities/polars.py +934 -420
  44. utilities/polars_ols.py +1 -1
  45. utilities/postgres.py +317 -153
  46. utilities/pottery.py +10 -86
  47. utilities/pqdm.py +3 -3
  48. utilities/pwd.py +28 -0
  49. utilities/pydantic.py +4 -51
  50. utilities/pydantic_settings.py +240 -0
  51. utilities/pydantic_settings_sops.py +76 -0
  52. utilities/pyinstrument.py +5 -5
  53. utilities/pytest.py +100 -126
  54. utilities/pytest_plugins/pytest_randomly.py +1 -1
  55. utilities/pytest_plugins/pytest_regressions.py +7 -3
  56. utilities/pytest_regressions.py +27 -8
  57. utilities/random.py +11 -6
  58. utilities/re.py +1 -1
  59. utilities/redis.py +101 -64
  60. utilities/sentinel.py +10 -0
  61. utilities/shelve.py +4 -1
  62. utilities/shutil.py +25 -0
  63. utilities/slack_sdk.py +9 -4
  64. utilities/sqlalchemy.py +422 -352
  65. utilities/sqlalchemy_polars.py +28 -52
  66. utilities/string.py +1 -1
  67. utilities/subprocess.py +1977 -0
  68. utilities/tempfile.py +112 -4
  69. utilities/testbook.py +50 -0
  70. utilities/text.py +174 -42
  71. utilities/throttle.py +158 -0
  72. utilities/timer.py +2 -2
  73. utilities/traceback.py +59 -38
  74. utilities/types.py +68 -22
  75. utilities/typing.py +479 -19
  76. utilities/uuid.py +42 -5
  77. utilities/version.py +27 -26
  78. utilities/whenever.py +663 -178
  79. utilities/zoneinfo.py +80 -22
  80. dycw_utilities-0.146.2.dist-info/METADATA +0 -41
  81. dycw_utilities-0.146.2.dist-info/RECORD +0 -99
  82. dycw_utilities-0.146.2.dist-info/WHEEL +0 -4
  83. dycw_utilities-0.146.2.dist-info/licenses/LICENSE +0 -21
  84. utilities/aiolimiter.py +0 -25
  85. utilities/eventkit.py +0 -388
  86. utilities/period.py +0 -237
  87. utilities/python_dotenv.py +0 -101
  88. utilities/streamlit.py +0 -105
  89. utilities/typed_settings.py +0 -144
utilities/throttle.py ADDED
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from dataclasses import dataclass
5
+ from functools import partial, wraps
6
+ from inspect import iscoroutinefunction
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING, Any, NoReturn, assert_never, cast, override
9
+
10
+ from whenever import ZonedDateTime
11
+
12
+ from utilities.atomicwrites import writer
13
+ from utilities.os import get_env_var
14
+ from utilities.pathlib import to_path
15
+ from utilities.types import MaybeCallablePathLike, MaybeCoro
16
+ from utilities.whenever import SECOND, get_now_local
17
+
18
+ if TYPE_CHECKING:
19
+ from utilities.types import Coro, Delta
20
+
21
+
22
+ def throttle[F: Callable[..., MaybeCoro[None]]](
23
+ *,
24
+ on_try: bool = False,
25
+ delta: Delta = SECOND,
26
+ path: MaybeCallablePathLike = Path.cwd,
27
+ raiser: Callable[[], NoReturn] | None = None,
28
+ ) -> Callable[[F], F]:
29
+ """Throttle a function. On success by default, on try otherwise."""
30
+ return cast(
31
+ "Any",
32
+ partial(_throttle_inner, on_try=on_try, delta=delta, path=path, raiser=raiser),
33
+ )
34
+
35
+
36
+ def _throttle_inner[F: Callable[..., MaybeCoro[None]]](
37
+ func: F,
38
+ /,
39
+ *,
40
+ on_try: bool = False,
41
+ delta: Delta = SECOND,
42
+ path: MaybeCallablePathLike = Path.cwd,
43
+ raiser: Callable[[], NoReturn] | None = None,
44
+ ) -> F:
45
+ match bool(iscoroutinefunction(func)), on_try:
46
+ case False, False:
47
+
48
+ @wraps(func)
49
+ def throttle_sync_on_pass(*args: Any, **kwargs: Any) -> None:
50
+ path_use = to_path(path)
51
+ if _is_throttle(path=path_use, delta=delta):
52
+ _try_raise(raiser=raiser)
53
+ else:
54
+ cast("Callable[..., None]", func)(*args, **kwargs)
55
+ _write_throttle(path=path_use)
56
+
57
+ return cast("Any", throttle_sync_on_pass)
58
+
59
+ case False, True:
60
+
61
+ @wraps(func)
62
+ def throttle_sync_on_try(*args: Any, **kwargs: Any) -> None:
63
+ path_use = to_path(path)
64
+ if _is_throttle(path=path_use, delta=delta):
65
+ _try_raise(raiser=raiser)
66
+ else:
67
+ _write_throttle(path=path_use)
68
+ cast("Callable[..., None]", func)(*args, **kwargs)
69
+
70
+ return cast("Any", throttle_sync_on_try)
71
+
72
+ case True, False:
73
+
74
+ @wraps(func)
75
+ async def throttle_async_on_pass(*args: Any, **kwargs: Any) -> None:
76
+ path_use = to_path(path)
77
+ if _is_throttle(path=path_use, delta=delta):
78
+ _try_raise(raiser=raiser)
79
+ else:
80
+ await cast("Callable[..., Coro[None]]", func)(*args, **kwargs)
81
+ _write_throttle(path=path_use)
82
+
83
+ return cast("Any", throttle_async_on_pass)
84
+
85
+ case True, True:
86
+
87
+ @wraps(func)
88
+ async def throttle_async_on_try(*args: Any, **kwargs: Any) -> None:
89
+ path_use = to_path(path)
90
+ if _is_throttle(path=path_use, delta=delta):
91
+ _try_raise(raiser=raiser)
92
+ else:
93
+ _write_throttle(path=path_use)
94
+ await cast("Callable[..., Coro[None]]", func)(*args, **kwargs)
95
+
96
+ return cast("Any", throttle_async_on_try)
97
+
98
+ case never:
99
+ assert_never(never)
100
+
101
+
102
+ def _is_throttle(
103
+ *, path: MaybeCallablePathLike = Path.cwd, delta: Delta = SECOND
104
+ ) -> bool:
105
+ if get_env_var("THROTTLE", nullable=True):
106
+ return False
107
+ path = to_path(path)
108
+ if path.is_file():
109
+ text = path.read_text()
110
+ if text == "":
111
+ path.unlink(missing_ok=True)
112
+ return False
113
+ try:
114
+ last = ZonedDateTime.parse_iso(text)
115
+ except ValueError:
116
+ raise _ThrottleParseZonedDateTimeError(path=path, text=text) from None
117
+ threshold = get_now_local() - delta
118
+ return threshold <= last
119
+ if not path.exists():
120
+ return False
121
+ raise _ThrottleMarkerFileError(path=path)
122
+
123
+
124
+ def _try_raise(*, raiser: Callable[[], NoReturn] | None = None) -> None:
125
+ if raiser is not None:
126
+ raiser()
127
+
128
+
129
+ def _write_throttle(*, path: MaybeCallablePathLike = Path.cwd) -> None:
130
+ path = to_path(path)
131
+ with writer(path, overwrite=True) as temp:
132
+ _ = temp.write_text(get_now_local().format_iso())
133
+
134
+
135
+ @dataclass(kw_only=True, slots=True)
136
+ class ThrottleError(Exception): ...
137
+
138
+
139
+ @dataclass(kw_only=True, slots=True)
140
+ class _ThrottleParseZonedDateTimeError(ThrottleError):
141
+ path: Path
142
+ text: str
143
+
144
+ @override
145
+ def __str__(self) -> str:
146
+ return f"Unable to parse the contents {self.text!r} of {str(self.path)!r} to a ZonedDateTime"
147
+
148
+
149
+ @dataclass(kw_only=True, slots=True)
150
+ class _ThrottleMarkerFileError(ThrottleError):
151
+ path: Path
152
+
153
+ @override
154
+ def __str__(self) -> str:
155
+ return f"Invalid marker file {str(self.path)!r}"
156
+
157
+
158
+ __all__ = ["ThrottleError", "throttle"]
utilities/timer.py CHANGED
@@ -56,11 +56,11 @@ class Timer:
56
56
 
57
57
  @override
58
58
  def __repr__(self) -> str:
59
- return self.timedelta.format_common_iso()
59
+ return self.timedelta.format_iso()
60
60
 
61
61
  @override
62
62
  def __str__(self) -> str:
63
- return self.timedelta.format_common_iso()
63
+ return self.timedelta.format_iso()
64
64
 
65
65
  # comparison
66
66
 
utilities/traceback.py CHANGED
@@ -6,16 +6,17 @@ from dataclasses import dataclass
6
6
  from functools import partial
7
7
  from getpass import getuser
8
8
  from itertools import repeat
9
+ from os import getpid
9
10
  from pathlib import Path
10
11
  from socket import gethostname
12
+ from sys import stderr
11
13
  from traceback import TracebackException
12
14
  from typing import TYPE_CHECKING, override
13
15
 
14
16
  from utilities.atomicwrites import writer
15
17
  from utilities.errors import repr_error
16
- from utilities.functions import to_bool
17
18
  from utilities.iterables import OneEmptyError, one
18
- from utilities.pathlib import get_path, module_path
19
+ from utilities.pathlib import module_path, to_path
19
20
  from utilities.reprlib import (
20
21
  RICH_EXPAND_ALL,
21
22
  RICH_INDENT_SIZE,
@@ -25,24 +26,26 @@ from utilities.reprlib import (
25
26
  RICH_MAX_WIDTH,
26
27
  yield_mapping_repr,
27
28
  )
28
- from utilities.version import get_version
29
+ from utilities.text import to_bool
30
+ from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
31
+ from utilities.version import to_version
29
32
  from utilities.whenever import (
30
33
  format_compact,
31
34
  get_now,
32
35
  get_now_local,
33
- to_local_plain,
34
36
  to_zoned_date_time,
35
37
  )
36
38
 
37
39
  if TYPE_CHECKING:
38
- from collections.abc import Callable, Iterator, Sequence
40
+ from collections.abc import Callable, Iterator
39
41
  from traceback import FrameSummary
40
42
  from types import TracebackType
41
43
 
42
44
  from utilities.types import (
43
- MaybeCallableBool,
45
+ Delta,
46
+ MaybeCallableBoolLike,
44
47
  MaybeCallablePathLike,
45
- MaybeCallableZonedDateTime,
48
+ MaybeCallableZonedDateTimeLike,
46
49
  PathLike,
47
50
  )
48
51
  from utilities.version import MaybeCallableVersionLike
@@ -51,15 +54,12 @@ if TYPE_CHECKING:
51
54
  ##
52
55
 
53
56
 
54
- _START = get_now()
55
-
56
-
57
57
  def format_exception_stack(
58
58
  error: BaseException,
59
59
  /,
60
60
  *,
61
61
  header: bool = False,
62
- start: MaybeCallableZonedDateTime | None = _START,
62
+ start: MaybeCallableZonedDateTimeLike = get_now,
63
63
  version: MaybeCallableVersionLike | None = None,
64
64
  capture_locals: bool = False,
65
65
  max_width: int = RICH_MAX_WIDTH,
@@ -70,7 +70,7 @@ def format_exception_stack(
70
70
  expand_all: bool = RICH_EXPAND_ALL,
71
71
  ) -> str:
72
72
  """Format an exception stack."""
73
- lines: Sequence[str] = []
73
+ lines: list[str] = []
74
74
  if header:
75
75
  lines.extend(_yield_header_lines(start=start, version=version))
76
76
  lines.extend(
@@ -90,22 +90,20 @@ def format_exception_stack(
90
90
 
91
91
  def _yield_header_lines(
92
92
  *,
93
- start: MaybeCallableZonedDateTime | None = _START,
93
+ start: MaybeCallableZonedDateTimeLike = get_now,
94
94
  version: MaybeCallableVersionLike | None = None,
95
95
  ) -> Iterator[str]:
96
96
  """Yield the header lines."""
97
97
  now = get_now_local()
98
- start_use = to_zoned_date_time(date_time=start)
99
- yield f"Date/time | {format_compact(now)}"
100
- start_str = "" if start_use is None else format_compact(start_use)
101
- yield f"Started | {start_str}"
102
- delta = None if start_use is None else (now - start_use)
103
- delta_str = "" if delta is None else delta.format_common_iso()
104
- yield f"Duration | {delta_str}"
105
- yield f"User | {getuser()}"
106
- yield f"Host | {gethostname()}"
107
- version_use = "" if version is None else get_version(version=version)
108
- yield f"Version | {version_use}"
98
+ yield f"Date/time | {format_compact(now)}"
99
+ start_use = to_zoned_date_time(start).to_tz(LOCAL_TIME_ZONE_NAME)
100
+ yield f"Started | {format_compact(start_use)}"
101
+ yield f"Duration | {(now - start_use).format_iso()}"
102
+ yield f"User | {getuser()}"
103
+ yield f"Host | {gethostname()}"
104
+ yield f"Process ID | {getpid()}"
105
+ version_use = "" if version is None else to_version(version)
106
+ yield f"Version | {version_use}"
109
107
  yield ""
110
108
 
111
109
 
@@ -198,9 +196,10 @@ def _trim_path(path: PathLike, pattern: str, /) -> Path | None:
198
196
 
199
197
  def make_except_hook(
200
198
  *,
201
- start: MaybeCallableZonedDateTime | None = _START,
199
+ start: MaybeCallableZonedDateTimeLike = get_now,
202
200
  version: MaybeCallableVersionLike | None = None,
203
201
  path: MaybeCallablePathLike | None = None,
202
+ path_max_age: Delta | None = None,
204
203
  max_width: int = RICH_MAX_WIDTH,
205
204
  indent_size: int = RICH_INDENT_SIZE,
206
205
  max_length: int | None = RICH_MAX_LENGTH,
@@ -208,7 +207,7 @@ def make_except_hook(
208
207
  max_depth: int | None = RICH_MAX_DEPTH,
209
208
  expand_all: bool = RICH_EXPAND_ALL,
210
209
  slack_url: str | None = None,
211
- pudb: MaybeCallableBool = False,
210
+ pudb: MaybeCallableBoolLike = False,
212
211
  ) -> Callable[
213
212
  [type[BaseException] | None, BaseException | None, TracebackType | None], None
214
213
  ]:
@@ -218,6 +217,7 @@ def make_except_hook(
218
217
  start=start,
219
218
  version=version,
220
219
  path=path,
220
+ path_max_age=path_max_age,
221
221
  max_width=max_width,
222
222
  indent_size=indent_size,
223
223
  max_length=max_length,
@@ -235,9 +235,10 @@ def _make_except_hook_inner(
235
235
  traceback: TracebackType | None,
236
236
  /,
237
237
  *,
238
- start: MaybeCallableZonedDateTime | None = _START,
238
+ start: MaybeCallableZonedDateTimeLike = get_now,
239
239
  version: MaybeCallableVersionLike | None = None,
240
240
  path: MaybeCallablePathLike | None = None,
241
+ path_max_age: Delta | None = None,
241
242
  max_width: int = RICH_MAX_WIDTH,
242
243
  indent_size: int = RICH_INDENT_SIZE,
243
244
  max_length: int | None = RICH_MAX_LENGTH,
@@ -245,7 +246,7 @@ def _make_except_hook_inner(
245
246
  max_depth: int | None = RICH_MAX_DEPTH,
246
247
  expand_all: bool = RICH_EXPAND_ALL,
247
248
  slack_url: str | None = None,
248
- pudb: MaybeCallableBool = False,
249
+ pudb: MaybeCallableBoolLike = False,
249
250
  ) -> None:
250
251
  """Exception hook to log the traceback."""
251
252
  _ = (exc_type, traceback)
@@ -254,11 +255,10 @@ def _make_except_hook_inner(
254
255
  slim = format_exception_stack(exc_val, header=True, start=start, version=version)
255
256
  _ = sys.stderr.write(f"{slim}\n") # don't 'from sys import stderr'
256
257
  if path is not None:
257
- path = (
258
- get_path(path=path)
259
- .joinpath(format_compact(to_local_plain(get_now())))
260
- .with_suffix(".txt")
261
- )
258
+ path = to_path(path)
259
+ path_log = path.joinpath(
260
+ format_compact(get_now_local(), path=True)
261
+ ).with_suffix(".txt")
262
262
  full = format_exception_stack(
263
263
  exc_val,
264
264
  header=True,
@@ -272,18 +272,39 @@ def _make_except_hook_inner(
272
272
  max_depth=max_depth,
273
273
  expand_all=expand_all,
274
274
  )
275
- with writer(path, overwrite=True) as temp:
275
+ with writer(path_log, overwrite=True) as temp:
276
276
  _ = temp.write_text(full)
277
+ if path_max_age is not None:
278
+ _make_except_hook_purge(path, path_max_age)
277
279
  if slack_url is not None: # pragma: no cover
278
- from utilities.slack_sdk import send_to_slack
280
+ from utilities.slack_sdk import SendToSlackError, send_to_slack
279
281
 
280
- send_to_slack(slack_url, f"```{slim}```")
281
- if to_bool(bool_=pudb): # pragma: no cover
282
- from pudb import post_mortem
282
+ try:
283
+ send_to_slack(slack_url, f"```{slim}```")
284
+ except SendToSlackError as error:
285
+ _ = stderr.write(f"{error}\n")
286
+ if to_bool(pudb): # pragma: no cover
287
+ from pudb import post_mortem # pyright: ignore[reportMissingImports]
283
288
 
284
289
  post_mortem(tb=traceback, e_type=exc_type, e_value=exc_val)
285
290
 
286
291
 
292
+ def _make_except_hook_purge(path: PathLike, max_age: Delta, /) -> None:
293
+ threshold = get_now_local() - max_age
294
+ paths: set[Path] = set()
295
+ for p in Path(path).iterdir():
296
+ if p.is_file():
297
+ try:
298
+ date_time = to_zoned_date_time(p.stem)
299
+ except ValueError:
300
+ pass
301
+ else:
302
+ if date_time <= threshold:
303
+ paths.add(p)
304
+ for p in paths:
305
+ p.unlink(missing_ok=True)
306
+
307
+
287
308
  @dataclass(kw_only=True, slots=True)
288
309
  class MakeExceptHookError(Exception):
289
310
  @override
utilities/types.py CHANGED
@@ -1,8 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
- from asyncio import Event
5
- from collections.abc import Callable, Coroutine, Hashable, Iterable, Mapping, Sequence
4
+ from collections.abc import Callable, Collection, Coroutine, Iterable, Mapping
6
5
  from enum import Enum
7
6
  from ipaddress import IPv4Address, IPv6Address
8
7
  from logging import Logger
@@ -16,9 +15,11 @@ from typing import (
16
15
  Literal,
17
16
  Protocol,
18
17
  TypeVar,
18
+ get_args,
19
19
  overload,
20
20
  runtime_checkable,
21
21
  )
22
+ from uuid import UUID
22
23
  from zoneinfo import ZoneInfo
23
24
 
24
25
  from whenever import (
@@ -62,22 +63,26 @@ type OpenMode = Literal[
62
63
  "a+b",
63
64
  ]
64
65
  type MaybeCallable[T] = T | Callable[[], T]
65
- type MaybeCallableBool = MaybeCallable[bool]
66
66
  type MaybeStr[T] = T | str
67
67
  type MaybeType[T] = T | type[T]
68
68
  type StrMapping = Mapping[str, Any]
69
69
  type StrStrMapping = Mapping[str, str]
70
- type TypeLike[T] = type[T] | tuple[type[T], ...]
71
70
  type TupleOrStrMapping = tuple[Any, ...] | StrMapping
71
+ type TypeLike[T] = type[T] | tuple[type[T], ...]
72
+ # basic - derived
73
+ type MaybeCallableBoolLike = MaybeCallable[BoolLike]
74
+ type BoolLike = MaybeStr[bool]
72
75
 
73
76
 
74
77
  # asyncio
75
78
  type Coro[T] = Coroutine[Any, Any, T]
76
- type MaybeCallableEvent = MaybeCallable[Event]
77
79
  type MaybeCoro[T] = T | Coro[T]
78
80
 
79
81
 
80
82
  # collections.abc
83
+ type SupportsFloatOrIndex = SupportsFloat | SupportsIndex
84
+
85
+
81
86
  @runtime_checkable
82
87
  class SupportsKeysAndGetItem(Protocol[_T, _T_co]):
83
88
  def keys(self) -> Iterable[_T]: ... # pragma: no cover
@@ -104,23 +109,33 @@ type MonthInt = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
104
109
  type EnumLike[E: Enum] = MaybeStr[E]
105
110
 
106
111
 
112
+ # errors
113
+ type ExceptionTypeLike[T: Exception] = type[T] | tuple[type[T], ...]
114
+
115
+
107
116
  # ipaddress
108
117
  IPv4AddressLike = MaybeStr[IPv4Address]
109
118
  IPv6AddressLike = MaybeStr[IPv6Address]
110
119
 
111
120
 
112
121
  # iterables
122
+ type MaybeCollection[T] = T | Collection[T]
113
123
  type MaybeIterable[T] = T | Iterable[T]
114
- type IterableHashable[T: Hashable] = tuple[T, ...] | frozenset[T]
115
124
  type MaybeList[T] = T | list[T]
116
- type MaybeListStr = MaybeList[str]
117
- type MaybeSequence[T] = T | Sequence[T]
118
- type MaybeIterableHashable[T: Hashable] = T | IterableHashable[T]
125
+ type MaybeSet[T] = T | set[T] | frozenset[T]
126
+ type SequenceLT[T] = list[T] | tuple[T, ...]
127
+ # iterables - dervied
128
+ type MaybeSequence[T] = T | SequenceLT[T]
129
+ type SequenceStr = SequenceLT[str]
130
+ type CollectionStr = dict[str, Any] | frozenset[str] | set[str] | SequenceStr
131
+ # iterables - maybe str
132
+ type MaybeCollectionStr = str | CollectionStr
133
+ type MaybeSequenceStr = str | SequenceStr
119
134
 
120
135
 
121
136
  # logging
122
137
  type LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
123
- type LoggerOrName = MaybeStr[Logger]
138
+ type LoggerLike = MaybeStr[Logger]
124
139
 
125
140
 
126
141
  # math
@@ -204,8 +219,8 @@ type SerializeObjectExtra = Mapping[Any, Callable[[Any], str]]
204
219
 
205
220
 
206
221
  # pathlib
207
- type MaybeCallablePathLike = MaybeCallable[PathLike]
208
222
  type PathLike = MaybeStr[Path]
223
+ type MaybeCallablePathLike = MaybeCallable[PathLike]
209
224
 
210
225
 
211
226
  # random
@@ -216,11 +231,24 @@ type Seed = int | float | str | bytes | bytearray | Random
216
231
  type PatternLike = MaybeStr[Pattern[str]]
217
232
 
218
233
 
234
+ # retry
235
+ type Retry = tuple[int, Delta | None]
236
+
237
+
238
+ # text
239
+ type MaybeCallableStr = MaybeCallable[str]
240
+
241
+
219
242
  # traceback
220
243
  type ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
221
244
  type OptExcInfo = ExcInfo | tuple[None, None, None]
222
245
 
223
246
 
247
+ # uuid
248
+ type UUIDLike = MaybeStr[UUID]
249
+ type MaybeCallableUUIDLike = MaybeCallable[UUIDLike | Seed]
250
+
251
+
224
252
  # whenever
225
253
  type DateDeltaLike = MaybeStr[DateDelta]
226
254
  type DateLike = MaybeStr[Date]
@@ -230,8 +258,9 @@ type DateTimeRoundMode = Literal[
230
258
  "ceil", "floor", "half_ceil", "half_floor", "half_even"
231
259
  ]
232
260
  type Delta = DateDelta | TimeDelta | DateTimeDelta
233
- type MaybeCallableDate = MaybeCallable[Date]
234
- type MaybeCallableZonedDateTime = MaybeCallable[ZonedDateTime]
261
+ type MaybeCallableDateLike = MaybeCallable[DateLike]
262
+ type MaybeCallableTimeLike = MaybeCallable[TimeLike]
263
+ type MaybeCallableZonedDateTimeLike = MaybeCallable[ZonedDateTimeLike]
235
264
  type MonthDayLike = MaybeStr[MonthDay]
236
265
  type PlainDateTimeLike = MaybeStr[PlainDateTime]
237
266
  type TimeDeltaLike = MaybeStr[TimeDelta]
@@ -248,12 +277,21 @@ type TimeZone = Literal[
248
277
  "Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", "Africa/Asmara", "Africa/Asmera", "Africa/Bamako", "Africa/Bangui", "Africa/Banjul", "Africa/Bissau", "Africa/Blantyre", "Africa/Brazzaville", "Africa/Bujumbura", "Africa/Cairo", "Africa/Casablanca", "Africa/Ceuta", "Africa/Conakry", "Africa/Dakar", "Africa/Dar_es_Salaam", "Africa/Djibouti", "Africa/Douala", "Africa/El_Aaiun", "Africa/Freetown", "Africa/Gaborone", "Africa/Harare", "Africa/Johannesburg", "Africa/Juba", "Africa/Kampala", "Africa/Khartoum", "Africa/Kigali", "Africa/Kinshasa", "Africa/Lagos", "Africa/Libreville", "Africa/Lome", "Africa/Luanda", "Africa/Lubumbashi", "Africa/Lusaka", "Africa/Malabo", "Africa/Maputo", "Africa/Maseru", "Africa/Mbabane", "Africa/Mogadishu", "Africa/Monrovia", "Africa/Nairobi", "Africa/Ndjamena", "Africa/Niamey", "Africa/Nouakchott", "Africa/Ouagadougou", "Africa/Porto-Novo", "Africa/Sao_Tome", "Africa/Timbuktu", "Africa/Tripoli", "Africa/Tunis", "Africa/Windhoek", "America/Adak", "America/Anchorage", "America/Anguilla", "America/Antigua", "America/Araguaina", "America/Argentina/Buenos_Aires", "America/Argentina/Catamarca", "America/Argentina/ComodRivadavia", "America/Argentina/Cordoba", "America/Argentina/Jujuy", "America/Argentina/La_Rioja", "America/Argentina/Mendoza", "America/Argentina/Rio_Gallegos", "America/Argentina/Salta", "America/Argentina/San_Juan", "America/Argentina/San_Luis", "America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Aruba", "America/Asuncion", "America/Atikokan", "America/Atka", "America/Bahia", "America/Bahia_Banderas", "America/Barbados", "America/Belem", "America/Belize", "America/Blanc-Sablon", "America/Boa_Vista", "America/Bogota", "America/Boise", "America/Buenos_Aires", "America/Cambridge_Bay", "America/Campo_Grande", "America/Cancun", "America/Caracas", "America/Catamarca", "America/Cayenne", "America/Cayman", "America/Chicago", "America/Chihuahua", "America/Ciudad_Juarez", "America/Coral_Harbour", "America/Cordoba", "America/Costa_Rica", "America/Coyhaique", "America/Creston", "America/Cuiaba", "America/Curacao", "America/Danmarkshavn", "America/Dawson", "America/Dawson_Creek", "America/Denver", "America/Detroit", "America/Dominica", "America/Edmonton", "America/Eirunepe", "America/El_Salvador", "America/Ensenada", "America/Fort_Nelson", "America/Fort_Wayne", "America/Fortaleza", "America/Glace_Bay", "America/Godthab", "America/Goose_Bay", "America/Grand_Turk", "America/Grenada", "America/Guadeloupe", "America/Guatemala", "America/Guayaquil", "America/Guyana", "America/Halifax", "America/Havana", "America/Hermosillo", "America/Indiana/Indianapolis", "America/Indiana/Knox", "America/Indiana/Marengo", "America/Indiana/Petersburg", "America/Indiana/Tell_City", "America/Indiana/Vevay", "America/Indiana/Vincennes", "America/Indiana/Winamac", "America/Indianapolis", "America/Inuvik", "America/Iqaluit", "America/Jamaica", "America/Jujuy", "America/Juneau", "America/Kentucky/Louisville", "America/Kentucky/Monticello", "America/Knox_IN", "America/Kralendijk", "America/La_Paz", "America/Lima", "America/Los_Angeles", "America/Louisville", "America/Lower_Princes", "America/Maceio", "America/Managua", "America/Manaus", "America/Marigot", "America/Martinique", "America/Matamoros", "America/Mazatlan", "America/Mendoza", "America/Menominee", "America/Merida", "America/Metlakatla", "America/Mexico_City", "America/Miquelon", "America/Moncton", "America/Monterrey", "America/Montevideo", "America/Montreal", "America/Montserrat", "America/Nassau", "America/New_York", "America/Nipigon", "America/Nome", "America/Noronha", "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung", "America/Paramaribo", "America/Phoenix", "America/Port-au-Prince", "America/Port_of_Spain", "America/Porto_Acre", "America/Porto_Velho", "America/Puerto_Rico", "America/Punta_Arenas", "America/Rainy_River", "America/Rankin_Inlet", "America/Recife", "America/Regina", "America/Resolute", "America/Rio_Branco", "America/Rosario", "America/Santa_Isabel", "America/Santarem", "America/Santiago", "America/Santo_Domingo", "America/Sao_Paulo", "America/Scoresbysund", "America/Shiprock", "America/Sitka", "America/St_Barthelemy", "America/St_Johns", "America/St_Kitts", "America/St_Lucia", "America/St_Thomas", "America/St_Vincent", "America/Swift_Current", "America/Tegucigalpa", "America/Thule", "America/Thunder_Bay", "America/Tijuana", "America/Toronto", "America/Tortola", "America/Vancouver", "America/Virgin", "America/Whitehorse", "America/Winnipeg", "America/Yakutat", "America/Yellowknife", "Antarctica/Casey", "Antarctica/Davis", "Antarctica/DumontDUrville", "Antarctica/Macquarie", "Antarctica/Mawson", "Antarctica/McMurdo", "Antarctica/Palmer", "Antarctica/Rothera", "Antarctica/South_Pole", "Antarctica/Syowa", "Antarctica/Troll", "Antarctica/Vostok", "Arctic/Longyearbyen", "Asia/Aden", "Asia/Almaty", "Asia/Amman", "Asia/Anadyr", "Asia/Aqtau", "Asia/Aqtobe", "Asia/Ashgabat", "Asia/Ashkhabad", "Asia/Atyrau", "Asia/Baghdad", "Asia/Bahrain", "Asia/Baku", "Asia/Bangkok", "Asia/Barnaul", "Asia/Beirut", "Asia/Bishkek", "Asia/Brunei", "Asia/Calcutta", "Asia/Chita", "Asia/Choibalsan", "Asia/Chongqing", "Asia/Chungking", "Asia/Colombo", "Asia/Dacca", "Asia/Damascus", "Asia/Dhaka", "Asia/Dili", "Asia/Dubai", "Asia/Dushanbe", "Asia/Famagusta", "Asia/Gaza", "Asia/Harbin", "Asia/Hebron", "Asia/Ho_Chi_Minh", "Asia/Hong_Kong", "Asia/Hovd", "Asia/Irkutsk", "Asia/Istanbul", "Asia/Jakarta", "Asia/Jayapura", "Asia/Jerusalem", "Asia/Kabul", "Asia/Kamchatka", "Asia/Karachi", "Asia/Kashgar", "Asia/Kathmandu", "Asia/Katmandu", "Asia/Khandyga", "Asia/Kolkata", "Asia/Krasnoyarsk", "Asia/Kuala_Lumpur", "Asia/Kuching", "Asia/Kuwait", "Asia/Macao", "Asia/Macau", "Asia/Magadan", "Asia/Makassar", "Asia/Manila", "Asia/Muscat", "Asia/Nicosia", "Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk", "Asia/Oral", "Asia/Phnom_Penh", "Asia/Pontianak", "Asia/Pyongyang", "Asia/Qatar", "Asia/Qostanay", "Asia/Qyzylorda", "Asia/Rangoon", "Asia/Riyadh", "Asia/Saigon", "Asia/Sakhalin", "Asia/Samarkand", "Asia/Seoul", "Asia/Shanghai", "Asia/Singapore", "Asia/Srednekolymsk", "Asia/Taipei", "Asia/Tashkent", "Asia/Tbilisi", "Asia/Tehran", "Asia/Tel_Aviv", "Asia/Thimbu", "Asia/Thimphu", "Asia/Tokyo", "Asia/Tomsk", "Asia/Ujung_Pandang", "Asia/Ulaanbaatar", "Asia/Ulan_Bator", "Asia/Urumqi", "Asia/Ust-Nera", "Asia/Vientiane", "Asia/Vladivostok", "Asia/Yakutsk", "Asia/Yangon", "Asia/Yekaterinburg", "Asia/Yerevan", "Atlantic/Azores", "Atlantic/Bermuda", "Atlantic/Canary", "Atlantic/Cape_Verde", "Atlantic/Faeroe", "Atlantic/Faroe", "Atlantic/Jan_Mayen", "Atlantic/Madeira", "Atlantic/Reykjavik", "Atlantic/South_Georgia", "Atlantic/St_Helena", "Atlantic/Stanley", "Australia/ACT", "Australia/Adelaide", "Australia/Brisbane", "Australia/Broken_Hill", "Australia/Canberra", "Australia/Currie", "Australia/Darwin", "Australia/Eucla", "Australia/Hobart", "Australia/LHI", "Australia/Lindeman", "Australia/Lord_Howe", "Australia/Melbourne", "Australia/NSW", "Australia/North", "Australia/Perth", "Australia/Queensland", "Australia/South", "Australia/Sydney", "Australia/Tasmania", "Australia/Victoria", "Australia/West", "Australia/Yancowinna", "Brazil/Acre", "Brazil/DeNoronha", "Brazil/East", "Brazil/West", "CET", "CST6CDT", "Canada/Atlantic", "Canada/Central", "Canada/Eastern", "Canada/Mountain", "Canada/Newfoundland", "Canada/Pacific", "Canada/Saskatchewan", "Canada/Yukon", "Chile/Continental", "Chile/EasterIsland", "Cuba", "EET", "EST", "EST5EDT", "Egypt", "Eire", "Etc/GMT", "Etc/GMT+0", "Etc/GMT+1", "Etc/GMT+10", "Etc/GMT+11", "Etc/GMT+12", "Etc/GMT+2", "Etc/GMT+3", "Etc/GMT+4", "Etc/GMT+5", "Etc/GMT+6", "Etc/GMT+7", "Etc/GMT+8", "Etc/GMT+9", "Etc/GMT-0", "Etc/GMT-1", "Etc/GMT-10", "Etc/GMT-11", "Etc/GMT-12", "Etc/GMT-13", "Etc/GMT-14", "Etc/GMT-2", "Etc/GMT-3", "Etc/GMT-4", "Etc/GMT-5", "Etc/GMT-6", "Etc/GMT-7", "Etc/GMT-8", "Etc/GMT-9", "Etc/GMT0", "Etc/Greenwich", "Etc/UCT", "Etc/UTC", "Etc/Universal", "Etc/Zulu", "Europe/Amsterdam", "Europe/Andorra", "Europe/Astrakhan", "Europe/Athens", "Europe/Belfast", "Europe/Belgrade", "Europe/Berlin", "Europe/Bratislava", "Europe/Brussels", "Europe/Bucharest", "Europe/Budapest", "Europe/Busingen", "Europe/Chisinau", "Europe/Copenhagen", "Europe/Dublin", "Europe/Gibraltar", "Europe/Guernsey", "Europe/Helsinki", "Europe/Isle_of_Man", "Europe/Istanbul", "Europe/Jersey", "Europe/Kaliningrad", "Europe/Kiev", "Europe/Kirov", "Europe/Kyiv", "Europe/Lisbon", "Europe/Ljubljana", "Europe/London", "Europe/Luxembourg", "Europe/Madrid", "Europe/Malta", "Europe/Mariehamn", "Europe/Minsk", "Europe/Monaco", "Europe/Moscow", "Europe/Nicosia", "Europe/Oslo", "Europe/Paris", "Europe/Podgorica", "Europe/Prague", "Europe/Riga", "Europe/Rome", "Europe/Samara", "Europe/San_Marino", "Europe/Sarajevo", "Europe/Saratov", "Europe/Simferopol", "Europe/Skopje", "Europe/Sofia", "Europe/Stockholm", "Europe/Tallinn", "Europe/Tirane", "Europe/Tiraspol", "Europe/Ulyanovsk", "Europe/Uzhgorod", "Europe/Vaduz", "Europe/Vatican", "Europe/Vienna", "Europe/Vilnius", "Europe/Volgograd", "Europe/Warsaw", "Europe/Zagreb", "Europe/Zaporozhye", "Europe/Zurich", "Factory", "GB", "GB-Eire", "GMT", "GMT+0", "GMT-0", "GMT0", "Greenwich", "HST", "Hongkong", "Iceland", "Indian/Antananarivo", "Indian/Chagos", "Indian/Christmas", "Indian/Cocos", "Indian/Comoro", "Indian/Kerguelen", "Indian/Mahe", "Indian/Maldives", "Indian/Mauritius", "Indian/Mayotte", "Indian/Reunion", "Iran", "Israel", "Jamaica", "Japan", "Kwajalein", "Libya", "MET", "MST", "MST7MDT", "Mexico/BajaNorte", "Mexico/BajaSur", "Mexico/General", "NZ", "NZ-CHAT", "Navajo", "PRC", "PST8PDT", "Pacific/Apia", "Pacific/Auckland", "Pacific/Bougainville", "Pacific/Chatham", "Pacific/Chuuk", "Pacific/Easter", "Pacific/Efate", "Pacific/Enderbury", "Pacific/Fakaofo", "Pacific/Fiji", "Pacific/Funafuti", "Pacific/Galapagos", "Pacific/Gambier", "Pacific/Guadalcanal", "Pacific/Guam", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Kanton", "Pacific/Kiritimati", "Pacific/Kosrae", "Pacific/Kwajalein", "Pacific/Majuro", "Pacific/Marquesas", "Pacific/Midway", "Pacific/Nauru", "Pacific/Niue", "Pacific/Norfolk", "Pacific/Noumea", "Pacific/Pago_Pago", "Pacific/Palau", "Pacific/Pitcairn", "Pacific/Pohnpei", "Pacific/Ponape", "Pacific/Port_Moresby", "Pacific/Rarotonga", "Pacific/Saipan", "Pacific/Samoa", "Pacific/Tahiti", "Pacific/Tarawa", "Pacific/Tongatapu", "Pacific/Truk", "Pacific/Wake", "Pacific/Wallis", "Pacific/Yap", "Poland", "Portugal", "ROC", "ROK", "Singapore", "Turkey", "UCT", "US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa", "UTC", "Universal", "W-SU", "WET", "Zulu"
249
278
  ]
250
279
  # fmt: on
280
+ TIME_ZONES: list[TimeZone] = list(get_args(TimeZone.__value__))
281
+
282
+
251
283
  type TimeZoneLike = (
252
- ZoneInfo | ZonedDateTime | Literal["local"] | TimeZone | dt.tzinfo | dt.datetime
284
+ ZoneInfo
285
+ | ZonedDateTime
286
+ | Literal["local", "localtime"]
287
+ | TimeZone
288
+ | dt.tzinfo
289
+ | dt.datetime
253
290
  )
254
291
 
255
292
 
256
293
  __all__ = [
294
+ "TIME_ZONES",
257
295
  "Coro",
258
296
  "Dataclass",
259
297
  "DateDeltaLike",
@@ -264,24 +302,28 @@ __all__ = [
264
302
  "Delta",
265
303
  "EnumLike",
266
304
  "ExcInfo",
305
+ "ExceptionTypeLike",
267
306
  "IPv4AddressLike",
268
307
  "IPv6AddressLike",
269
- "IterableHashable",
270
308
  "LogLevel",
271
- "LoggerOrName",
309
+ "LoggerLike",
272
310
  "MathRoundMode",
273
311
  "MaybeCallable",
274
- "MaybeCallableBool",
275
- "MaybeCallableDate",
276
- "MaybeCallableEvent",
312
+ "MaybeCallableBoolLike",
313
+ "MaybeCallableDateLike",
277
314
  "MaybeCallablePathLike",
278
- "MaybeCallableZonedDateTime",
315
+ "MaybeCallableStr",
316
+ "MaybeCallableTimeLike",
317
+ "MaybeCallableUUIDLike",
318
+ "MaybeCallableZonedDateTimeLike",
319
+ "MaybeCollection",
320
+ "MaybeCollectionStr",
279
321
  "MaybeCoro",
280
322
  "MaybeIterable",
281
- "MaybeIterableHashable",
282
323
  "MaybeList",
283
- "MaybeListStr",
284
324
  "MaybeSequence",
325
+ "MaybeSequenceStr",
326
+ "MaybeSet",
285
327
  "MaybeStr",
286
328
  "MaybeType",
287
329
  "MonthDayLike",
@@ -294,7 +336,9 @@ __all__ = [
294
336
  "PathLike",
295
337
  "PatternLike",
296
338
  "PlainDateTimeLike",
339
+ "Retry",
297
340
  "Seed",
341
+ "SequenceStr",
298
342
  "SerializeObjectExtra",
299
343
  "Sign",
300
344
  "StrMapping",
@@ -304,6 +348,7 @@ __all__ = [
304
348
  "SupportsBytes",
305
349
  "SupportsComplex",
306
350
  "SupportsFloat",
351
+ "SupportsFloatOrIndex",
307
352
  "SupportsGT",
308
353
  "SupportsInt",
309
354
  "SupportsInt",
@@ -318,6 +363,7 @@ __all__ = [
318
363
  "TimeZoneLike",
319
364
  "TupleOrStrMapping",
320
365
  "TypeLike",
366
+ "UUIDLike",
321
367
  "WeekDay",
322
368
  "YearMonthLike",
323
369
  "ZonedDateTimeLike",