dycw-utilities 0.129.10__py3-none-any.whl → 0.175.17__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.
Files changed (103) hide show
  1. dycw_utilities-0.175.17.dist-info/METADATA +34 -0
  2. dycw_utilities-0.175.17.dist-info/RECORD +103 -0
  3. dycw_utilities-0.175.17.dist-info/WHEEL +4 -0
  4. dycw_utilities-0.175.17.dist-info/entry_points.txt +4 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +14 -14
  7. utilities/asyncio.py +350 -819
  8. utilities/atomicwrites.py +18 -6
  9. utilities/atools.py +77 -22
  10. utilities/cachetools.py +24 -29
  11. utilities/click.py +393 -237
  12. utilities/concurrent.py +8 -11
  13. utilities/contextlib.py +216 -17
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +83 -118
  17. utilities/docker.py +293 -0
  18. utilities/enum.py +26 -23
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +29 -65
  21. utilities/fpdf2.py +3 -3
  22. utilities/functions.py +169 -416
  23. utilities/functools.py +18 -19
  24. utilities/git.py +9 -30
  25. utilities/grp.py +28 -0
  26. utilities/gzip.py +31 -0
  27. utilities/http.py +3 -2
  28. utilities/hypothesis.py +738 -589
  29. utilities/importlib.py +17 -1
  30. utilities/inflect.py +25 -0
  31. utilities/iterables.py +194 -262
  32. utilities/jinja2.py +148 -0
  33. utilities/json.py +70 -0
  34. utilities/libcst.py +38 -17
  35. utilities/lightweight_charts.py +5 -9
  36. utilities/logging.py +345 -543
  37. utilities/math.py +18 -13
  38. utilities/memory_profiler.py +11 -15
  39. utilities/more_itertools.py +200 -131
  40. utilities/operator.py +33 -29
  41. utilities/optuna.py +6 -6
  42. utilities/orjson.py +272 -137
  43. utilities/os.py +61 -4
  44. utilities/parse.py +59 -61
  45. utilities/pathlib.py +281 -40
  46. utilities/permissions.py +298 -0
  47. utilities/pickle.py +2 -2
  48. utilities/platform.py +24 -5
  49. utilities/polars.py +1214 -430
  50. utilities/polars_ols.py +1 -1
  51. utilities/postgres.py +408 -0
  52. utilities/pottery.py +113 -26
  53. utilities/pqdm.py +10 -11
  54. utilities/psutil.py +6 -57
  55. utilities/pwd.py +28 -0
  56. utilities/pydantic.py +4 -54
  57. utilities/pydantic_settings.py +240 -0
  58. utilities/pydantic_settings_sops.py +76 -0
  59. utilities/pyinstrument.py +8 -10
  60. utilities/pytest.py +227 -121
  61. utilities/pytest_plugins/__init__.py +1 -0
  62. utilities/pytest_plugins/pytest_randomly.py +23 -0
  63. utilities/pytest_plugins/pytest_regressions.py +56 -0
  64. utilities/pytest_regressions.py +26 -46
  65. utilities/random.py +13 -9
  66. utilities/re.py +58 -28
  67. utilities/redis.py +401 -550
  68. utilities/scipy.py +1 -1
  69. utilities/sentinel.py +10 -0
  70. utilities/shelve.py +4 -1
  71. utilities/shutil.py +25 -0
  72. utilities/slack_sdk.py +36 -106
  73. utilities/sqlalchemy.py +502 -473
  74. utilities/sqlalchemy_polars.py +38 -94
  75. utilities/string.py +2 -3
  76. utilities/subprocess.py +1572 -0
  77. utilities/tempfile.py +86 -4
  78. utilities/testbook.py +50 -0
  79. utilities/text.py +165 -42
  80. utilities/timer.py +37 -65
  81. utilities/traceback.py +158 -929
  82. utilities/types.py +146 -116
  83. utilities/typing.py +531 -71
  84. utilities/tzdata.py +1 -53
  85. utilities/tzlocal.py +6 -23
  86. utilities/uuid.py +43 -5
  87. utilities/version.py +27 -26
  88. utilities/whenever.py +1776 -386
  89. utilities/zoneinfo.py +84 -22
  90. dycw_utilities-0.129.10.dist-info/METADATA +0 -241
  91. dycw_utilities-0.129.10.dist-info/RECORD +0 -96
  92. dycw_utilities-0.129.10.dist-info/WHEEL +0 -4
  93. dycw_utilities-0.129.10.dist-info/licenses/LICENSE +0 -21
  94. utilities/datetime.py +0 -1409
  95. utilities/eventkit.py +0 -402
  96. utilities/loguru.py +0 -144
  97. utilities/luigi.py +0 -228
  98. utilities/period.py +0 -324
  99. utilities/pyrsistent.py +0 -89
  100. utilities/python_dotenv.py +0 -105
  101. utilities/streamlit.py +0 -105
  102. utilities/sys.py +0 -87
  103. utilities/tenacity.py +0 -145
