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.
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.datetime import SECOND, datetime_duration_to_float
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 utilities.types import Duration
16
+ from whenever import TimeDelta
18
17
 
19
18
 
20
19
  _LOCALHOST: str = "localhost"
21
- _TIMEOUT: Duration = SECOND
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: Duration = _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=timeout_use)
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) -> dt.date | None: ...
213
+ def ensure_date(obj: Any, /, *, nullable: bool) -> Date | None: ...
214
214
  @overload
215
- def ensure_date(obj: Any, /, *, nullable: Literal[False] = False) -> dt.date: ...
216
- def ensure_date(obj: Any, /, *, nullable: bool = False) -> dt.date | None:
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, dt.date, nullable=nullable)
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) -> dt.time | None: ...
501
+ def ensure_time(obj: Any, /, *, nullable: bool) -> Time | None: ...
500
502
  @overload
501
- def ensure_time(obj: Any, /, *, nullable: Literal[False] = False) -> dt.time: ...
502
- def ensure_time(obj: Any, /, *, nullable: bool = False) -> dt.time | None:
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, dt.time, nullable=nullable)
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 ensure_timedelta(obj: Any, /, *, nullable: bool) -> dt.timedelta | None: ...
526
+ def ensure_time_delta(obj: Any, /, *, nullable: bool) -> TimeDelta | None: ...
525
527
  @overload
526
- def ensure_timedelta(
528
+ def ensure_time_delta(
527
529
  obj: Any, /, *, nullable: Literal[False] = False
528
- ) -> dt.timedelta: ...
529
- def ensure_timedelta(obj: Any, /, *, nullable: bool = False) -> dt.timedelta | None:
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, dt.timedelta, nullable=nullable)
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 timedelta", nullable=self.nullable)
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
- "ensure_timedelta",
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, max_nullable, min_nullable
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
- from collections.abc import Collection, Hashable, Iterable, Iterator, Sequence
126
- from zoneinfo import ZoneInfo
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 Duration, MathRoundMode, Number, TimeZoneLike
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
- plain_datetime = draw(
1492
- plain_datetimes_whenever(min_value=min_value_, max_value=max_value_)
1493
- )
1494
- with assume_does_not_raise(ValueError):
1495
- return plain_datetime.assume_tz(time_zone_.key, disambiguate="raise")
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(first, globalns=globalns, localns=localns)
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(_dataclass_to_dataframe_uuid, df)
1026
+ return map_over_columns(_dataclass_to_dataframe_cast, df)
1024
1027
 
1025
1028
 
1026
- def _dataclass_to_dataframe_uuid(series: Series, /) -> Series:
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()