dycw-utilities 0.131.18__py3-none-any.whl → 0.131.19__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.18.dist-info → dycw_utilities-0.131.19.dist-info}/METADATA +1 -1
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.19.dist-info}/RECORD +15 -16
- utilities/__init__.py +1 -1
- utilities/datetime.py +3 -906
- utilities/fastapi.py +5 -7
- utilities/functions.py +78 -45
- utilities/hypothesis.py +20 -269
- utilities/polars.py +6 -3
- utilities/pytest.py +83 -63
- utilities/types.py +3 -24
- utilities/typing.py +2 -15
- utilities/whenever2.py +147 -3
- utilities/zoneinfo.py +4 -0
- utilities/whenever.py +0 -230
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.19.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.19.dist-info}/licenses/LICENSE +0 -0
utilities/fastapi.py
CHANGED
@@ -8,17 +8,16 @@ from fastapi import FastAPI
|
|
8
8
|
from uvicorn import Config, Server
|
9
9
|
|
10
10
|
from utilities.asyncio import Looper
|
11
|
-
from utilities.
|
12
|
-
from utilities.whenever2 import get_now_local
|
11
|
+
from utilities.whenever2 import SECOND, get_now_local
|
13
12
|
|
14
13
|
if TYPE_CHECKING:
|
15
14
|
from types import TracebackType
|
16
15
|
|
17
|
-
from
|
16
|
+
from whenever import TimeDelta
|
18
17
|
|
19
18
|
|
20
19
|
_LOCALHOST: str = "localhost"
|
21
|
-
_TIMEOUT:
|
20
|
+
_TIMEOUT: TimeDelta = SECOND
|
22
21
|
|
23
22
|
|
24
23
|
class _PingerReceiverApp(FastAPI):
|
@@ -71,16 +70,15 @@ class PingReceiver(Looper[None]):
|
|
71
70
|
|
72
71
|
@classmethod
|
73
72
|
async def ping(
|
74
|
-
cls, port: int, /, *, host: str = _LOCALHOST, timeout:
|
73
|
+
cls, port: int, /, *, host: str = _LOCALHOST, timeout: TimeDelta = _TIMEOUT
|
75
74
|
) -> str | Literal[False]:
|
76
75
|
"""Ping the receiver."""
|
77
76
|
from httpx import AsyncClient, ConnectError # skipif-ci
|
78
77
|
|
79
78
|
url = f"http://{host}:{port}/ping" # skipif-ci
|
80
|
-
timeout_use = datetime_duration_to_float(timeout) # skipif-ci
|
81
79
|
try: # skipif-ci
|
82
80
|
async with AsyncClient() as client:
|
83
|
-
response = await client.get(url, timeout=
|
81
|
+
response = await client.get(url, timeout=timeout.in_seconds())
|
84
82
|
except ConnectError: # skipif-ci
|
85
83
|
return False
|
86
84
|
return response.text if response.status_code == 200 else False # skipif-ci
|
utilities/functions.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import datetime as dt
|
4
3
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
5
4
|
from dataclasses import asdict, dataclass, is_dataclass
|
6
5
|
from functools import _lru_cache_wrapper, cached_property, partial, reduce, wraps
|
@@ -28,6 +27,7 @@ from typing import (
|
|
28
27
|
)
|
29
28
|
|
30
29
|
from typing_extensions import ParamSpec
|
30
|
+
from whenever import Date, PlainDateTime, Time, TimeDelta, ZonedDateTime
|
31
31
|
|
32
32
|
from utilities.reprlib import get_repr, get_repr_and_class
|
33
33
|
from utilities.sentinel import Sentinel, sentinel
|
@@ -210,13 +210,13 @@ class EnsureClassError(Exception):
|
|
210
210
|
|
211
211
|
|
212
212
|
@overload
|
213
|
-
def ensure_date(obj: Any, /, *, nullable: bool) ->
|
213
|
+
def ensure_date(obj: Any, /, *, nullable: bool) -> Date | None: ...
|
214
214
|
@overload
|
215
|
-
def ensure_date(obj: Any, /, *, nullable: Literal[False] = False) ->
|
216
|
-
def ensure_date(obj: Any, /, *, nullable: bool = False) ->
|
215
|
+
def ensure_date(obj: Any, /, *, nullable: Literal[False] = False) -> Date: ...
|
216
|
+
def ensure_date(obj: Any, /, *, nullable: bool = False) -> Date | None:
|
217
217
|
"""Ensure an object is a date."""
|
218
218
|
try:
|
219
|
-
return ensure_class(obj,
|
219
|
+
return ensure_class(obj, Date, nullable=nullable)
|
220
220
|
except EnsureClassError as error:
|
221
221
|
raise EnsureDateError(obj=error.obj, nullable=nullable) from None
|
222
222
|
|
@@ -234,33 +234,6 @@ class EnsureDateError(Exception):
|
|
234
234
|
##
|
235
235
|
|
236
236
|
|
237
|
-
@overload
|
238
|
-
def ensure_datetime(obj: Any, /, *, nullable: bool) -> dt.datetime | None: ...
|
239
|
-
@overload
|
240
|
-
def ensure_datetime(
|
241
|
-
obj: Any, /, *, nullable: Literal[False] = False
|
242
|
-
) -> dt.datetime: ...
|
243
|
-
def ensure_datetime(obj: Any, /, *, nullable: bool = False) -> dt.datetime | None:
|
244
|
-
"""Ensure an object is a datetime."""
|
245
|
-
try:
|
246
|
-
return ensure_class(obj, dt.datetime, nullable=nullable)
|
247
|
-
except EnsureClassError as error:
|
248
|
-
raise EnsureDateTimeError(obj=error.obj, nullable=nullable) from None
|
249
|
-
|
250
|
-
|
251
|
-
@dataclass(kw_only=True, slots=True)
|
252
|
-
class EnsureDateTimeError(Exception):
|
253
|
-
obj: Any
|
254
|
-
nullable: bool
|
255
|
-
|
256
|
-
@override
|
257
|
-
def __str__(self) -> str:
|
258
|
-
return _make_error_msg(self.obj, "a datetime", nullable=self.nullable)
|
259
|
-
|
260
|
-
|
261
|
-
##
|
262
|
-
|
263
|
-
|
264
237
|
@overload
|
265
238
|
def ensure_float(obj: Any, /, *, nullable: bool) -> float | None: ...
|
266
239
|
@overload
|
@@ -432,6 +405,35 @@ class EnsurePathError(Exception):
|
|
432
405
|
##
|
433
406
|
|
434
407
|
|
408
|
+
@overload
|
409
|
+
def ensure_plain_date_time(obj: Any, /, *, nullable: bool) -> PlainDateTime | None: ...
|
410
|
+
@overload
|
411
|
+
def ensure_plain_date_time(
|
412
|
+
obj: Any, /, *, nullable: Literal[False] = False
|
413
|
+
) -> PlainDateTime: ...
|
414
|
+
def ensure_plain_date_time(
|
415
|
+
obj: Any, /, *, nullable: bool = False
|
416
|
+
) -> PlainDateTime | None:
|
417
|
+
"""Ensure an object is a plain date-time."""
|
418
|
+
try:
|
419
|
+
return ensure_class(obj, PlainDateTime, nullable=nullable)
|
420
|
+
except EnsureClassError as error:
|
421
|
+
raise EnsurePlainDateTimeError(obj=error.obj, nullable=nullable) from None
|
422
|
+
|
423
|
+
|
424
|
+
@dataclass(kw_only=True, slots=True)
|
425
|
+
class EnsurePlainDateTimeError(Exception):
|
426
|
+
obj: Any
|
427
|
+
nullable: bool
|
428
|
+
|
429
|
+
@override
|
430
|
+
def __str__(self) -> str:
|
431
|
+
return _make_error_msg(self.obj, "a plain date-time", nullable=self.nullable)
|
432
|
+
|
433
|
+
|
434
|
+
##
|
435
|
+
|
436
|
+
|
435
437
|
def ensure_sized(obj: Any, /) -> Sized:
|
436
438
|
"""Ensure an object is sized."""
|
437
439
|
if is_sized(obj):
|
@@ -496,13 +498,13 @@ class EnsureStrError(Exception):
|
|
496
498
|
|
497
499
|
|
498
500
|
@overload
|
499
|
-
def ensure_time(obj: Any, /, *, nullable: bool) ->
|
501
|
+
def ensure_time(obj: Any, /, *, nullable: bool) -> Time | None: ...
|
500
502
|
@overload
|
501
|
-
def ensure_time(obj: Any, /, *, nullable: Literal[False] = False) ->
|
502
|
-
def ensure_time(obj: Any, /, *, nullable: bool = False) ->
|
503
|
+
def ensure_time(obj: Any, /, *, nullable: Literal[False] = False) -> Time: ...
|
504
|
+
def ensure_time(obj: Any, /, *, nullable: bool = False) -> Time | None:
|
503
505
|
"""Ensure an object is a time."""
|
504
506
|
try:
|
505
|
-
return ensure_class(obj,
|
507
|
+
return ensure_class(obj, Time, nullable=nullable)
|
506
508
|
except EnsureClassError as error:
|
507
509
|
raise EnsureTimeError(obj=error.obj, nullable=nullable) from None
|
508
510
|
|
@@ -521,15 +523,15 @@ class EnsureTimeError(Exception):
|
|
521
523
|
|
522
524
|
|
523
525
|
@overload
|
524
|
-
def
|
526
|
+
def ensure_time_delta(obj: Any, /, *, nullable: bool) -> TimeDelta | None: ...
|
525
527
|
@overload
|
526
|
-
def
|
528
|
+
def ensure_time_delta(
|
527
529
|
obj: Any, /, *, nullable: Literal[False] = False
|
528
|
-
) ->
|
529
|
-
def
|
530
|
+
) -> TimeDelta: ...
|
531
|
+
def ensure_time_delta(obj: Any, /, *, nullable: bool = False) -> TimeDelta | None:
|
530
532
|
"""Ensure an object is a timedelta."""
|
531
533
|
try:
|
532
|
-
return ensure_class(obj,
|
534
|
+
return ensure_class(obj, TimeDelta, nullable=nullable)
|
533
535
|
except EnsureClassError as error:
|
534
536
|
raise EnsureTimeDeltaError(obj=error.obj, nullable=nullable) from None
|
535
537
|
|
@@ -541,7 +543,36 @@ class EnsureTimeDeltaError(Exception):
|
|
541
543
|
|
542
544
|
@override
|
543
545
|
def __str__(self) -> str:
|
544
|
-
return _make_error_msg(self.obj, "a
|
546
|
+
return _make_error_msg(self.obj, "a time-delta", nullable=self.nullable)
|
547
|
+
|
548
|
+
|
549
|
+
##
|
550
|
+
|
551
|
+
|
552
|
+
@overload
|
553
|
+
def ensure_zoned_date_time(obj: Any, /, *, nullable: bool) -> ZonedDateTime | None: ...
|
554
|
+
@overload
|
555
|
+
def ensure_zoned_date_time(
|
556
|
+
obj: Any, /, *, nullable: Literal[False] = False
|
557
|
+
) -> ZonedDateTime: ...
|
558
|
+
def ensure_zoned_date_time(
|
559
|
+
obj: Any, /, *, nullable: bool = False
|
560
|
+
) -> ZonedDateTime | None:
|
561
|
+
"""Ensure an object is a zoned date-time."""
|
562
|
+
try:
|
563
|
+
return ensure_class(obj, ZonedDateTime, nullable=nullable)
|
564
|
+
except EnsureClassError as error:
|
565
|
+
raise EnsureZonedDateTimeError(obj=error.obj, nullable=nullable) from None
|
566
|
+
|
567
|
+
|
568
|
+
@dataclass(kw_only=True, slots=True)
|
569
|
+
class EnsureZonedDateTimeError(Exception):
|
570
|
+
obj: Any
|
571
|
+
nullable: bool
|
572
|
+
|
573
|
+
@override
|
574
|
+
def __str__(self) -> str:
|
575
|
+
return _make_error_msg(self.obj, "a zoned date-time", nullable=self.nullable)
|
545
576
|
|
546
577
|
|
547
578
|
##
|
@@ -988,7 +1019,6 @@ __all__ = [
|
|
988
1019
|
"EnsureBytesError",
|
989
1020
|
"EnsureClassError",
|
990
1021
|
"EnsureDateError",
|
991
|
-
"EnsureDateTimeError",
|
992
1022
|
"EnsureFloatError",
|
993
1023
|
"EnsureHashableError",
|
994
1024
|
"EnsureIntError",
|
@@ -996,11 +1026,13 @@ __all__ = [
|
|
996
1026
|
"EnsureNotNoneError",
|
997
1027
|
"EnsureNumberError",
|
998
1028
|
"EnsurePathError",
|
1029
|
+
"EnsurePlainDateTimeError",
|
999
1030
|
"EnsureSizedError",
|
1000
1031
|
"EnsureSizedNotStrError",
|
1001
1032
|
"EnsureStrError",
|
1002
1033
|
"EnsureTimeDeltaError",
|
1003
1034
|
"EnsureTimeError",
|
1035
|
+
"EnsureZonedDateTimeError",
|
1004
1036
|
"MaxNullableError",
|
1005
1037
|
"MinNullableError",
|
1006
1038
|
"apply_decorators",
|
@@ -1008,7 +1040,6 @@ __all__ = [
|
|
1008
1040
|
"ensure_bytes",
|
1009
1041
|
"ensure_class",
|
1010
1042
|
"ensure_date",
|
1011
|
-
"ensure_datetime",
|
1012
1043
|
"ensure_float",
|
1013
1044
|
"ensure_hashable",
|
1014
1045
|
"ensure_int",
|
@@ -1016,11 +1047,13 @@ __all__ = [
|
|
1016
1047
|
"ensure_not_none",
|
1017
1048
|
"ensure_number",
|
1018
1049
|
"ensure_path",
|
1050
|
+
"ensure_plain_date_time",
|
1019
1051
|
"ensure_sized",
|
1020
1052
|
"ensure_sized_not_str",
|
1021
1053
|
"ensure_str",
|
1022
1054
|
"ensure_time",
|
1023
|
-
"
|
1055
|
+
"ensure_time_delta",
|
1056
|
+
"ensure_zoned_date_time",
|
1024
1057
|
"first",
|
1025
1058
|
"get_class",
|
1026
1059
|
"get_class_name",
|
utilities/hypothesis.py
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import builtins
|
4
|
-
import datetime as dt
|
5
4
|
from contextlib import contextmanager
|
6
5
|
from dataclasses import dataclass
|
7
|
-
from datetime import timezone
|
8
6
|
from enum import Enum, auto
|
9
|
-
from functools import partial
|
10
7
|
from math import ceil, floor, inf, isclose, isfinite, nan
|
11
8
|
from os import environ
|
12
9
|
from pathlib import Path
|
@@ -44,7 +41,6 @@ from hypothesis.strategies import (
|
|
44
41
|
sampled_from,
|
45
42
|
sets,
|
46
43
|
text,
|
47
|
-
timedeltas,
|
48
44
|
uuids,
|
49
45
|
)
|
50
46
|
from hypothesis.utils.conventions import not_set
|
@@ -53,30 +49,23 @@ from whenever import (
|
|
53
49
|
DateDelta,
|
54
50
|
DateTimeDelta,
|
55
51
|
PlainDateTime,
|
52
|
+
RepeatedTime,
|
53
|
+
SkippedTime,
|
56
54
|
Time,
|
57
55
|
TimeDelta,
|
56
|
+
TimeZoneNotFoundError,
|
58
57
|
ZonedDateTime,
|
59
58
|
)
|
60
59
|
|
61
60
|
from utilities.datetime import (
|
62
|
-
DATETIME_MAX_NAIVE,
|
63
|
-
DATETIME_MAX_UTC,
|
64
|
-
DATETIME_MIN_NAIVE,
|
65
|
-
DATETIME_MIN_UTC,
|
66
|
-
DAY,
|
67
61
|
MAX_DATE_TWO_DIGIT_YEAR,
|
68
62
|
MAX_MONTH,
|
69
63
|
MIN_DATE_TWO_DIGIT_YEAR,
|
70
64
|
MIN_MONTH,
|
71
65
|
Month,
|
72
|
-
date_duration_to_int,
|
73
|
-
date_duration_to_timedelta,
|
74
66
|
date_to_month,
|
75
|
-
datetime_duration_to_float,
|
76
|
-
datetime_duration_to_timedelta,
|
77
|
-
round_datetime,
|
78
67
|
)
|
79
|
-
from utilities.functions import ensure_int, ensure_str
|
68
|
+
from utilities.functions import ensure_int, ensure_str
|
80
69
|
from utilities.math import (
|
81
70
|
MAX_FLOAT32,
|
82
71
|
MAX_FLOAT64,
|
@@ -109,6 +98,7 @@ from utilities.whenever2 import (
|
|
109
98
|
DATE_TIME_DELTA_MIN,
|
110
99
|
DATE_TIME_DELTA_PARSABLE_MAX,
|
111
100
|
DATE_TIME_DELTA_PARSABLE_MIN,
|
101
|
+
DAY,
|
112
102
|
PLAIN_DATE_TIME_MAX,
|
113
103
|
PLAIN_DATE_TIME_MIN,
|
114
104
|
TIME_DELTA_MAX,
|
@@ -122,14 +112,14 @@ from utilities.whenever2 import (
|
|
122
112
|
from utilities.zoneinfo import UTC, ensure_time_zone
|
123
113
|
|
124
114
|
if TYPE_CHECKING:
|
125
|
-
|
126
|
-
from
|
115
|
+
import datetime as dt
|
116
|
+
from collections.abc import Collection, Hashable, Iterable, Iterator
|
127
117
|
|
128
118
|
from hypothesis.database import ExampleDatabase
|
129
119
|
from numpy.random import RandomState
|
130
120
|
|
131
121
|
from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
|
132
|
-
from utilities.types import
|
122
|
+
from utilities.types import Number, TimeZoneLike
|
133
123
|
|
134
124
|
|
135
125
|
_T = TypeVar("_T")
|
@@ -226,68 +216,6 @@ def date_deltas_whenever(
|
|
226
216
|
##
|
227
217
|
|
228
218
|
|
229
|
-
@composite
|
230
|
-
def date_durations(
|
231
|
-
draw: DrawFn,
|
232
|
-
/,
|
233
|
-
*,
|
234
|
-
min_int: MaybeSearchStrategy[int | None] = None,
|
235
|
-
max_int: MaybeSearchStrategy[int | None] = None,
|
236
|
-
min_timedelta: MaybeSearchStrategy[dt.timedelta | None] = None,
|
237
|
-
max_timedelta: MaybeSearchStrategy[dt.timedelta | None] = None,
|
238
|
-
two_way: bool = False,
|
239
|
-
) -> Duration:
|
240
|
-
"""Strategy for generating datetime durations."""
|
241
|
-
min_int_, max_int_ = [draw2(draw, v) for v in [min_int, max_int]]
|
242
|
-
min_timedelta_, max_timedelta_ = [
|
243
|
-
draw2(draw, v) for v in [min_timedelta, max_timedelta]
|
244
|
-
]
|
245
|
-
min_parts: Sequence[dt.timedelta | None] = [dt.timedelta.min, min_timedelta_]
|
246
|
-
if min_int_ is not None:
|
247
|
-
with assume_does_not_raise(OverflowError):
|
248
|
-
min_parts.append(date_duration_to_timedelta(min_int_))
|
249
|
-
if two_way:
|
250
|
-
from utilities.whenever import MIN_SERIALIZABLE_TIMEDELTA
|
251
|
-
|
252
|
-
min_parts.append(MIN_SERIALIZABLE_TIMEDELTA)
|
253
|
-
min_timedelta_use = max_nullable(min_parts)
|
254
|
-
max_parts: Sequence[dt.timedelta | None] = [dt.timedelta.max, max_timedelta_]
|
255
|
-
if max_int_ is not None:
|
256
|
-
with assume_does_not_raise(OverflowError):
|
257
|
-
max_parts.append(date_duration_to_timedelta(max_int_))
|
258
|
-
if two_way:
|
259
|
-
from utilities.whenever import MAX_SERIALIZABLE_TIMEDELTA
|
260
|
-
|
261
|
-
max_parts.append(MAX_SERIALIZABLE_TIMEDELTA)
|
262
|
-
max_timedelta_use = min_nullable(max_parts)
|
263
|
-
_ = assume(min_timedelta_use <= max_timedelta_use)
|
264
|
-
st_timedeltas = (
|
265
|
-
timedeltas(min_value=min_timedelta_use, max_value=max_timedelta_use)
|
266
|
-
.map(_round_timedelta)
|
267
|
-
.filter(
|
268
|
-
partial(
|
269
|
-
_is_between_timedelta, min_=min_timedelta_use, max_=max_timedelta_use
|
270
|
-
)
|
271
|
-
)
|
272
|
-
)
|
273
|
-
st_integers = st_timedeltas.map(date_duration_to_int)
|
274
|
-
st_floats = st_integers.map(float)
|
275
|
-
return draw(st_integers | st_floats | st_timedeltas)
|
276
|
-
|
277
|
-
|
278
|
-
def _round_timedelta(timedelta: dt.timedelta, /) -> dt.timedelta:
|
279
|
-
return dt.timedelta(days=timedelta.days)
|
280
|
-
|
281
|
-
|
282
|
-
def _is_between_timedelta(
|
283
|
-
timedelta: dt.timedelta, /, *, min_: dt.timedelta, max_: dt.timedelta
|
284
|
-
) -> bool:
|
285
|
-
return min_ <= timedelta <= max_
|
286
|
-
|
287
|
-
|
288
|
-
##
|
289
|
-
|
290
|
-
|
291
219
|
@composite
|
292
220
|
def date_time_deltas_whenever(
|
293
221
|
draw: DrawFn,
|
@@ -375,53 +303,6 @@ def dates_whenever(
|
|
375
303
|
##
|
376
304
|
|
377
305
|
|
378
|
-
@composite
|
379
|
-
def datetime_durations(
|
380
|
-
draw: DrawFn,
|
381
|
-
/,
|
382
|
-
*,
|
383
|
-
min_number: MaybeSearchStrategy[Number | None] = None,
|
384
|
-
max_number: MaybeSearchStrategy[Number | None] = None,
|
385
|
-
min_timedelta: MaybeSearchStrategy[dt.timedelta | None] = None,
|
386
|
-
max_timedelta: MaybeSearchStrategy[dt.timedelta | None] = None,
|
387
|
-
two_way: bool = False,
|
388
|
-
) -> Duration:
|
389
|
-
"""Strategy for generating datetime durations."""
|
390
|
-
min_number_, max_number_ = [draw2(draw, v) for v in [min_number, max_number]]
|
391
|
-
min_timedelta_, max_timedelta_ = [
|
392
|
-
draw2(draw, v) for v in [min_timedelta, max_timedelta]
|
393
|
-
]
|
394
|
-
min_parts = [min_timedelta_, dt.timedelta.min]
|
395
|
-
if min_number_ is not None:
|
396
|
-
with assume_does_not_raise(OverflowError):
|
397
|
-
min_parts.append(datetime_duration_to_timedelta(min_number_))
|
398
|
-
if two_way:
|
399
|
-
from utilities.whenever import MIN_SERIALIZABLE_TIMEDELTA
|
400
|
-
|
401
|
-
min_parts.append(MIN_SERIALIZABLE_TIMEDELTA)
|
402
|
-
min_timedelta_use = max_nullable(min_parts)
|
403
|
-
max_parts = [max_timedelta_, dt.timedelta.max]
|
404
|
-
if max_number_ is not None:
|
405
|
-
with assume_does_not_raise(OverflowError):
|
406
|
-
max_parts.append(datetime_duration_to_timedelta(max_number_))
|
407
|
-
if two_way:
|
408
|
-
from utilities.whenever import MAX_SERIALIZABLE_TIMEDELTA
|
409
|
-
|
410
|
-
max_parts.append(MAX_SERIALIZABLE_TIMEDELTA)
|
411
|
-
max_timedelta_use = min_nullable(max_parts)
|
412
|
-
_ = assume(min_timedelta_use <= max_timedelta_use)
|
413
|
-
min_float_use, max_float_use = map(
|
414
|
-
datetime_duration_to_float, [min_timedelta_use, max_timedelta_use]
|
415
|
-
)
|
416
|
-
_ = assume(min_float_use <= max_float_use)
|
417
|
-
st_numbers = numbers(min_value=min_float_use, max_value=max_float_use)
|
418
|
-
st_timedeltas = timedeltas(min_value=min_timedelta_use, max_value=max_timedelta_use)
|
419
|
-
return draw(st_numbers | st_timedeltas)
|
420
|
-
|
421
|
-
|
422
|
-
##
|
423
|
-
|
424
|
-
|
425
306
|
@overload
|
426
307
|
def draw2(
|
427
308
|
data_or_draw: DataObject | DrawFn,
|
@@ -853,43 +734,6 @@ def paths() -> SearchStrategy[Path]:
|
|
853
734
|
##
|
854
735
|
|
855
736
|
|
856
|
-
@composite
|
857
|
-
def plain_datetimes(
|
858
|
-
draw: DrawFn,
|
859
|
-
/,
|
860
|
-
*,
|
861
|
-
min_value: MaybeSearchStrategy[dt.datetime] = DATETIME_MIN_NAIVE,
|
862
|
-
max_value: MaybeSearchStrategy[dt.datetime] = DATETIME_MAX_NAIVE,
|
863
|
-
round_: MathRoundMode | None = None,
|
864
|
-
timedelta: dt.timedelta | None = None,
|
865
|
-
rel_tol: float | None = None,
|
866
|
-
abs_tol: float | None = None,
|
867
|
-
) -> dt.datetime:
|
868
|
-
"""Strategy for generating plain datetimes."""
|
869
|
-
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
870
|
-
datetime = draw(datetimes(min_value=min_value_, max_value=max_value_))
|
871
|
-
if round_ is not None:
|
872
|
-
if timedelta is None:
|
873
|
-
raise PlainDateTimesError(round_=round_)
|
874
|
-
datetime = round_datetime(
|
875
|
-
datetime, timedelta, mode=round_, rel_tol=rel_tol, abs_tol=abs_tol
|
876
|
-
)
|
877
|
-
_ = assume(min_value_ <= datetime <= max_value_)
|
878
|
-
return datetime
|
879
|
-
|
880
|
-
|
881
|
-
@dataclass(kw_only=True, slots=True)
|
882
|
-
class PlainDateTimesError(Exception):
|
883
|
-
round_: MathRoundMode
|
884
|
-
|
885
|
-
@override
|
886
|
-
def __str__(self) -> str:
|
887
|
-
return "Rounding requires a timedelta; got None"
|
888
|
-
|
889
|
-
|
890
|
-
##
|
891
|
-
|
892
|
-
|
893
737
|
@composite
|
894
738
|
def plain_datetimes_whenever(
|
895
739
|
draw: DrawFn,
|
@@ -1266,32 +1110,6 @@ def time_deltas_whenever(
|
|
1266
1110
|
##
|
1267
1111
|
|
1268
1112
|
|
1269
|
-
@composite
|
1270
|
-
def timedeltas_2w(
|
1271
|
-
draw: DrawFn,
|
1272
|
-
/,
|
1273
|
-
*,
|
1274
|
-
min_value: MaybeSearchStrategy[dt.timedelta] = dt.timedelta.min,
|
1275
|
-
max_value: MaybeSearchStrategy[dt.timedelta] = dt.timedelta.max,
|
1276
|
-
) -> dt.timedelta:
|
1277
|
-
"""Strategy for generating timedeltas which can be se/deserialized."""
|
1278
|
-
from utilities.whenever import (
|
1279
|
-
MAX_SERIALIZABLE_TIMEDELTA,
|
1280
|
-
MIN_SERIALIZABLE_TIMEDELTA,
|
1281
|
-
)
|
1282
|
-
|
1283
|
-
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1284
|
-
return draw(
|
1285
|
-
timedeltas(
|
1286
|
-
min_value=max(min_value_, MIN_SERIALIZABLE_TIMEDELTA),
|
1287
|
-
max_value=min(max_value_, MAX_SERIALIZABLE_TIMEDELTA),
|
1288
|
-
)
|
1289
|
-
)
|
1290
|
-
|
1291
|
-
|
1292
|
-
##
|
1293
|
-
|
1294
|
-
|
1295
1113
|
@composite
|
1296
1114
|
def times_whenever(
|
1297
1115
|
draw: DrawFn,
|
@@ -1393,73 +1211,6 @@ def versions(draw: DrawFn, /, *, suffix: MaybeSearchStrategy[bool] = False) -> V
|
|
1393
1211
|
##
|
1394
1212
|
|
1395
1213
|
|
1396
|
-
@composite
|
1397
|
-
def zoned_datetimes(
|
1398
|
-
draw: DrawFn,
|
1399
|
-
/,
|
1400
|
-
*,
|
1401
|
-
min_value: MaybeSearchStrategy[dt.datetime] = DATETIME_MIN_UTC + DAY,
|
1402
|
-
max_value: MaybeSearchStrategy[dt.datetime] = DATETIME_MAX_UTC - DAY,
|
1403
|
-
time_zone: MaybeSearchStrategy[ZoneInfo | timezone] = UTC,
|
1404
|
-
round_: MathRoundMode | None = None,
|
1405
|
-
timedelta: dt.timedelta | None = None,
|
1406
|
-
rel_tol: float | None = None,
|
1407
|
-
abs_tol: float | None = None,
|
1408
|
-
valid: bool = False,
|
1409
|
-
) -> dt.datetime:
|
1410
|
-
"""Strategy for generating zoned datetimes."""
|
1411
|
-
from utilities.whenever import (
|
1412
|
-
CheckValidZonedDateTimeError,
|
1413
|
-
check_valid_zoned_datetime,
|
1414
|
-
)
|
1415
|
-
|
1416
|
-
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1417
|
-
time_zone_ = draw2(draw, time_zone)
|
1418
|
-
if min_value_.tzinfo is None:
|
1419
|
-
min_value_ = min_value_.replace(tzinfo=time_zone_)
|
1420
|
-
else:
|
1421
|
-
with assume_does_not_raise(OverflowError, match="date value out of range"):
|
1422
|
-
min_value_ = min_value_.astimezone(time_zone_)
|
1423
|
-
if max_value_.tzinfo is None:
|
1424
|
-
max_value_ = max_value_.replace(tzinfo=time_zone_)
|
1425
|
-
else:
|
1426
|
-
with assume_does_not_raise(OverflowError, match="date value out of range"):
|
1427
|
-
max_value_ = max_value_.astimezone(time_zone_)
|
1428
|
-
try:
|
1429
|
-
datetime = draw(
|
1430
|
-
plain_datetimes(
|
1431
|
-
min_value=min_value_.replace(tzinfo=None),
|
1432
|
-
max_value=max_value_.replace(tzinfo=None),
|
1433
|
-
round_=round_,
|
1434
|
-
timedelta=timedelta,
|
1435
|
-
rel_tol=rel_tol,
|
1436
|
-
abs_tol=abs_tol,
|
1437
|
-
)
|
1438
|
-
)
|
1439
|
-
except PlainDateTimesError as error:
|
1440
|
-
raise ZonedDateTimesError(round_=error.round_) from None
|
1441
|
-
datetime = datetime.replace(tzinfo=time_zone_)
|
1442
|
-
_ = assume(min_value_ <= datetime <= max_value_)
|
1443
|
-
if valid:
|
1444
|
-
with assume_does_not_raise( # skipif-ci-and-windows
|
1445
|
-
CheckValidZonedDateTimeError
|
1446
|
-
):
|
1447
|
-
check_valid_zoned_datetime(datetime)
|
1448
|
-
return datetime
|
1449
|
-
|
1450
|
-
|
1451
|
-
@dataclass(kw_only=True, slots=True)
|
1452
|
-
class ZonedDateTimesError(Exception):
|
1453
|
-
round_: MathRoundMode
|
1454
|
-
|
1455
|
-
@override
|
1456
|
-
def __str__(self) -> str:
|
1457
|
-
return "Rounding requires a timedelta; got None"
|
1458
|
-
|
1459
|
-
|
1460
|
-
##
|
1461
|
-
|
1462
|
-
|
1463
1214
|
@composite
|
1464
1215
|
def zoned_datetimes_whenever(
|
1465
1216
|
draw: DrawFn,
|
@@ -1488,27 +1239,30 @@ def zoned_datetimes_whenever(
|
|
1488
1239
|
max_value_ = max_value_.to_tz(time_zone_.key).to_plain()
|
1489
1240
|
case _ as never:
|
1490
1241
|
assert_never(never)
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1242
|
+
plain = draw(plain_datetimes_whenever(min_value=min_value_, max_value=max_value_))
|
1243
|
+
with (
|
1244
|
+
assume_does_not_raise(RepeatedTime),
|
1245
|
+
assume_does_not_raise(SkippedTime),
|
1246
|
+
assume_does_not_raise(TimeZoneNotFoundError),
|
1247
|
+
assume_does_not_raise(ValueError, match="Resulting datetime is out of range"),
|
1248
|
+
):
|
1249
|
+
zoned = plain.assume_tz(time_zone_.key, disambiguate="raise")
|
1250
|
+
with assume_does_not_raise(OverflowError, match="date value out of range"):
|
1251
|
+
if not ((DATE_MIN + DAY) <= zoned.date() <= (DATE_MAX - DAY)):
|
1252
|
+
_ = zoned.py_datetime()
|
1253
|
+
return zoned
|
1496
1254
|
|
1497
1255
|
|
1498
1256
|
__all__ = [
|
1499
1257
|
"Draw2Error",
|
1500
1258
|
"MaybeSearchStrategy",
|
1501
|
-
"PlainDateTimesError",
|
1502
1259
|
"Shape",
|
1503
|
-
"ZonedDateTimesError",
|
1504
1260
|
"assume_does_not_raise",
|
1505
1261
|
"bool_arrays",
|
1506
1262
|
"date_deltas_whenever",
|
1507
|
-
"date_durations",
|
1508
1263
|
"date_time_deltas_whenever",
|
1509
1264
|
"dates_two_digit_year",
|
1510
1265
|
"dates_whenever",
|
1511
|
-
"datetime_durations",
|
1512
1266
|
"draw2",
|
1513
1267
|
"float32s",
|
1514
1268
|
"float64s",
|
@@ -1525,7 +1279,6 @@ __all__ = [
|
|
1525
1279
|
"numbers",
|
1526
1280
|
"pairs",
|
1527
1281
|
"paths",
|
1528
|
-
"plain_datetimes",
|
1529
1282
|
"plain_datetimes_whenever",
|
1530
1283
|
"random_states",
|
1531
1284
|
"sentinels",
|
@@ -1542,12 +1295,10 @@ __all__ = [
|
|
1542
1295
|
"text_digits",
|
1543
1296
|
"text_printable",
|
1544
1297
|
"time_deltas_whenever",
|
1545
|
-
"timedeltas_2w",
|
1546
1298
|
"times_whenever",
|
1547
1299
|
"triples",
|
1548
1300
|
"uint32s",
|
1549
1301
|
"uint64s",
|
1550
1302
|
"versions",
|
1551
|
-
"zoned_datetimes",
|
1552
1303
|
"zoned_datetimes_whenever",
|
1553
1304
|
]
|
utilities/polars.py
CHANGED
@@ -1005,6 +1005,7 @@ def dataclass_to_dataframe(
|
|
1005
1005
|
*,
|
1006
1006
|
globalns: StrMapping | None = None,
|
1007
1007
|
localns: StrMapping | None = None,
|
1008
|
+
warn_name_errors: bool = False,
|
1008
1009
|
) -> DataFrame:
|
1009
1010
|
"""Convert a dataclass/es into a DataFrame."""
|
1010
1011
|
objs = list(always_iterable(objs))
|
@@ -1018,12 +1019,14 @@ def dataclass_to_dataframe(
|
|
1018
1019
|
) from None
|
1019
1020
|
data = list(map(asdict, objs))
|
1020
1021
|
first, *_ = objs
|
1021
|
-
schema = dataclass_to_schema(
|
1022
|
+
schema = dataclass_to_schema(
|
1023
|
+
first, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
|
1024
|
+
)
|
1022
1025
|
df = DataFrame(data, schema=schema, orient="row")
|
1023
|
-
return map_over_columns(
|
1026
|
+
return map_over_columns(_dataclass_to_dataframe_cast, df)
|
1024
1027
|
|
1025
1028
|
|
1026
|
-
def
|
1029
|
+
def _dataclass_to_dataframe_cast(series: Series, /) -> Series:
|
1027
1030
|
if series.dtype == Object:
|
1028
1031
|
is_path = series.map_elements(make_isinstance(Path), return_dtype=Boolean).all()
|
1029
1032
|
is_uuid = series.map_elements(make_isinstance(UUID), return_dtype=Boolean).all()
|