utilities/click.py CHANGED
@@ -1,65 +1,60 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime as dt
3
+ import enum
4
+ import ipaddress
4
5
  import pathlib
5
- from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar, override
6
- from uuid import UUID
6
+ import uuid
7
+ from enum import StrEnum
8
+ from typing import TYPE_CHECKING, TypedDict, assert_never, override
7
9
 
8
- import click
10
+ import whenever
9
11
  from click import Choice, Context, Parameter, ParamType
10
- from click.types import (
11
- BoolParamType,
12
- FloatParamType,
13
- IntParamType,
14
- StringParamType,
15
- UUIDParameterType,
16
- )
12
+ from click.types import IntParamType, StringParamType
17
13
 
18
- import utilities.datetime
19
- import utilities.types
20
- from utilities.datetime import EnsureMonthError, MonthLike, ensure_month
21
14
  from utilities.enum import EnsureEnumError, ensure_enum
22
- from utilities.functions import EnsureStrError, ensure_str, get_class_name
23
- from utilities.iterables import is_iterable_not_str
15
+ from utilities.functions import EnsureStrError, ensure_str, get_class, get_class_name
16
+ from utilities.iterables import is_iterable_not_str, one_unique
17
+ from utilities.parse import ParseObjectError, parse_object
24
18
  from utilities.text import split_str
25
- from utilities.types import (
26
- DateLike,
27
- DateTimeLike,
28
- EnumLike,
29
- MaybeStr,
30
- TEnum,
31
- TimeDeltaLike,
32
- TimeLike,
33
- )
34
19
 
35
20
  if TYPE_CHECKING:
36
- from collections.abc import Iterable, Sequence
37
-
38
- _T = TypeVar("_T")
39
- _TParam = TypeVar("_TParam", bound=ParamType)
21
+ from collections.abc import Iterable
22
+
23
+ from utilities.types import (
24
+ DateDeltaLike,
25
+ DateLike,
26
+ DateTimeDeltaLike,
27
+ EnumLike,
28
+ IPv4AddressLike,
29
+ IPv6AddressLike,
30
+ MaybeStr,
31
+ MonthDayLike,
32
+ PathLike,
33
+ PlainDateTimeLike,
34
+ TimeDeltaLike,
35
+ TimeLike,
36
+ YearMonthLike,
37
+ ZonedDateTimeLike,
38
+ )
40
39
 
41
40
 
42
- FilePath = click.Path(file_okay=True, dir_okay=False, path_type=pathlib.Path)
43
- DirPath = click.Path(file_okay=False, dir_okay=True, path_type=pathlib.Path)
44
- ExistingFilePath = click.Path(
45
- exists=True, file_okay=True, dir_okay=False, path_type=pathlib.Path
46
- )
47
- ExistingDirPath = click.Path(
48
- exists=True, file_okay=False, dir_okay=True, path_type=pathlib.Path
49
- )
50
-
51
-
52
- class _HelpOptionNames(TypedDict):
53
- help_option_names: Sequence[str]
41
+ class _ContextSettings(TypedDict):
42
+ context_settings: _ContextSettingsInner
54
43
 
55
44
 
56
- class _ContextSettings(TypedDict):
57
- context_settings: _HelpOptionNames
45
+ class _ContextSettingsInner(TypedDict):
46
+ max_content_width: int
47
+ help_option_names: list[str]
48
+ show_default: bool
58
49
 
59
50
 
60
- CONTEXT_SETTINGS_HELP_OPTION_NAMES = _ContextSettings(
61
- context_settings=_HelpOptionNames(help_option_names=["-h", "--help"])
51
+ _MAX_CONTENT_WIDTH = 120
52
+ _CONTEXT_SETTINGS_INNER = _ContextSettingsInner(
53
+ max_content_width=_MAX_CONTENT_WIDTH,
54
+ help_option_names=["-h", "--help"],
55
+ show_default=True,
62
56
  )
57
+ CONTEXT_SETTINGS = _ContextSettings(context_settings=_CONTEXT_SETTINGS_INNER)
63
58
 
64
59
 
65
60
  # parameters
@@ -77,20 +72,24 @@ class Date(ParamType):
77
72
  @override
78
73
  def convert(
79
74
  self, value: DateLike, param: Parameter | None, ctx: Context | None
80
- ) -> dt.date:
75
+ ) -> whenever.Date:
81
76
  """Convert a value into the `Date` type."""
