dycw-utilities 0.131.15__py3-none-any.whl → 0.131.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.
- {dycw_utilities-0.131.15.dist-info → dycw_utilities-0.131.17.dist-info}/METADATA +1 -1
- {dycw_utilities-0.131.15.dist-info → dycw_utilities-0.131.17.dist-info}/RECORD +16 -17
- utilities/__init__.py +1 -1
- utilities/asyncio.py +47 -60
- utilities/click.py +128 -179
- utilities/hypothesis.py +57 -19
- utilities/pottery.py +16 -19
- utilities/psutil.py +8 -7
- utilities/redis.py +108 -116
- utilities/slack_sdk.py +17 -18
- utilities/sqlalchemy.py +36 -30
- utilities/sqlalchemy_polars.py +12 -27
- utilities/whenever.py +1 -192
- utilities/whenever2.py +209 -8
- utilities/tenacity.py +0 -145
- {dycw_utilities-0.131.15.dist-info → dycw_utilities-0.131.17.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.15.dist-info → dycw_utilities-0.131.17.dist-info}/licenses/LICENSE +0 -0
utilities/click.py
CHANGED
@@ -1,40 +1,38 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import datetime as dt
|
4
3
|
import pathlib
|
5
|
-
from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar, override
|
6
|
-
from uuid import UUID
|
4
|
+
from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar, assert_never, override
|
7
5
|
|
8
6
|
import click
|
7
|
+
import whenever
|
9
8
|
from click import Choice, Context, Parameter, ParamType
|
10
|
-
from click.types import
|
11
|
-
BoolParamType,
|
12
|
-
FloatParamType,
|
13
|
-
IntParamType,
|
14
|
-
StringParamType,
|
15
|
-
UUIDParameterType,
|
16
|
-
)
|
9
|
+
from click.types import StringParamType
|
17
10
|
|
18
|
-
|
19
|
-
import utilities.types
|
20
|
-
from utilities.datetime import EnsureMonthError, MonthLike, ensure_month
|
11
|
+
from utilities.datetime import EnsureMonthError, ensure_month
|
21
12
|
from utilities.enum import EnsureEnumError, ensure_enum
|
22
13
|
from utilities.functions import EnsureStrError, ensure_str, get_class_name
|
23
14
|
from utilities.iterables import is_iterable_not_str
|
24
15
|
from utilities.text import split_str
|
25
16
|
from utilities.types import (
|
26
|
-
|
17
|
+
DateDeltaLike,
|
18
|
+
DateLike,
|
19
|
+
DateTimeDeltaLike,
|
27
20
|
EnumLike,
|
28
21
|
MaybeStr,
|
29
|
-
|
30
|
-
PyTimeDeltaLike,
|
31
|
-
PyTimeLike,
|
22
|
+
PlainDateTimeLike,
|
32
23
|
TEnum,
|
24
|
+
TimeDeltaLike,
|
25
|
+
TimeLike,
|
26
|
+
ZonedDateTimeLike,
|
33
27
|
)
|
34
28
|
|
35
29
|
if TYPE_CHECKING:
|
36
30
|
from collections.abc import Iterable, Sequence
|
37
31
|
|
32
|
+
import utilities.datetime
|
33
|
+
from utilities.datetime import MonthLike
|
34
|
+
|
35
|
+
|
38
36
|
_T = TypeVar("_T")
|
39
37
|
_TParam = TypeVar("_TParam", bound=ParamType)
|
40
38
|
|
@@ -76,21 +74,25 @@ class Date(ParamType):
|
|
76
74
|
|
77
75
|
@override
|
78
76
|
def convert(
|
79
|
-
self, value:
|
80
|
-
) ->
|
77
|
+
self, value: DateLike, param: Parameter | None, ctx: Context | None
|
78
|
+
) -> whenever.Date:
|
81
79
|
"""Convert a value into the `Date` type."""
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
80
|
+
match value:
|
81
|
+
case whenever.Date():
|
82
|
+
return value
|
83
|
+
case str():
|
84
|
+
try:
|
85
|
+
return whenever.Date.parse_common_iso(value)
|
86
|
+
except ValueError as error:
|
87
|
+
self.fail(str(error), param, ctx)
|
88
|
+
case _ as never:
|
89
|
+
assert_never(never)
|
88
90
|
|
89
91
|
|
90
|
-
class
|
91
|
-
"""A
|
92
|
+
class DateDelta(ParamType):
|
93
|
+
"""A date-delta-valued parameter."""
|
92
94
|
|
93
|
-
name = "
|
95
|
+
name = "date delta"
|
94
96
|
|
95
97
|
@override
|
96
98
|
def __repr__(self) -> str:
|
@@ -98,18 +100,45 @@ class Duration(ParamType):
|
|
98
100
|
|
99
101
|
@override
|
100
102
|
def convert(
|
101
|
-
self,
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
103
|
+
self, value: DateDeltaLike, param: Parameter | None, ctx: Context | None
|
104
|
+
) -> whenever.DateDelta:
|
105
|
+
"""Convert a value into the `DateDelta` type."""
|
106
|
+
match value:
|
107
|
+
case whenever.DateDelta():
|
108
|
+
return value
|
109
|
+
case str():
|
110
|
+
try:
|
111
|
+
return whenever.DateDelta.parse_common_iso(value)
|
112
|
+
except ValueError as error:
|
113
|
+
self.fail(str(error), param, ctx)
|
114
|
+
case _ as never:
|
115
|
+
assert_never(never)
|
116
|
+
|
117
|
+
|
118
|
+
class DateTimeDelta(ParamType):
|
119
|
+
"""A date-delta-valued parameter."""
|
120
|
+
|
121
|
+
name = "date-time delta"
|
108
122
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
123
|
+
@override
|
124
|
+
def __repr__(self) -> str:
|
125
|
+
return self.name.upper()
|
126
|
+
|
127
|
+
@override
|
128
|
+
def convert(
|
129
|
+
self, value: DateTimeDeltaLike, param: Parameter | None, ctx: Context | None
|
130
|
+
) -> whenever.DateTimeDelta:
|
131
|
+
"""Convert a value into the `DateTimeDelta` type."""
|
132
|
+
match value:
|
133
|
+
case whenever.DateTimeDelta():
|
134
|
+
return value
|
135
|
+
case str():
|
136
|
+
try:
|
137
|
+
return whenever.DateTimeDelta.parse_common_iso(value)
|
138
|
+
except ValueError as error:
|
139
|
+
self.fail(str(error), param, ctx)
|
140
|
+
case _ as never:
|
141
|
+
assert_never(never)
|
113
142
|
|
114
143
|
|
115
144
|
class Enum(ParamType, Generic[TEnum]):
|
@@ -167,7 +196,7 @@ class Month(ParamType):
|
|
167
196
|
class PlainDateTime(ParamType):
|
168
197
|
"""A local-datetime-valued parameter."""
|
169
198
|
|
170
|
-
name = "plain
|
199
|
+
name = "plain date-time"
|
171
200
|
|
172
201
|
@override
|
173
202
|
def __repr__(self) -> str:
|
@@ -175,15 +204,19 @@ class PlainDateTime(ParamType):
|
|
175
204
|
|
176
205
|
@override
|
177
206
|
def convert(
|
178
|
-
self, value:
|
179
|
-
) ->
|
180
|
-
"""Convert a value into the `
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
207
|
+
self, value: PlainDateTimeLike, param: Parameter | None, ctx: Context | None
|
208
|
+
) -> whenever.PlainDateTime:
|
209
|
+
"""Convert a value into the `PlainDateTime` type."""
|
210
|
+
match value:
|
211
|
+
case whenever.PlainDateTime():
|
212
|
+
return value
|
213
|
+
case str():
|
214
|
+
try:
|
215
|
+
return whenever.PlainDateTime.parse_common_iso(value)
|
216
|
+
except ValueError as error:
|
217
|
+
self.fail(str(error), param, ctx)
|
218
|
+
case _ as never:
|
219
|
+
assert_never(never)
|
187
220
|
|
188
221
|
|
189
222
|
class Time(ParamType):
|
@@ -197,21 +230,25 @@ class Time(ParamType):
|
|
197
230
|
|
198
231
|
@override
|
199
232
|
def convert(
|
200
|
-
self, value:
|
201
|
-
) ->
|
233
|
+
self, value: TimeLike, param: Parameter | None, ctx: Context | None
|
234
|
+
) -> whenever.Time:
|
202
235
|
"""Convert a value into the `Time` type."""
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
236
|
+
match value:
|
237
|
+
case whenever.Time():
|
238
|
+
return value
|
239
|
+
case str():
|
240
|
+
try:
|
241
|
+
return whenever.Time.parse_common_iso(value)
|
242
|
+
except ValueError as error:
|
243
|
+
self.fail(str(error), param, ctx)
|
244
|
+
case _ as never:
|
245
|
+
assert_never(never)
|
246
|
+
|
247
|
+
|
248
|
+
class TimeDelta(ParamType):
|
212
249
|
"""A timedelta-valued parameter."""
|
213
250
|
|
214
|
-
name = "
|
251
|
+
name = "time-delta"
|
215
252
|
|
216
253
|
@override
|
217
254
|
def __repr__(self) -> str:
|
@@ -219,21 +256,25 @@ class Timedelta(ParamType):
|
|
219
256
|
|
220
257
|
@override
|
221
258
|
def convert(
|
222
|
-
self, value:
|
223
|
-
) ->
|
224
|
-
"""Convert a value into the `
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
259
|
+
self, value: TimeDeltaLike, param: Parameter | None, ctx: Context | None
|
260
|
+
) -> whenever.TimeDelta:
|
261
|
+
"""Convert a value into the `TimeDelta` type."""
|
262
|
+
match value:
|
263
|
+
case whenever.TimeDelta():
|
264
|
+
return value
|
265
|
+
case str():
|
266
|
+
try:
|
267
|
+
return whenever.TimeDelta.parse_common_iso(value)
|
268
|
+
except ValueError as error:
|
269
|
+
self.fail(str(error), param, ctx)
|
270
|
+
case _ as never:
|
271
|
+
assert_never(never)
|
231
272
|
|
232
273
|
|
233
274
|
class ZonedDateTime(ParamType):
|
234
275
|
"""A zoned-datetime-valued parameter."""
|
235
276
|
|
236
|
-
name = "zoned
|
277
|
+
name = "zoned date-time"
|
237
278
|
|
238
279
|
@override
|
239
280
|
def __repr__(self) -> str:
|
@@ -241,15 +282,19 @@ class ZonedDateTime(ParamType):
|
|
241
282
|
|
242
283
|
@override
|
243
284
|
def convert(
|
244
|
-
self, value:
|
245
|
-
) ->
|
246
|
-
"""Convert a value into the `
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
285
|
+
self, value: ZonedDateTimeLike, param: Parameter | None, ctx: Context | None
|
286
|
+
) -> whenever.ZonedDateTime:
|
287
|
+
"""Convert a value into the `ZonedDateTime` type."""
|
288
|
+
match value:
|
289
|
+
case whenever.ZonedDateTime():
|
290
|
+
return value
|
291
|
+
case str():
|
292
|
+
try:
|
293
|
+
return whenever.ZonedDateTime.parse_common_iso(value)
|
294
|
+
except ValueError as error:
|
295
|
+
self.fail(str(error), param, ctx)
|
296
|
+
case _ as never:
|
297
|
+
assert_never(never)
|
253
298
|
|
254
299
|
|
255
300
|
# parameters - frozenset
|
@@ -297,20 +342,6 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
|
|
297
342
|
return _make_metavar(param, desc)
|
298
343
|
|
299
344
|
|
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
345
|
class FrozenSetChoices(FrozenSetParameter[Choice, str]):
|
315
346
|
"""A frozenset-of-choices-valued parameter."""
|
316
347
|
|
@@ -341,27 +372,6 @@ class FrozenSetEnums(FrozenSetParameter[Enum[TEnum], TEnum]):
|
|
341
372
|
super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
|
342
373
|
|
343
374
|
|
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
|
-
class FrozenSetInts(FrozenSetParameter[IntParamType, int]):
|
352
|
-
"""A frozenset-of-ints-valued parameter."""
|
353
|
-
|
354
|
-
def __init__(self, *, separator: str = ",") -> None:
|
355
|
-
super().__init__(IntParamType(), separator=separator)
|
356
|
-
|
357
|
-
|
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
375
|
class FrozenSetStrs(FrozenSetParameter[StringParamType, str]):
|
366
376
|
"""A frozenset-of-strs-valued parameter."""
|
367
377
|
|
@@ -369,13 +379,6 @@ class FrozenSetStrs(FrozenSetParameter[StringParamType, str]):
|
|
369
379
|
super().__init__(StringParamType(), separator=separator)
|
370
380
|
|
371
381
|
|
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
382
|
# parameters - list
|
380
383
|
|
381
384
|
|
@@ -421,20 +424,6 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
|
|
421
424
|
return _make_metavar(param, desc)
|
422
425
|
|
423
426
|
|
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
427
|
class ListEnums(ListParameter[Enum[TEnum], TEnum]):
|
439
428
|
"""A list-of-enums-valued parameter."""
|
440
429
|
|
@@ -449,27 +438,6 @@ class ListEnums(ListParameter[Enum[TEnum], TEnum]):
|
|
449
438
|
super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
|
450
439
|
|
451
440
|
|
452
|
-
class ListFloats(ListParameter[FloatParamType, float]):
|
453
|
-
"""A list-of-floats-valued parameter."""
|
454
|
-
|
455
|
-
def __init__(self, *, separator: str = ",") -> None:
|
456
|
-
super().__init__(FloatParamType(), separator=separator)
|
457
|
-
|
458
|
-
|
459
|
-
class ListInts(ListParameter[IntParamType, int]):
|
460
|
-
"""A list-of-ints-valued parameter."""
|
461
|
-
|
462
|
-
def __init__(self, *, separator: str = ",") -> None:
|
463
|
-
super().__init__(IntParamType(), separator=separator)
|
464
|
-
|
465
|
-
|
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
441
|
class ListStrs(ListParameter[StringParamType, str]):
|
474
442
|
"""A list-of-strs-valued parameter."""
|
475
443
|
|
@@ -477,13 +445,6 @@ class ListStrs(ListParameter[StringParamType, str]):
|
|
477
445
|
super().__init__(StringParamType(), separator=separator)
|
478
446
|
|
479
447
|
|
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
448
|
# private
|
488
449
|
|
489
450
|
|
@@ -495,34 +456,22 @@ def _make_metavar(param: Parameter, desc: str, /) -> str:
|
|
495
456
|
__all__ = [
|
496
457
|
"CONTEXT_SETTINGS_HELP_OPTION_NAMES",
|
497
458
|
"Date",
|
459
|
+
"DateDelta",
|
460
|
+
"DateTimeDelta",
|
498
461
|
"DirPath",
|
499
|
-
"Duration",
|
500
462
|
"Enum",
|
501
463
|
"ExistingDirPath",
|
502
464
|
"ExistingFilePath",
|
503
465
|
"FilePath",
|
504
|
-
"FrozenSetBools",
|
505
466
|
"FrozenSetChoices",
|
506
|
-
"FrozenSetDates",
|
507
467
|
"FrozenSetEnums",
|
508
|
-
"FrozenSetFloats",
|
509
|
-
"FrozenSetInts",
|
510
|
-
"FrozenSetMonths",
|
511
468
|
"FrozenSetParameter",
|
512
469
|
"FrozenSetStrs",
|
513
|
-
"FrozenSetUUIDs",
|
514
|
-
"ListBools",
|
515
|
-
"ListDates",
|
516
470
|
"ListEnums",
|
517
|
-
"ListFloats",
|
518
|
-
"ListInts",
|
519
|
-
"ListMonths",
|
520
471
|
"ListParameter",
|
521
472
|
"ListStrs",
|
522
|
-
"ListUUIDs",
|
523
|
-
"Month",
|
524
473
|
"PlainDateTime",
|
525
474
|
"Time",
|
526
|
-
"
|
475
|
+
"TimeDelta",
|
527
476
|
"ZonedDateTime",
|
528
477
|
]
|
utilities/hypothesis.py
CHANGED
@@ -48,7 +48,15 @@ from hypothesis.strategies import (
|
|
48
48
|
uuids,
|
49
49
|
)
|
50
50
|
from hypothesis.utils.conventions import not_set
|
51
|
-
from whenever import
|
51
|
+
from whenever import (
|
52
|
+
Date,
|
53
|
+
DateDelta,
|
54
|
+
DateTimeDelta,
|
55
|
+
PlainDateTime,
|
56
|
+
Time,
|
57
|
+
TimeDelta,
|
58
|
+
ZonedDateTime,
|
59
|
+
)
|
52
60
|
|
53
61
|
from utilities.datetime import (
|
54
62
|
DATETIME_MAX_NAIVE,
|
@@ -97,12 +105,19 @@ from utilities.whenever2 import (
|
|
97
105
|
DATE_DELTA_PARSABLE_MIN,
|
98
106
|
DATE_MAX,
|
99
107
|
DATE_MIN,
|
108
|
+
DATE_TIME_DELTA_MAX,
|
109
|
+
DATE_TIME_DELTA_MIN,
|
110
|
+
DATE_TIME_DELTA_PARSABLE_MAX,
|
111
|
+
DATE_TIME_DELTA_PARSABLE_MIN,
|
100
112
|
PLAIN_DATE_TIME_MAX,
|
101
113
|
PLAIN_DATE_TIME_MIN,
|
102
114
|
TIME_DELTA_MAX,
|
103
115
|
TIME_DELTA_MIN,
|
104
116
|
TIME_MAX,
|
105
117
|
TIME_MIN,
|
118
|
+
to_date_time_delta,
|
119
|
+
to_days,
|
120
|
+
to_nanos,
|
106
121
|
)
|
107
122
|
from utilities.zoneinfo import UTC, ensure_time_zone
|
108
123
|
|
@@ -199,25 +214,11 @@ def date_deltas_whenever(
|
|
199
214
|
...
|
200
215
|
case _ as never:
|
201
216
|
assert_never(never)
|
202
|
-
|
203
|
-
|
204
|
-
assert min_months == 0
|
205
|
-
max_years, max_months, max_days = max_value_.in_years_months_days()
|
206
|
-
assert max_years == 0
|
207
|
-
assert max_months == 0
|
217
|
+
min_days = to_days(min_value_)
|
218
|
+
max_days = to_days(max_value_)
|
208
219
|
if draw2(draw, parsable):
|
209
|
-
|
210
|
-
|
211
|
-
)
|
212
|
-
assert parsable_min_years == 0
|
213
|
-
assert parsable_min_months == 0
|
214
|
-
min_days = max(min_days, parsable_min_days)
|
215
|
-
parsable_max_years, parsable_max_months, parsable_max_days = (
|
216
|
-
DATE_DELTA_PARSABLE_MAX.in_years_months_days()
|
217
|
-
)
|
218
|
-
assert parsable_max_years == 0
|
219
|
-
assert parsable_max_months == 0
|
220
|
-
max_days = min(max_days, parsable_max_days)
|
220
|
+
min_days = max(min_days, to_days(DATE_DELTA_PARSABLE_MIN))
|
221
|
+
max_days = min(max_days, to_days(DATE_DELTA_PARSABLE_MAX))
|
221
222
|
days = draw(integers(min_value=min_days, max_value=max_days))
|
222
223
|
return DateDelta(days=days)
|
223
224
|
|
@@ -287,6 +288,42 @@ def _is_between_timedelta(
|
|
287
288
|
##
|
288
289
|
|
289
290
|
|
291
|
+
@composite
|
292
|
+
def date_time_deltas_whenever(
|
293
|
+
draw: DrawFn,
|
294
|
+
/,
|
295
|
+
*,
|
296
|
+
min_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
|
297
|
+
max_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
|
298
|
+
parsable: MaybeSearchStrategy[bool] = False,
|
299
|
+
) -> DateTimeDelta:
|
300
|
+
"""Strategy for generating date deltas."""
|
301
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
302
|
+
match min_value_:
|
303
|
+
case None:
|
304
|
+
min_value_ = DATE_TIME_DELTA_MIN
|
305
|
+
case DateTimeDelta():
|
306
|
+
...
|
307
|
+
case _ as never:
|
308
|
+
assert_never(never)
|
309
|
+
match max_value_:
|
310
|
+
case None:
|
311
|
+
max_value_ = DATE_TIME_DELTA_MAX
|
312
|
+
case DateTimeDelta():
|
313
|
+
...
|
314
|
+
case _ as never:
|
315
|
+
assert_never(never)
|
316
|
+
min_nanos, max_nanos = map(to_nanos, [min_value_, max_value_])
|
317
|
+
if draw2(draw, parsable):
|
318
|
+
min_nanos = max(min_nanos, to_nanos(DATE_TIME_DELTA_PARSABLE_MIN))
|
319
|
+
max_nanos = min(max_nanos, to_nanos(DATE_TIME_DELTA_PARSABLE_MAX))
|
320
|
+
nanos = draw(integers(min_value=min_nanos, max_value=max_nanos))
|
321
|
+
return to_date_time_delta(nanos)
|
322
|
+
|
323
|
+
|
324
|
+
##
|
325
|
+
|
326
|
+
|
290
327
|
@composite
|
291
328
|
def dates_two_digit_year(
|
292
329
|
draw: DrawFn,
|
@@ -1468,6 +1505,7 @@ __all__ = [
|
|
1468
1505
|
"bool_arrays",
|
1469
1506
|
"date_deltas_whenever",
|
1470
1507
|
"date_durations",
|
1508
|
+
"date_time_deltas_whenever",
|
1471
1509
|
"dates_two_digit_year",
|
1472
1510
|
"dates_whenever",
|
1473
1511
|
"datetime_durations",
|
utilities/pottery.py
CHANGED
@@ -8,14 +8,16 @@ from pottery import AIORedlock
|
|
8
8
|
from pottery.exceptions import ReleaseUnlockedLock
|
9
9
|
from redis.asyncio import Redis
|
10
10
|
|
11
|
-
from utilities.asyncio import
|
12
|
-
from utilities.datetime import MILLISECOND, SECOND, datetime_duration_to_float
|
11
|
+
from utilities.asyncio import sleep_td, timeout_td
|
13
12
|
from utilities.iterables import always_iterable
|
13
|
+
from utilities.whenever2 import MILLISECOND, SECOND
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from collections.abc import AsyncIterator, Iterable
|
17
17
|
|
18
|
-
from
|
18
|
+
from whenever import TimeDelta
|
19
|
+
|
20
|
+
from utilities.types import MaybeIterable
|
19
21
|
|
20
22
|
|
21
23
|
@asynccontextmanager
|
@@ -25,10 +27,10 @@ async def yield_access(
|
|
25
27
|
/,
|
26
28
|
*,
|
27
29
|
num: int = 1,
|
28
|
-
timeout_acquire:
|
29
|
-
timeout_release:
|
30
|
-
sleep:
|
31
|
-
throttle:
|
30
|
+
timeout_acquire: TimeDelta | None = None,
|
31
|
+
timeout_release: TimeDelta = 10 * SECOND,
|
32
|
+
sleep: TimeDelta = MILLISECOND,
|
33
|
+
throttle: TimeDelta | None = None,
|
32
34
|
) -> AsyncIterator[None]:
|
33
35
|
"""Acquire access to a locked resource, amongst 1 of multiple connections."""
|
34
36
|
if num <= 0:
|
@@ -36,14 +38,11 @@ async def yield_access(
|
|
36
38
|
masters = ( # skipif-ci-and-not-linux
|
37
39
|
{redis} if isinstance(redis, Redis) else set(always_iterable(redis))
|
38
40
|
)
|
39
|
-
auto_release_time = datetime_duration_to_float( # skipif-ci-and-not-linux
|
40
|
-
timeout_release
|
41
|
-
)
|
42
41
|
locks = [ # skipif-ci-and-not-linux
|
43
42
|
AIORedlock(
|
44
43
|
key=f"{key}_{i}_of_{num}",
|
45
44
|
masters=masters,
|
46
|
-
auto_release_time=
|
45
|
+
auto_release_time=timeout_release.in_seconds(),
|
47
46
|
)
|
48
47
|
for i in range(1, num + 1)
|
49
48
|
]
|
@@ -54,7 +53,7 @@ async def yield_access(
|
|
54
53
|
)
|
55
54
|
yield
|
56
55
|
finally: # skipif-ci-and-not-linux
|
57
|
-
await
|
56
|
+
await sleep_td(throttle)
|
58
57
|
if lock is not None:
|
59
58
|
with suppress(ReleaseUnlockedLock):
|
60
59
|
await lock.release()
|
@@ -66,20 +65,18 @@ async def _get_first_available_lock(
|
|
66
65
|
/,
|
67
66
|
*,
|
68
67
|
num: int = 1,
|
69
|
-
timeout:
|
70
|
-
sleep:
|
68
|
+
timeout: TimeDelta | None = None,
|
69
|
+
sleep: TimeDelta | None = None,
|
71
70
|
) -> AIORedlock:
|
72
71
|
locks = list(locks) # skipif-ci-and-not-linux
|
73
72
|
error = _YieldAccessUnableToAcquireLockError( # skipif-ci-and-not-linux
|
74
73
|
key=key, num=num, timeout=timeout
|
75
74
|
)
|
76
|
-
async with
|
77
|
-
duration=timeout, error=error
|
78
|
-
):
|
75
|
+
async with timeout_td(timeout, error=error): # skipif-ci-and-not-linux
|
79
76
|
while True:
|
80
77
|
if (result := await _get_first_available_lock_if_any(locks)) is not None:
|
81
78
|
return result
|
82
|
-
await
|
79
|
+
await sleep_td(sleep)
|
83
80
|
|
84
81
|
|
85
82
|
async def _get_first_available_lock_if_any(
|
@@ -106,7 +103,7 @@ class _YieldAccessNumLocksError(YieldAccessError):
|
|
106
103
|
|
107
104
|
@dataclass(kw_only=True, slots=True)
|
108
105
|
class _YieldAccessUnableToAcquireLockError(YieldAccessError):
|
109
|
-
timeout:
|
106
|
+
timeout: TimeDelta | None
|
110
107
|
|
111
108
|
@override
|
112
109
|
def __str__(self) -> str:
|