82
- from utilities.whenever import EnsureDateError, ensure_date
77
+ match value:
78
+ case whenever.Date():
79
+ return value
80
+ case str():
81
+ try:
82
+ return whenever.Date.parse_iso(value)
83
+ except ValueError as error:
84
+ self.fail(str(error), param, ctx)
85
+ case never:
86
+ assert_never(never)
83
87
 
84
- try:
85
- return ensure_date(value)
86
- except EnsureDateError as error:
87
- self.fail(str(error), param, ctx)
88
88
 
89
+ class DateDelta(ParamType):
90
+ """A date-delta-valued parameter."""
89
91
 
90
- class Duration(ParamType):
91
- """A duration-valued parameter."""
92
-
93
- name = "duration"
92
+ name = "date delta"
94
93
 
95
94
  @override
96
95
  def __repr__(self) -> str:
@@ -98,27 +97,58 @@ class Duration(ParamType):
98
97
 
99
98
  @override
100
99
  def convert(
101
- self,
102
- value: MaybeStr[utilities.types.Duration],
103
- param: Parameter | None,
104
- ctx: Context | None,
105
- ) -> utilities.types.Duration:
106
- """Convert a value into the `Duration` type."""
107
- from utilities.whenever import EnsureDurationError, ensure_duration
108
-
109
- try:
110
- return ensure_duration(value)
111
- except EnsureDurationError as error:
112
- self.fail(str(error), param, ctx)
100
+ self, value: DateDeltaLike, param: Parameter | None, ctx: Context | None
101
+ ) -> whenever.DateDelta:
102
+ """Convert a value into the `DateDelta` type."""
103
+ match value:
104
+ case whenever.DateDelta():
105
+ return value
106
+ case str():
107
+ try:
108
+ return whenever.DateDelta.parse_iso(value)
109
+ except ValueError as error:
110
+ self.fail(str(error), param, ctx)
111
+ case never:
112
+ assert_never(never)
113
+
114
+
115
+ class DateTimeDelta(ParamType):
116
+ """A date-delta-valued parameter."""
117
+
118
+ name = "date-time delta"
113
119
 
120
+ @override
121
+ def __repr__(self) -> str:
122
+ return self.name.upper()
114
123
 
115
- class Enum(ParamType, Generic[TEnum]):
124
+ @override
125
+ def convert(
126
+ self, value: DateTimeDeltaLike, param: Parameter | None, ctx: Context | None
127
+ ) -> whenever.DateTimeDelta:
128
+ """Convert a value into the `DateTimeDelta` type."""
129
+ match value:
130
+ case whenever.DateTimeDelta():
131
+ return value
132
+ case str():
133
+ try:
134
+ return whenever.DateTimeDelta.parse_iso(value)
135
+ except ValueError as error:
136
+ self.fail(str(error), param, ctx)
137
+ case never:
138
+ assert_never(never)
139
+
140
+
141
+ class Enum[E: enum.Enum](ParamType):
116
142
  """An enum-valued parameter."""
117
143
 
118
- def __init__(self, enum: type[TEnum], /, *, case_sensitive: bool = False) -> None:
144
+ @override
145
+ def __init__(
146
+ self, enum: type[E], /, *, value: bool = False, case_sensitive: bool = False
147
+ ) -> None:
119
148
  cls = get_class_name(enum)
120
- self.name = f"ENUM[{cls}]"
149
+ self.name = f"enum[{cls}]"
121
150
  self._enum = enum
151
+ self._value = issubclass(self._enum, StrEnum) or value
122
152
  self._case_sensitive = case_sensitive
123
153
  super().__init__()
124
154
 
@@ -129,8 +159,8 @@ class Enum(ParamType, Generic[TEnum]):
129
159
 
130
160
  @override
131
161
  def convert(
132
- self, value: EnumLike[TEnum], param: Parameter | None, ctx: Context | None
133
- ) -> TEnum:
162
+ self, value: EnumLike[E], param: Parameter | None, ctx: Context | None
163
+ ) -> E:
134
164
  """Convert a value into the `Enum` type."""
135
165
  try:
136
166
  return ensure_enum(value, self._enum, case_sensitive=self._case_sensitive)
@@ -140,34 +170,160 @@ class Enum(ParamType, Generic[TEnum]):
140
170
  @override
141
171
  def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
142
172
  _ = ctx
143
- desc = ",".join(e.name for e in self._enum)
173
+ desc = ",".join(str(e.value) if self._value else e.name for e in self._enum)
144
174
  return _make_metavar(param, desc)
145
175
 
146
176
 
147
- class Month(ParamType):
148
- """A month-valued parameter."""
177
+ class EnumPartial[E: enum.Enum](ParamType):
178
+ """An enum-valued parameter."""
149
179
 
150
- name = "month"
180
+ @override
181
+ def __init__(
182
+ self,
183
+ members: Iterable[E],
184
+ /,
185
+ *,
186
+ value: bool = False,
187
+ case_sensitive: bool = False,
188
+ ) -> None:
189
+ self._members = list(members)
190
+ self._enum = one_unique(get_class(e) for e in self._members)
191
+ cls = get_class_name(self._enum)
192
+ self.name = f"enum-partial[{cls}]"
193
+ self._value = issubclass(self._enum, StrEnum) or value
194
+ self._case_sensitive = case_sensitive
195
+ super().__init__()
151
196
 
152
197
  @override
153
198
  def __repr__(self) -> str:
154
- return self.name.upper()
199
+ cls = get_class_name(self._enum)
200
+ return f"ENUMPARTIAL[{cls}]"
155
201
 
156
202
  @override
157
203
  def convert(
158
- self, value: MonthLike, param: Parameter | None, ctx: Context | None
159
- ) -> utilities.datetime.Month:
160
- """Convert a value into the `Month` type."""
204
+ self, value: EnumLike[E], param: Parameter | None, ctx: Context | None
205
+ ) -> E:
206
+ """Convert a value into the `Enum` type."""
161
207
  try:
162
- return ensure_month(value)
163
- except EnsureMonthError as error:
208
+ enum = ensure_enum(value, self._enum, case_sensitive=self._case_sensitive)
209
+ except EnsureEnumError as error:
164
210
  self.fail(str(error), param, ctx)
211
+ if enum in self._members:
212
+ return enum
213
+ return self.fail(f"{enum.value!r} is not a selected member")
214
+
215
+ @override
216
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
217
+ _ = ctx
218
+ desc = ",".join(str(e.value) if self._value else e.name for e in self._members)
219
+ return _make_metavar(param, desc)
220
+
221
+
222
+ class IPv4Address(ParamType):
223
+ """An IPv4 address-valued parameter."""
224
+
225
+ name = "ipv4 address"
226
+
227
+ @override
228
+ def __repr__(self) -> str:
229
+ return self.name.upper()
230
+
231
+ @override
232
+ def convert(
233
+ self, value: IPv4AddressLike, param: Parameter | None, ctx: Context | None
234
+ ) -> ipaddress.IPv4Address:
235
+ """Convert a value into the `IPv4Address` type."""
236
+ match value:
237
+ case ipaddress.IPv4Address():
238
+ return value
239
+ case str():
240
+ try:
241
+ return parse_object(ipaddress.IPv4Address, value)
242
+ except ParseObjectError as error:
243
+ self.fail(str(error), param, ctx)
244
+ case never:
245
+ assert_never(never)
246
+
247
+
248
+ class IPv6Address(ParamType):
249
+ """An IPv6 address-valued parameter."""
250
+
251
+ name = "ipv6 address"
252
+
253
+ @override
254
+ def __repr__(self) -> str:
255
+ return self.name.upper()
256
+
257
+ @override
258
+ def convert(
259
+ self, value: IPv6AddressLike, param: Parameter | None, ctx: Context | None
260
+ ) -> ipaddress.IPv6Address:
261
+ """Convert a value into the `IPv6Address` type."""
262
+ match value:
263
+ case ipaddress.IPv6Address():
264
+ return value
265
+ case str():
266
+ try:
267
+ return parse_object(ipaddress.IPv6Address, value)
268
+ except ParseObjectError as error:
269
+ self.fail(str(error), param, ctx)
270
+ case never:
271
+ assert_never(never)
272
+
273
+
274
+ class MonthDay(ParamType):
275
+ """A month-day parameter."""
276
+
277
+ name = "month-day"
278
+
279
+ @override
280
+ def __repr__(self) -> str:
281
+ return self.name.upper()
282
+
283
+ @override
284
+ def convert(
285
+ self, value: MonthDayLike, param: Parameter | None, ctx: Context | None
286
+ ) -> whenever.MonthDay:
287
+ """Convert a value into the `MonthDay` type."""
288
+ match value:
289
+ case whenever.MonthDay():
290
+ return value
291
+ case str():
292
+ try:
293
+ return whenever.MonthDay.parse_iso(value)
294
+ except ValueError as error:
295
+ self.fail(str(error), param, ctx)
296
+ case never:
297
+ assert_never(never)
298
+
299
+
300
+ class Path(ParamType):
301
+ """A path-valued parameter."""
302
+
303
+ name = "path"
304
+
305
+ @override
306
+ def __repr__(self) -> str:
307
+ return self.name.upper()
308
+
309
+ @override
310
+ def convert(
311
+ self, value: PathLike, param: Parameter | None, ctx: Context | None
312
+ ) -> pathlib.Path:
313
+ """Convert a value into the `Path` type."""
314
+ match value:
315
+ case pathlib.Path():
316
+ return value.expanduser()
317
+ case str():
318
+ return pathlib.Path(value).expanduser()
319
+ case never:
320
+ assert_never(never)
165
321
 
166
322
 
167
323
  class PlainDateTime(ParamType):
168
324
  """A local-datetime-valued parameter."""
169
325
 
170
- name = "plain datetime"
326
+ name = "plain date-time"
171
327
 
172
328
  @override
173
329
  def __repr__(self) -> str:
@@ -175,15 +331,19 @@ class PlainDateTime(ParamType):
175
331
 
176
332
  @override
177
333
  def convert(
178
- self, value: DateTimeLike, param: Parameter | None, ctx: Context | None
179
- ) -> dt.date:
180
- """Convert a value into the `LocalDateTime` type."""
181
- from utilities.whenever import EnsurePlainDateTimeError, ensure_plain_datetime
182
-
183
- try:
184
- return ensure_plain_datetime(value)
185
- except EnsurePlainDateTimeError as error:
186
- self.fail(str(error), param, ctx)
334
+ self, value: PlainDateTimeLike, param: Parameter | None, ctx: Context | None
335
+ ) -> whenever.PlainDateTime:
336
+ """Convert a value into the `PlainDateTime` type."""
337
+ match value:
338
+ case whenever.PlainDateTime():
339
+ return value
340
+ case str():
341
+ try:
342
+ return whenever.PlainDateTime.parse_iso(value)
343
+ except ValueError as error:
344
+ self.fail(str(error), param, ctx)
345
+ case never:
346
+ assert_never(never)
187
347
 
188
348
 
189
349
  class Time(ParamType):
@@ -198,20 +358,50 @@ class Time(ParamType):
198
358
  @override
199
359
  def convert(
200
360
  self, value: TimeLike, param: Parameter | None, ctx: Context | None
201
- ) -> dt.time:
361
+ ) -> whenever.Time:
202
362
  """Convert a value into the `Time` type."""
203
- from utilities.whenever import EnsureTimeError, ensure_time
363
+ match value:
364
+ case whenever.Time():
365
+ return value
366
+ case str():
367
+ try:
368
+ return whenever.Time.parse_iso(value)
369
+ except ValueError as error:
370
+ self.fail(str(error), param, ctx)
371
+ case never:
372
+ assert_never(never)
373
+
374
+
375
+ class TimeDelta(ParamType):
376
+ """A timedelta-valued parameter."""
204
377
 
205
- try:
206
- return ensure_time(value)
207
- except EnsureTimeError as error:
208
- return self.fail(str(error), param=param, ctx=ctx)
378
+ name = "time-delta"
379
+
380
+ @override
381
+ def __repr__(self) -> str:
382
+ return self.name.upper()
209
383
 
384
+ @override
385
+ def convert(
386
+ self, value: TimeDeltaLike, param: Parameter | None, ctx: Context | None
387
+ ) -> whenever.TimeDelta:
388
+ """Convert a value into the `TimeDelta` type."""
389
+ match value:
390
+ case whenever.TimeDelta():
391
+ return value
392
+ case str():
393
+ try:
394
+ return whenever.TimeDelta.parse_iso(value)
395
+ except ValueError as error:
396
+ self.fail(str(error), param, ctx)
397
+ case never:
398
+ assert_never(never)
210
399
 
211
- class Timedelta(ParamType):
212
- """A timedelta-valued parameter."""
213
400
 
214
- name = "timedelta"
401
+ class UUID(ParamType):
402
+ """A UUID-valued parameter."""
403
+
404
+ name = "uuid"
215
405
 
216
406
  @override
217
407
  def __repr__(self) -> str:
@@ -219,21 +409,51 @@ class Timedelta(ParamType):
219
409
 
220
410
  @override
221
411
  def convert(
222
- self, value: TimeDeltaLike, param: Parameter | None, ctx: Context | None
223
- ) -> dt.timedelta:
224
- """Convert a value into the `Timedelta` type."""
225
- from utilities.whenever import EnsureTimedeltaError, ensure_timedelta
412
+ self, value: uuid.UUID | str, param: Parameter | None, ctx: Context | None
413
+ ) -> uuid.UUID:
414
+ """Convert a value into the `UUID` type."""
415
+ match value:
416
+ case uuid.UUID():
417
+ return value
418
+ case str():
419
+ try:
420
+ return uuid.UUID(value)
421
+ except ValueError as error:
422
+ self.fail(str(error), param, ctx)
423
+ case never:
424
+ assert_never(never)
425
+
426
+
427
+ class YearMonth(ParamType):
428
+ """A year-month parameter."""
429
+
430
+ name = "year-month"
226
431
 
227
- try:
228
- return ensure_timedelta(value)
229
- except EnsureTimedeltaError as error:
230
- self.fail(str(error), param, ctx)
432
+ @override
433
+ def __repr__(self) -> str:
434
+ return self.name.upper()
435
+
436
+ @override
437
+ def convert(
438
+ self, value: YearMonthLike, param: Parameter | None, ctx: Context | None
439
+ ) -> whenever.YearMonth:
440
+ """Convert a value into the `YearMonth` type."""
441
+ match value:
442
+ case whenever.YearMonth():
443
+ return value
444
+ case str():
445
+ try:
446
+ return whenever.YearMonth.parse_iso(value)
447
+ except ValueError as error:
448
+ self.fail(str(error), param, ctx)
449
+ case never:
450
+ assert_never(never)
231
451
 
232
452
 
233
453
  class ZonedDateTime(ParamType):
234
454
  """A zoned-datetime-valued parameter."""
235
455
 
236
- name = "zoned datetime"
456
+ name = "zoned date-time"
237
457
 
238
458
  @override
239
459
  def __repr__(self) -> str:
@@ -241,41 +461,42 @@ class ZonedDateTime(ParamType):
241
461
 
242
462
  @override
243
463
  def convert(
244
- self, value: DateTimeLike, param: Parameter | None, ctx: Context | None
245
- ) -> dt.date:
246
- """Convert a value into the `DateTime` type."""
247
- from utilities.whenever import EnsureZonedDateTimeError, ensure_zoned_datetime
248
-
249
- try:
250
- return ensure_zoned_datetime(value)
251
- except EnsureZonedDateTimeError as error:
252
- self.fail(str(error), param, ctx)
464
+ self, value: ZonedDateTimeLike, param: Parameter | None, ctx: Context | None
465
+ ) -> whenever.ZonedDateTime:
466
+ """Convert a value into the `ZonedDateTime` type."""
467
+ match value:
468
+ case whenever.ZonedDateTime():
469
+ return value
470
+ case str():
471
+ try:
472
+ return whenever.ZonedDateTime.parse_iso(value)
473
+ except ValueError as error:
474
+ self.fail(str(error), param, ctx)
475
+ case never:
476
+ assert_never(never)
253
477
 
254
478
 
255
479
  # parameters - frozenset
256
480
 
257
481
 
258
- class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
482
+ class FrozenSetParameter[P: ParamType, T](ParamType):
259
483
  """A frozenset-valued parameter."""
260
484
 
261
- def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
262
- self.name = f"FROZENSET[{param.name}]"
485
+ @override
486
+ def __init__(self, param: P, /, *, separator: str = ",") -> None:
487
+ self.name = f"frozenset[{param.name}]"
263
488
  self._param = param
264
489
  self._separator = separator
265
490
  super().__init__()
266
491
 
267
492
  @override
268
493
  def __repr__(self) -> str:
269
- desc = repr(self._param)
270
- return f"FROZENSET[{desc}]"
494
+ return f"FROZENSET[{self._param!r}]"
271
495
 
272
496
  @override
273
497
  def convert(
274
- self,
275
- value: MaybeStr[Iterable[_T]],
276
- param: Parameter | None,
277
- ctx: Context | None,
278
- ) -> frozenset[_T]:
498
+ self, value: MaybeStr[Iterable[T]], param: Parameter | None, ctx: Context | None
499
+ ) -> frozenset[T]:
279
500
  """Convert a value into the `ListDates` type."""
280
501
  if is_iterable_not_str(value):
281
502
  return frozenset(value)
@@ -297,26 +518,13 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
297
518
  return _make_metavar(param, desc)
298
519
 
299
520
 
300
- class FrozenSetBools(FrozenSetParameter[BoolParamType, str]):
301
- """A frozenset-of-bools-valued parameter."""
302
-
303
- def __init__(self, *, separator: str = ",") -> None:
304
- super().__init__(BoolParamType(), separator=separator)
305
-
306
-
307
- class FrozenSetDates(FrozenSetParameter[Date, dt.date]):
308
- """A frozenset-of-dates-valued parameter."""
309
-
310
- def __init__(self, *, separator: str = ",") -> None:
311
- super().__init__(Date(), separator=separator)
312
-
313
-
314
521
  class FrozenSetChoices(FrozenSetParameter[Choice, str]):
315
522
  """A frozenset-of-choices-valued parameter."""
316
523
 
524
+ @override
317
525
  def __init__(
318
526
  self,
319
- choices: Sequence[str],
527
+ choices: list[str],
320
528
  /,
321
529
  *,
322
530
  case_sensitive: bool = False,
@@ -327,79 +535,53 @@ class FrozenSetChoices(FrozenSetParameter[Choice, str]):
327
535
  )
328
536
 
329
537
 
330
- class FrozenSetEnums(FrozenSetParameter[Enum[TEnum], TEnum]):
538
+ class FrozenSetEnums[E: enum.Enum](FrozenSetParameter[Enum[E], E]):
331
539
  """A frozenset-of-enums-valued parameter."""
332
540
 
541
+ @override
333
542
  def __init__(
334
- self,
335
- enum: type[TEnum],
336
- /,
337
- *,
338
- case_sensitive: bool = False,
339
- separator: str = ",",
543
+ self, enum: type[E], /, *, case_sensitive: bool = False, separator: str = ","
340
544
  ) -> None:
341
545
  super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
342
546
 
343
547
 
344
- class FrozenSetFloats(FrozenSetParameter[FloatParamType, float]):
345
- """A frozenset-of-floats-valued parameter."""
346
-
347
- def __init__(self, *, separator: str = ",") -> None:
348
- super().__init__(FloatParamType(), separator=separator)
349
-
350
-
351
548
  class FrozenSetInts(FrozenSetParameter[IntParamType, int]):
352
549
  """A frozenset-of-ints-valued parameter."""
353
550
 
551
+ @override
354
552
  def __init__(self, *, separator: str = ",") -> None:
355
553
  super().__init__(IntParamType(), separator=separator)
356
554
 
357
555
 
358
- class FrozenSetMonths(FrozenSetParameter[Month, utilities.datetime.Month]):
359
- """A frozenset-of-months-valued parameter."""
360
-
361
- def __init__(self, *, separator: str = ",") -> None:
362
- super().__init__(Month(), separator=separator)
363
-
364
-
365
556
  class FrozenSetStrs(FrozenSetParameter[StringParamType, str]):
366
557
  """A frozenset-of-strs-valued parameter."""
367
558
 
559
+ @override
368
560
  def __init__(self, *, separator: str = ",") -> None:
369
561
  super().__init__(StringParamType(), separator=separator)
370
562
 
371
563
 
372
- class FrozenSetUUIDs(FrozenSetParameter[UUIDParameterType, UUID]):
373
- """A frozenset-of-UUIDs-valued parameter."""
374
-
375
- def __init__(self, *, separator: str = ",") -> None:
376
- super().__init__(UUIDParameterType(), separator=separator)
377
-
378
-
379
564
  # parameters - list
380
565
 
381
566
 
382
- class ListParameter(ParamType, Generic[_TParam, _T]):
567
+ class ListParameter[P: ParamType, T](ParamType):
383
568
  """A list-valued parameter."""
384
569
 
385
- def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
386
- self.name = f"LIST[{param.name}]"
570
+ @override
571
+ def __init__(self, param: P, /, *, separator: str = ",") -> None:
572
+ self.name = f"list[{param.name}]"
387
573
  self._param = param
388
574
  self._separator = separator
389
575
  super().__init__()
390
576
 
391
577
  @override
392
578
  def __repr__(self) -> str:
393
- desc = repr(self._param)
394
- return f"LIST[{desc}]"
579
+ return f"LIST[{self._param!r}]"
395
580
 
396
581
  @override
397
582
  def convert(
398
- self,
399
- value: MaybeStr[Iterable[_T]],
400
- param: Parameter | None,
401
- ctx: Context | None,
402
- ) -> list[_T]:
583
+ self, value: MaybeStr[Iterable[T]], param: Parameter | None, ctx: Context | None
584
+ ) -> list[T]:
403
585
  """Convert a value into the `List` type."""
404
586
  if is_iterable_not_str(value):
405
587
  return list(value)
@@ -421,69 +603,49 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
421
603
  return _make_metavar(param, desc)
422
604
 
423
605
 
424
- class ListBools(ListParameter[BoolParamType, str]):
425
- """A list-of-bools-valued parameter."""
426
-
427
- def __init__(self, *, separator: str = ",") -> None:
428
- super().__init__(BoolParamType(), separator=separator)
429
-
430
-
431
- class ListDates(ListParameter[Date, dt.date]):
432
- """A list-of-dates-valued parameter."""
433
-
434
- def __init__(self, *, separator: str = ",") -> None:
435
- super().__init__(Date(), separator=separator)
436
-
437
-
438
- class ListEnums(ListParameter[Enum[TEnum], TEnum]):
439
- """A list-of-enums-valued parameter."""
606
+ class ListChoices(ListParameter[Choice, str]):
607
+ """A frozenset-of-choices-valued parameter."""
440
608
 
609
+ @override
441
610
  def __init__(
442
611
  self,
443
- enum: type[TEnum],
612
+ choices: list[str],
444
613
  /,
445
614
  *,
446
615
  case_sensitive: bool = False,
447
616
  separator: str = ",",
448
617
  ) -> None:
449
- super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
618
+ super().__init__(
619
+ Choice(choices, case_sensitive=case_sensitive), separator=separator
620
+ )
450
621
 
451
622
 
452
- class ListFloats(ListParameter[FloatParamType, float]):
453
- """A list-of-floats-valued parameter."""
623
+ class ListEnums[E: enum.Enum](ListParameter[Enum[E], E]):
624
+ """A list-of-enums-valued parameter."""
454
625
 
455
- def __init__(self, *, separator: str = ",") -> None:
456
- super().__init__(FloatParamType(), separator=separator)
626
+ @override
627
+ def __init__(
628
+ self, enum: type[E], /, *, case_sensitive: bool = False, separator: str = ","
629
+ ) -> None:
630
+ super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
457
631
 
458
632
 
459
633
  class ListInts(ListParameter[IntParamType, int]):
460
634
  """A list-of-ints-valued parameter."""
461
635
 
636
+ @override
462
637
  def __init__(self, *, separator: str = ",") -> None:
463
638
  super().__init__(IntParamType(), separator=separator)
464
639
 
465
640
 
466
- class ListMonths(ListParameter[Month, utilities.datetime.Month]):
467
- """A list-of-months-valued parameter."""
468
-
469
- def __init__(self, *, separator: str = ",") -> None:
470
- super().__init__(Month(), separator=separator)
471
-
472
-
473
641
  class ListStrs(ListParameter[StringParamType, str]):
474
642
  """A list-of-strs-valued parameter."""
475
643
 
644
+ @override
476
645
  def __init__(self, *, separator: str = ",") -> None:
477
646
  super().__init__(StringParamType(), separator=separator)
478
647
 
479
648
 
480
- class ListUUIDs(ListParameter[UUIDParameterType, UUID]):
481
- """A list-of-UUIDs-valued parameter."""
482
-
483
- def __init__(self, *, separator: str = ",") -> None:
484
- super().__init__(UUIDParameterType(), separator=separator)
485
-
486
-
487
649
  # private
488
650
 
489
651
 
@@ -493,36 +655,30 @@ def _make_metavar(param: Parameter, desc: str, /) -> str:
493
655
 
494
656
 
495
657
  __all__ = [
496
- "CONTEXT_SETTINGS_HELP_OPTION_NAMES",
658
+ "CONTEXT_SETTINGS",
659
+ "UUID",
497
660
  "Date",
498
- "DirPath",
499
- "Duration",
661
+ "DateDelta",
662
+ "DateTimeDelta",
500
663
  "Enum",
501
- "ExistingDirPath",
502
- "ExistingFilePath",
503
- "FilePath",
504
- "FrozenSetBools",
664
+ "EnumPartial",
505
665
  "FrozenSetChoices",
506
- "FrozenSetDates",
507
666
  "FrozenSetEnums",
508
- "FrozenSetFloats",
509
- "FrozenSetInts",
510
- "FrozenSetMonths",
511
667
  "FrozenSetParameter",
512
668
  "FrozenSetStrs",
513
- "FrozenSetUUIDs",
514
- "ListBools",
515
- "ListDates",
669
+ "IPv4Address",
670
+ "IPv6Address",
671
+ "ListChoices",
516
672
  "ListEnums",
517
- "ListFloats",
518
673
  "ListInts",
519
- "ListMonths",
520
674
  "ListParameter",
521
675
  "ListStrs",
522
- "ListUUIDs",
523
- "Month",
676
+ "MonthDay",
677
+ "Path",
678
+ "Path",
524
679
  "PlainDateTime",
525
680
  "Time",
526
- "Timedelta",
681
+ "TimeDelta",
682
+ "YearMonth",
527
683
  "ZonedDateTime",
528
684
  ]