dycw-utilities 0.154.0__py3-none-any.whl → 0.155.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dycw_utilities-0.154.0.dist-info → dycw_utilities-0.155.1.dist-info}/METADATA +2 -3
- {dycw_utilities-0.154.0.dist-info → dycw_utilities-0.155.1.dist-info}/RECORD +13 -13
- utilities/__init__.py +1 -1
- utilities/hypothesis.py +151 -42
- utilities/orjson.py +45 -12
- utilities/polars.py +76 -43
- utilities/pytest_regressions.py +3 -1
- utilities/traceback.py +1 -1
- utilities/typing.py +9 -2
- utilities/whenever.py +10 -0
- {dycw_utilities-0.154.0.dist-info → dycw_utilities-0.155.1.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.154.0.dist-info → dycw_utilities-0.155.1.dist-info}/entry_points.txt +0 -0
- {dycw_utilities-0.154.0.dist-info → dycw_utilities-0.155.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.155.1
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
@@ -12,8 +12,7 @@ Provides-Extra: logging
|
|
12
12
|
Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
|
13
13
|
Provides-Extra: test
|
14
14
|
Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
|
15
|
-
Requires-Dist: hypothesis<6.
|
16
|
-
Requires-Dist: pudb<2025.2,>=2025.1; extra == 'test'
|
15
|
+
Requires-Dist: hypothesis<6.138,>=6.137.1; extra == 'test'
|
17
16
|
Requires-Dist: pytest-asyncio<1.2,>=1.1.0; extra == 'test'
|
18
17
|
Requires-Dist: pytest-cov<6.3,>=6.2.1; extra == 'test'
|
19
18
|
Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=VUm6D-MbPENpEcdDNizwkQ7ZdWupTs1IZmUsMeC93k0,60
|
2
2
|
utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
|
3
3
|
utilities/asyncio.py,sha256=QXkTtugXkqtYt7Do23zgYErqzdp6jwzPpV_SP9fJ1gI,16780
|
4
4
|
utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
|
@@ -22,7 +22,7 @@ utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
|
22
22
|
utilities/gzip.py,sha256=fkGP3KdsBfXlstodT4wtlp-PwNyUsogpbDCVVVGdsm4,781
|
23
23
|
utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
|
24
24
|
utilities/http.py,sha256=TsavEfHlRtlLaeV21Z6KZh0qbPw-kvD1zsQdZ7Kep5Q,977
|
25
|
-
utilities/hypothesis.py,sha256=
|
25
|
+
utilities/hypothesis.py,sha256=2lAUvuXj_zswg-8Ot0ZuBzTbZyaRYWRmeR8qSF7Mmys,43817
|
26
26
|
utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
27
27
|
utilities/inflect.py,sha256=v7YkOWSu8NAmVghPcf4F3YBZQoJCS47_DLf9jbfWIs0,581
|
28
28
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
@@ -39,13 +39,13 @@ utilities/more_itertools.py,sha256=rklJ5vpvXr_H5pAGpWmwVpqtBVehoJ0-jBGYsZbux3M,1
|
|
39
39
|
utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
|
40
40
|
utilities/operator.py,sha256=nhxn5q6CFNzUm1wpTwWPCu9JGCqVHSlaJf0o1-efoII,3616
|
41
41
|
utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
|
42
|
-
utilities/orjson.py,sha256=
|
42
|
+
utilities/orjson.py,sha256=Ll0U172ITMqOJc3kjV90C0eI-EWzSXlMHSdUBaUSe80,41499
|
43
43
|
utilities/os.py,sha256=mFvjydySvjtSXpk7tLStUJcndauAoujxUUmj_CO7LWY,3778
|
44
44
|
utilities/parse.py,sha256=JcJn5yXKhIWXBCwgBdPsyu7Hvcuw6kyEdqvaebCaI9k,17951
|
45
45
|
utilities/pathlib.py,sha256=qGuU8XPmdgGpy8tOMUgelfXx3kxI8h9IaV3TI_06QGE,8428
|
46
46
|
utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
|
47
47
|
utilities/platform.py,sha256=pTn7gw6N4T6LdKrf0virwarof_mze9WtoQlrGMzhGVI,2798
|
48
|
-
utilities/polars.py,sha256=
|
48
|
+
utilities/polars.py,sha256=yedkwwcyX35lreA2CeOGVRyOrXrs8DKk98T9dWpnxCo,79844
|
49
49
|
utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
|
50
50
|
utilities/postgres.py,sha256=ynCTTaF-bVEOSW-KEAR-dlLh_hYjeVVjm__-4pEU8Zk,12269
|
51
51
|
utilities/pottery.py,sha256=HJ96oLRarTP37Vhg0WTyB3yAu2hETeg6HgRmpDIqyUs,6581
|
@@ -54,7 +54,7 @@ utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
|
|
54
54
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
utilities/pyinstrument.py,sha256=NZCZz2nBo0BLJ9DTf7H_Q_KGxvsf2S2M3h0qYoYh2kw,804
|
56
56
|
utilities/pytest.py,sha256=2HHfAWkzZeK2OAzL2F49EDKooMkfDoGqg8Ev4cHC_N8,7869
|
57
|
-
utilities/pytest_regressions.py,sha256=
|
57
|
+
utilities/pytest_regressions.py,sha256=8by5DWEL89Y469TI5AzX1pMy3NJWVtjEg2xQdOOdYuM,4169
|
58
58
|
utilities/random.py,sha256=hZlH4gnAtoaofWswuJYjcygejrY8db4CzP-z_adO2Mo,4165
|
59
59
|
utilities/re.py,sha256=S4h-DLL6ScMPqjboZ_uQ1BVTJajrqV06r_81D--_HCE,4573
|
60
60
|
utilities/redis.py,sha256=2fdveFbqL2pEAeyiVuN_Je8nSM_IZHeahPduMHhFRzY,28381
|
@@ -72,23 +72,23 @@ utilities/tempfile.py,sha256=HxB2BF28CcecDJLQ3Bx2Ej-Pb6RJc6W9ngSpB9CnP4k,2018
|
|
72
72
|
utilities/text.py,sha256=uwCDgpEunYruyh6sKMfNWK3Rp5H3ndpKRAkq86CBNys,13043
|
73
73
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
74
74
|
utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
|
75
|
-
utilities/traceback.py,sha256=
|
75
|
+
utilities/traceback.py,sha256=1k5JgumSMaqAGLd0dZ36CtPS0EGaglxTr29r2Dz4D60,9457
|
76
76
|
utilities/typed_settings.py,sha256=SFWqS3lAzV7IfNRwqFcTk0YynTcQ7BmrcW2mr_KUnos,4466
|
77
77
|
utilities/types.py,sha256=L4cjFPyFZX58Urfw0S_i-XRywPIFyuSLOieewj0qqsM,18516
|
78
|
-
utilities/typing.py,sha256=
|
78
|
+
utilities/typing.py,sha256=7ZgCNZwA6oaiwpSJIS9Rj3i3MbRBYHMqbC3jMe5KiNg,13992
|
79
79
|
utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
|
80
80
|
utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
|
81
81
|
utilities/uuid.py,sha256=nQZs6tFX4mqtc2Ku3KqjloYCqwpTKeTj8eKwQwh3FQI,1572
|
82
82
|
utilities/version.py,sha256=ipBj5-WYY_nelp2uwFlApfWWCzTLzPwpovUi9x_OBMs,5085
|
83
83
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
84
|
-
utilities/whenever.py,sha256=
|
84
|
+
utilities/whenever.py,sha256=vsoVRd8-KXVn9Ik5PveIGgOCuIGnMNqSEoPCsR0sZ30,57755
|
85
85
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
86
86
|
utilities/zoneinfo.py,sha256=FBMcUQ4662Aq8SsuCL1OAhDQiyANmVjtb-C30DRrWoE,1966
|
87
87
|
utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
|
88
88
|
utilities/pytest_plugins/pytest_randomly.py,sha256=B1qYVlExGOxTywq2r1SMi5o7btHLk2PNdY_b1p98dkE,409
|
89
89
|
utilities/pytest_plugins/pytest_regressions.py,sha256=9v8kAXDM2ycIXJBimoiF4EgrwbUvxTycFWJiGR_GHhM,1466
|
90
|
-
dycw_utilities-0.
|
91
|
-
dycw_utilities-0.
|
92
|
-
dycw_utilities-0.
|
93
|
-
dycw_utilities-0.
|
94
|
-
dycw_utilities-0.
|
90
|
+
dycw_utilities-0.155.1.dist-info/METADATA,sha256=t4j9mkVdOy56nqyYGTiiODz6Zq0dOJUFTTz_4CTcQTg,1643
|
91
|
+
dycw_utilities-0.155.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
92
|
+
dycw_utilities-0.155.1.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
|
93
|
+
dycw_utilities-0.155.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
94
|
+
dycw_utilities-0.155.1.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/hypothesis.py
CHANGED
@@ -50,10 +50,12 @@ from whenever import (
|
|
50
50
|
ZonedDateTime,
|
51
51
|
)
|
52
52
|
|
53
|
-
from utilities.functions import ensure_int, ensure_str
|
53
|
+
from utilities.functions import ensure_int, ensure_str, max_nullable, min_nullable
|
54
54
|
from utilities.math import (
|
55
55
|
MAX_FLOAT32,
|
56
56
|
MAX_FLOAT64,
|
57
|
+
MAX_INT8,
|
58
|
+
MAX_INT16,
|
57
59
|
MAX_INT32,
|
58
60
|
MAX_INT64,
|
59
61
|
MAX_UINT8,
|
@@ -62,6 +64,8 @@ from utilities.math import (
|
|
62
64
|
MAX_UINT64,
|
63
65
|
MIN_FLOAT32,
|
64
66
|
MIN_FLOAT64,
|
67
|
+
MIN_INT8,
|
68
|
+
MIN_INT16,
|
65
69
|
MIN_INT32,
|
66
70
|
MIN_INT64,
|
67
71
|
MIN_UINT8,
|
@@ -90,6 +94,9 @@ from utilities.whenever import (
|
|
90
94
|
DAY,
|
91
95
|
TIME_DELTA_MAX,
|
92
96
|
TIME_DELTA_MIN,
|
97
|
+
DatePeriod,
|
98
|
+
TimePeriod,
|
99
|
+
ZonedDateTimePeriod,
|
93
100
|
to_date_time_delta,
|
94
101
|
to_days,
|
95
102
|
to_nanoseconds,
|
@@ -201,6 +208,26 @@ def date_deltas(
|
|
201
208
|
##
|
202
209
|
|
203
210
|
|
211
|
+
@composite
|
212
|
+
def date_periods(
|
213
|
+
draw: DrawFn,
|
214
|
+
/,
|
215
|
+
*,
|
216
|
+
min_value: MaybeSearchStrategy[Date | None] = None,
|
217
|
+
max_value: MaybeSearchStrategy[Date | None] = None,
|
218
|
+
two_digit: MaybeSearchStrategy[bool] = False,
|
219
|
+
) -> DatePeriod:
|
220
|
+
"""Strategy for generating date periods."""
|
221
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
222
|
+
two_digit_ = draw2(draw, two_digit)
|
223
|
+
strategy = dates(min_value=min_value_, max_value=max_value_, two_digit=two_digit_)
|
224
|
+
start, end = draw(pairs(strategy, sorted=True))
|
225
|
+
return DatePeriod(start, end)
|
226
|
+
|
227
|
+
|
228
|
+
##
|
229
|
+
|
230
|
+
|
204
231
|
@composite
|
205
232
|
def date_time_deltas(
|
206
233
|
draw: DrawFn,
|
@@ -209,6 +236,7 @@ def date_time_deltas(
|
|
209
236
|
min_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
|
210
237
|
max_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
|
211
238
|
parsable: MaybeSearchStrategy[bool] = False,
|
239
|
+
nativable: MaybeSearchStrategy[bool] = False,
|
212
240
|
) -> DateTimeDelta:
|
213
241
|
"""Strategy for generating date deltas."""
|
214
242
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
@@ -230,7 +258,13 @@ def date_time_deltas(
|
|
230
258
|
if draw2(draw, parsable):
|
231
259
|
min_nanos = max(min_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MIN))
|
232
260
|
max_nanos = min(max_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MAX))
|
233
|
-
|
261
|
+
if draw2(draw, nativable):
|
262
|
+
min_micros, _ = divmod(min_nanos, 1000)
|
263
|
+
max_micros, _ = divmod(max_nanos, 1000)
|
264
|
+
micros = draw(integers(min_value=min_micros + 1, max_value=max_micros))
|
265
|
+
nanos = 1000 * micros
|
266
|
+
else:
|
267
|
+
nanos = draw(integers(min_value=min_nanos, max_value=max_nanos))
|
234
268
|
return to_date_time_delta(nanos)
|
235
269
|
|
236
270
|
|
@@ -373,13 +407,13 @@ def float32s(
|
|
373
407
|
draw: DrawFn,
|
374
408
|
/,
|
375
409
|
*,
|
376
|
-
min_value: MaybeSearchStrategy[float] =
|
377
|
-
max_value: MaybeSearchStrategy[float] =
|
410
|
+
min_value: MaybeSearchStrategy[float | None] = None,
|
411
|
+
max_value: MaybeSearchStrategy[float | None] = None,
|
378
412
|
) -> float:
|
379
413
|
"""Strategy for generating float32s."""
|
380
414
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
381
|
-
min_value_ =
|
382
|
-
max_value_ =
|
415
|
+
min_value_ = max_nullable([min_value_, MIN_FLOAT32])
|
416
|
+
max_value_ = min_nullable([max_value_, MAX_FLOAT32])
|
383
417
|
if is_zero(min_value_) and is_zero(max_value_):
|
384
418
|
min_value_ = max_value_ = 0.0
|
385
419
|
return draw(floats(min_value_, max_value_, width=32))
|
@@ -390,13 +424,13 @@ def float64s(
|
|
390
424
|
draw: DrawFn,
|
391
425
|
/,
|
392
426
|
*,
|
393
|
-
min_value: MaybeSearchStrategy[float] =
|
394
|
-
max_value: MaybeSearchStrategy[float] =
|
427
|
+
min_value: MaybeSearchStrategy[float | None] = None,
|
428
|
+
max_value: MaybeSearchStrategy[float | None] = None,
|
395
429
|
) -> float:
|
396
430
|
"""Strategy for generating float64s."""
|
397
431
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
398
|
-
min_value_ =
|
399
|
-
max_value_ =
|
432
|
+
min_value_ = max_nullable([min_value_, MIN_FLOAT64])
|
433
|
+
max_value_ = min_nullable([max_value_, MAX_FLOAT64])
|
400
434
|
if is_zero(min_value_) and is_zero(max_value_):
|
401
435
|
min_value_ = max_value_ = 0.0
|
402
436
|
return draw(floats(min_value_, max_value_, width=64))
|
@@ -601,18 +635,48 @@ def int_arrays(
|
|
601
635
|
##
|
602
636
|
|
603
637
|
|
638
|
+
@composite
|
639
|
+
def int8s(
|
640
|
+
draw: DrawFn,
|
641
|
+
/,
|
642
|
+
*,
|
643
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
644
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
645
|
+
) -> int:
|
646
|
+
"""Strategy for generating int8s."""
|
647
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
648
|
+
min_value_ = max_nullable([min_value_, MIN_INT8])
|
649
|
+
max_value_ = min_nullable([max_value_, MAX_INT8])
|
650
|
+
return draw(integers(min_value=min_value_, max_value=max_value_))
|
651
|
+
|
652
|
+
|
653
|
+
@composite
|
654
|
+
def int16s(
|
655
|
+
draw: DrawFn,
|
656
|
+
/,
|
657
|
+
*,
|
658
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
659
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
660
|
+
) -> int:
|
661
|
+
"""Strategy for generating int16s."""
|
662
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
663
|
+
min_value_ = max_nullable([min_value_, MIN_INT16])
|
664
|
+
max_value_ = min_nullable([max_value_, MAX_INT16])
|
665
|
+
return draw(integers(min_value=min_value_, max_value=max_value_))
|
666
|
+
|
667
|
+
|
604
668
|
@composite
|
605
669
|
def int32s(
|
606
670
|
draw: DrawFn,
|
607
671
|
/,
|
608
672
|
*,
|
609
|
-
min_value: MaybeSearchStrategy[int] =
|
610
|
-
max_value: MaybeSearchStrategy[int] =
|
673
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
674
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
611
675
|
) -> int:
|
612
676
|
"""Strategy for generating int32s."""
|
613
677
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
614
|
-
min_value_ =
|
615
|
-
max_value_ =
|
678
|
+
min_value_ = max_nullable([min_value_, MIN_INT32])
|
679
|
+
max_value_ = min_nullable([max_value_, MAX_INT32])
|
616
680
|
return draw(integers(min_value_, max_value_))
|
617
681
|
|
618
682
|
|
@@ -621,13 +685,13 @@ def int64s(
|
|
621
685
|
draw: DrawFn,
|
622
686
|
/,
|
623
687
|
*,
|
624
|
-
min_value: MaybeSearchStrategy[int] =
|
625
|
-
max_value: MaybeSearchStrategy[int] =
|
688
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
689
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
626
690
|
) -> int:
|
627
691
|
"""Strategy for generating int64s."""
|
628
692
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
629
|
-
min_value_ =
|
630
|
-
max_value_ =
|
693
|
+
min_value_ = max_nullable([min_value_, MIN_INT64])
|
694
|
+
max_value_ = min_nullable([max_value_, MAX_INT64])
|
631
695
|
return draw(integers(min_value_, max_value_))
|
632
696
|
|
633
697
|
|
@@ -780,7 +844,7 @@ def _path_parts(draw: DrawFn, /) -> str:
|
|
780
844
|
|
781
845
|
|
782
846
|
@composite
|
783
|
-
def
|
847
|
+
def plain_date_times(
|
784
848
|
draw: DrawFn,
|
785
849
|
/,
|
786
850
|
*,
|
@@ -1174,6 +1238,24 @@ def time_deltas(
|
|
1174
1238
|
##
|
1175
1239
|
|
1176
1240
|
|
1241
|
+
@composite
|
1242
|
+
def time_periods(
|
1243
|
+
draw: DrawFn,
|
1244
|
+
/,
|
1245
|
+
*,
|
1246
|
+
min_value: MaybeSearchStrategy[Time | None] = None,
|
1247
|
+
max_value: MaybeSearchStrategy[Time | None] = None,
|
1248
|
+
) -> TimePeriod:
|
1249
|
+
"""Strategy for generating time periods."""
|
1250
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1251
|
+
strategy = times(min_value=min_value_, max_value=max_value_)
|
1252
|
+
start, end = draw(pairs(strategy, sorted=True))
|
1253
|
+
return TimePeriod(start, end)
|
1254
|
+
|
1255
|
+
|
1256
|
+
##
|
1257
|
+
|
1258
|
+
|
1177
1259
|
@composite
|
1178
1260
|
def times(
|
1179
1261
|
draw: DrawFn,
|
@@ -1235,13 +1317,13 @@ def uint8s(
|
|
1235
1317
|
draw: DrawFn,
|
1236
1318
|
/,
|
1237
1319
|
*,
|
1238
|
-
min_value: MaybeSearchStrategy[int] =
|
1239
|
-
max_value: MaybeSearchStrategy[int] =
|
1320
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
1321
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
1240
1322
|
) -> int:
|
1241
1323
|
"""Strategy for generating uint8s."""
|
1242
1324
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1243
|
-
min_value_ =
|
1244
|
-
max_value_ =
|
1325
|
+
min_value_ = max_nullable([min_value_, MIN_UINT8])
|
1326
|
+
max_value_ = min_nullable([max_value_, MAX_UINT8])
|
1245
1327
|
return draw(integers(min_value=min_value_, max_value=max_value_))
|
1246
1328
|
|
1247
1329
|
|
@@ -1250,13 +1332,13 @@ def uint16s(
|
|
1250
1332
|
draw: DrawFn,
|
1251
1333
|
/,
|
1252
1334
|
*,
|
1253
|
-
min_value: MaybeSearchStrategy[int] =
|
1254
|
-
max_value: MaybeSearchStrategy[int] =
|
1335
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
1336
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
1255
1337
|
) -> int:
|
1256
1338
|
"""Strategy for generating uint16s."""
|
1257
1339
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1258
|
-
min_value_ =
|
1259
|
-
max_value_ =
|
1340
|
+
min_value_ = max_nullable([min_value_, MIN_UINT16])
|
1341
|
+
max_value_ = min_nullable([max_value_, MAX_UINT16])
|
1260
1342
|
return draw(integers(min_value=min_value_, max_value=max_value_))
|
1261
1343
|
|
1262
1344
|
|
@@ -1265,13 +1347,13 @@ def uint32s(
|
|
1265
1347
|
draw: DrawFn,
|
1266
1348
|
/,
|
1267
1349
|
*,
|
1268
|
-
min_value: MaybeSearchStrategy[int] =
|
1269
|
-
max_value: MaybeSearchStrategy[int] =
|
1350
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
1351
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
1270
1352
|
) -> int:
|
1271
1353
|
"""Strategy for generating uint32s."""
|
1272
1354
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1273
|
-
min_value_ =
|
1274
|
-
max_value_ =
|
1355
|
+
min_value_ = max_nullable([min_value_, MIN_UINT32])
|
1356
|
+
max_value_ = min_nullable([max_value_, MAX_UINT32])
|
1275
1357
|
return draw(integers(min_value=min_value_, max_value=max_value_))
|
1276
1358
|
|
1277
1359
|
|
@@ -1280,13 +1362,13 @@ def uint64s(
|
|
1280
1362
|
draw: DrawFn,
|
1281
1363
|
/,
|
1282
1364
|
*,
|
1283
|
-
min_value: MaybeSearchStrategy[int] =
|
1284
|
-
max_value: MaybeSearchStrategy[int] =
|
1365
|
+
min_value: MaybeSearchStrategy[int | None] = None,
|
1366
|
+
max_value: MaybeSearchStrategy[int | None] = None,
|
1285
1367
|
) -> int:
|
1286
1368
|
"""Strategy for generating uint64s."""
|
1287
1369
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1288
|
-
min_value_ =
|
1289
|
-
max_value_ =
|
1370
|
+
min_value_ = max_nullable([min_value_, MIN_UINT64])
|
1371
|
+
max_value_ = min_nullable([max_value_, MAX_UINT64])
|
1290
1372
|
return draw(integers(min_value=min_value_, max_value=max_value_))
|
1291
1373
|
|
1292
1374
|
|
@@ -1374,7 +1456,29 @@ def year_months(
|
|
1374
1456
|
|
1375
1457
|
|
1376
1458
|
@composite
|
1377
|
-
def
|
1459
|
+
def zoned_date_time_periods(
|
1460
|
+
draw: DrawFn,
|
1461
|
+
/,
|
1462
|
+
*,
|
1463
|
+
min_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
|
1464
|
+
max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
|
1465
|
+
time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
|
1466
|
+
) -> ZonedDateTimePeriod:
|
1467
|
+
"""Strategy for generating zoned date-time periods."""
|
1468
|
+
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1469
|
+
time_zone_: TimeZoneLike = draw2(draw, time_zone)
|
1470
|
+
strategy = zoned_date_times(
|
1471
|
+
min_value=min_value_, max_value=max_value_, time_zone=time_zone_
|
1472
|
+
)
|
1473
|
+
start, end = draw(pairs(strategy, sorted=True))
|
1474
|
+
return ZonedDateTimePeriod(start, end)
|
1475
|
+
|
1476
|
+
|
1477
|
+
##
|
1478
|
+
|
1479
|
+
|
1480
|
+
@composite
|
1481
|
+
def zoned_date_times(
|
1378
1482
|
draw: DrawFn,
|
1379
1483
|
/,
|
1380
1484
|
*,
|
@@ -1382,7 +1486,7 @@ def zoned_datetimes(
|
|
1382
1486
|
max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
|
1383
1487
|
time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
|
1384
1488
|
) -> ZonedDateTime:
|
1385
|
-
"""Strategy for generating zoned
|
1489
|
+
"""Strategy for generating zoned date-times."""
|
1386
1490
|
min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
|
1387
1491
|
time_zone_ = ensure_time_zone(draw2(draw, time_zone))
|
1388
1492
|
match min_value_:
|
@@ -1401,7 +1505,7 @@ def zoned_datetimes(
|
|
1401
1505
|
max_value_ = max_value_.to_tz(time_zone_.key).to_plain()
|
1402
1506
|
case never:
|
1403
1507
|
assert_never(never)
|
1404
|
-
plain = draw(
|
1508
|
+
plain = draw(plain_date_times(min_value=min_value_, max_value=max_value_))
|
1405
1509
|
with (
|
1406
1510
|
assume_does_not_raise(RepeatedTime),
|
1407
1511
|
assume_does_not_raise(SkippedTime),
|
@@ -1415,7 +1519,7 @@ def zoned_datetimes(
|
|
1415
1519
|
return zoned
|
1416
1520
|
|
1417
1521
|
|
1418
|
-
|
1522
|
+
zoned_date_times_2000 = zoned_date_times(
|
1419
1523
|
min_value=ZonedDateTime(2000, 1, 1, tz=UTC.key),
|
1420
1524
|
max_value=ZonedDateTime(2000, 12, 31, tz=UTC.key),
|
1421
1525
|
)
|
@@ -1427,6 +1531,7 @@ __all__ = [
|
|
1427
1531
|
"assume_does_not_raise",
|
1428
1532
|
"bool_arrays",
|
1429
1533
|
"date_deltas",
|
1534
|
+
"date_periods",
|
1430
1535
|
"date_time_deltas",
|
1431
1536
|
"dates",
|
1432
1537
|
"draw2",
|
@@ -1438,6 +1543,8 @@ __all__ = [
|
|
1438
1543
|
"hashables",
|
1439
1544
|
"import_froms",
|
1440
1545
|
"imports",
|
1546
|
+
"int8s",
|
1547
|
+
"int16s",
|
1441
1548
|
"int32s",
|
1442
1549
|
"int64s",
|
1443
1550
|
"int_arrays",
|
@@ -1446,7 +1553,7 @@ __all__ = [
|
|
1446
1553
|
"numbers",
|
1447
1554
|
"pairs",
|
1448
1555
|
"paths",
|
1449
|
-
"
|
1556
|
+
"plain_date_times",
|
1450
1557
|
"py_datetimes",
|
1451
1558
|
"random_states",
|
1452
1559
|
"sentinels",
|
@@ -1463,6 +1570,7 @@ __all__ = [
|
|
1463
1570
|
"text_digits",
|
1464
1571
|
"text_printable",
|
1465
1572
|
"time_deltas",
|
1573
|
+
"time_periods",
|
1466
1574
|
"times",
|
1467
1575
|
"triples",
|
1468
1576
|
"uint8s",
|
@@ -1472,6 +1580,7 @@ __all__ = [
|
|
1472
1580
|
"urls",
|
1473
1581
|
"versions",
|
1474
1582
|
"year_months",
|
1475
|
-
"
|
1476
|
-
"
|
1583
|
+
"zoned_date_time_periods",
|
1584
|
+
"zoned_date_times",
|
1585
|
+
"zoned_date_times_2000",
|
1477
1586
|
]
|
utilities/orjson.py
CHANGED
@@ -5,7 +5,7 @@ import re
|
|
5
5
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
6
6
|
from contextlib import suppress
|
7
7
|
from dataclasses import dataclass, field, replace
|
8
|
-
from enum import Enum, unique
|
8
|
+
from enum import Enum, StrEnum, unique
|
9
9
|
from functools import cached_property, partial
|
10
10
|
from itertools import chain
|
11
11
|
from logging import Formatter, LogRecord
|
@@ -52,7 +52,12 @@ from utilities.math import MAX_INT64, MIN_INT64
|
|
52
52
|
from utilities.types import Dataclass, LogLevel, MaybeIterable, PathLike, StrMapping
|
53
53
|
from utilities.tzlocal import LOCAL_TIME_ZONE
|
54
54
|
from utilities.version import Version, parse_version
|
55
|
-
from utilities.whenever import
|
55
|
+
from utilities.whenever import (
|
56
|
+
DatePeriod,
|
57
|
+
TimePeriod,
|
58
|
+
ZonedDateTimePeriod,
|
59
|
+
from_timestamp,
|
60
|
+
)
|
56
61
|
|
57
62
|
if TYPE_CHECKING:
|
58
63
|
from collections.abc import Set as AbstractSet
|
@@ -65,10 +70,11 @@ if TYPE_CHECKING:
|
|
65
70
|
|
66
71
|
|
67
72
|
@unique
|
68
|
-
class _Prefixes(
|
73
|
+
class _Prefixes(StrEnum):
|
69
74
|
dataclass = "dc"
|
70
75
|
date = "d"
|
71
76
|
date_delta = "dd"
|
77
|
+
date_period = "dp"
|
72
78
|
date_time_delta = "D"
|
73
79
|
enum = "e"
|
74
80
|
exception_class = "Ex"
|
@@ -77,7 +83,7 @@ class _Prefixes(Enum):
|
|
77
83
|
frozenset_ = "fr"
|
78
84
|
list_ = "l"
|
79
85
|
month_day = "md"
|
80
|
-
none = "
|
86
|
+
none = "0"
|
81
87
|
path = "p"
|
82
88
|
plain_date_time = "pd"
|
83
89
|
py_date = "!d"
|
@@ -87,12 +93,14 @@ class _Prefixes(Enum):
|
|
87
93
|
set_ = "s"
|
88
94
|
time = "ti"
|
89
95
|
time_delta = "td"
|
96
|
+
time_period = "tp"
|
90
97
|
tuple_ = "tu"
|
91
98
|
unserializable = "un"
|
92
99
|
uuid = "uu"
|
93
100
|
version = "v"
|
94
101
|
year_month = "ym"
|
95
102
|
zoned_date_time = "zd"
|
103
|
+
zoned_date_time_period = "zp"
|
96
104
|
|
97
105
|
|
98
106
|
type _DataclassHook = Callable[[type[Dataclass], StrMapping], StrMapping]
|
@@ -167,8 +175,10 @@ def _pre_process(
|
|
167
175
|
return f"[{_Prefixes.date.value}]{date}"
|
168
176
|
case DateDelta() as date:
|
169
177
|
return f"[{_Prefixes.date_delta.value}]{date}"
|
170
|
-
case
|
171
|
-
return f"[{_Prefixes.
|
178
|
+
case DatePeriod() as period:
|
179
|
+
return f"[{_Prefixes.date_period.value}]{period.start},{period.end}"
|
180
|
+
case DateTimeDelta() as date_time_delta:
|
181
|
+
return f"[{_Prefixes.date_time_delta.value}]{date_time_delta}"
|
172
182
|
case Exception() as error_:
|
173
183
|
return {
|
174
184
|
f"[{_Prefixes.exception_instance.value}|{type(error_).__qualname__}]": pre(
|
@@ -187,14 +197,16 @@ def _pre_process(
|
|
187
197
|
return f"[{_Prefixes.month_day.value}]{month_day!s}"
|
188
198
|
case Path() as path:
|
189
199
|
return f"[{_Prefixes.path.value}]{path!s}"
|
190
|
-
case PlainDateTime() as
|
191
|
-
return f"[{_Prefixes.plain_date_time.value}]{
|
192
|
-
case str() as
|
193
|
-
return
|
200
|
+
case PlainDateTime() as date_time:
|
201
|
+
return f"[{_Prefixes.plain_date_time.value}]{date_time}"
|
202
|
+
case str() as text:
|
203
|
+
return text
|
194
204
|
case Time() as time:
|
195
205
|
return f"[{_Prefixes.time.value}]{time}"
|
196
206
|
case TimeDelta() as time_delta:
|
197
207
|
return f"[{_Prefixes.time_delta.value}]{time_delta}"
|
208
|
+
case TimePeriod() as period:
|
209
|
+
return f"[{_Prefixes.time_period.value}]{period.start},{period.end}"
|
198
210
|
case type() as error_cls if issubclass(error_cls, Exception):
|
199
211
|
return f"[{_Prefixes.exception_class.value}|{error_cls.__qualname__}]"
|
200
212
|
case UUID() as uuid:
|
@@ -203,8 +215,12 @@ def _pre_process(
|
|
203
215
|
return f"[{_Prefixes.version.value}]{version}"
|
204
216
|
case YearMonth() as year_month:
|
205
217
|
return f"[{_Prefixes.year_month.value}]{year_month}"
|
206
|
-
case ZonedDateTime() as
|
207
|
-
return f"[{_Prefixes.zoned_date_time.value}]{
|
218
|
+
case ZonedDateTime() as date_time:
|
219
|
+
return f"[{_Prefixes.zoned_date_time.value}]{date_time}"
|
220
|
+
case ZonedDateTimePeriod() as period:
|
221
|
+
return (
|
222
|
+
f"[{_Prefixes.zoned_date_time_period.value}]{period.start},{period.end}"
|
223
|
+
)
|
208
224
|
case dt.datetime() as py_datetime:
|
209
225
|
match py_datetime.tzinfo:
|
210
226
|
case None:
|
@@ -369,6 +385,7 @@ def deserialize(
|
|
369
385
|
(
|
370
386
|
_DATE_PATTERN,
|
371
387
|
_DATE_DELTA_PATTERN,
|
388
|
+
_DATE_PERIOD_PATTERN,
|
372
389
|
_DATE_TIME_DELTA_PATTERN,
|
373
390
|
_FLOAT_PATTERN,
|
374
391
|
_MONTH_DAY_PATTERN,
|
@@ -381,15 +398,18 @@ def deserialize(
|
|
381
398
|
_PY_ZONED_DATE_TIME_PATTERN,
|
382
399
|
_TIME_PATTERN,
|
383
400
|
_TIME_DELTA_PATTERN,
|
401
|
+
_TIME_PERIOD_PATTERN,
|
384
402
|
_UUID_PATTERN,
|
385
403
|
_VERSION_PATTERN,
|
386
404
|
_YEAR_MONTH_PATTERN,
|
387
405
|
_ZONED_DATE_TIME_PATTERN,
|
406
|
+
_ZONED_DATE_TIME_PERIOD_PATTERN,
|
388
407
|
) = [
|
389
408
|
re.compile(r"^\[" + p.value + r"\](" + ".*" + ")$")
|
390
409
|
for p in [
|
391
410
|
_Prefixes.date,
|
392
411
|
_Prefixes.date_delta,
|
412
|
+
_Prefixes.date_period,
|
393
413
|
_Prefixes.date_time_delta,
|
394
414
|
_Prefixes.float_,
|
395
415
|
_Prefixes.month_day,
|
@@ -402,10 +422,12 @@ def deserialize(
|
|
402
422
|
_Prefixes.py_zoned_date_time,
|
403
423
|
_Prefixes.time,
|
404
424
|
_Prefixes.time_delta,
|
425
|
+
_Prefixes.time_period,
|
405
426
|
_Prefixes.uuid,
|
406
427
|
_Prefixes.version,
|
407
428
|
_Prefixes.year_month,
|
408
429
|
_Prefixes.zoned_date_time,
|
430
|
+
_Prefixes.zoned_date_time_period,
|
409
431
|
]
|
410
432
|
]
|
411
433
|
|
@@ -453,6 +475,9 @@ def _object_hook(
|
|
453
475
|
return Date.parse_common_iso(match.group(1))
|
454
476
|
if match := _DATE_DELTA_PATTERN.search(text):
|
455
477
|
return DateDelta.parse_common_iso(match.group(1))
|
478
|
+
if match := _DATE_PERIOD_PATTERN.search(text):
|
479
|
+
start, end = map(Date.parse_common_iso, match.group(1).split(","))
|
480
|
+
return DatePeriod(start, end)
|
456
481
|
if match := _DATE_TIME_DELTA_PATTERN.search(text):
|
457
482
|
return DateTimeDelta.parse_common_iso(match.group(1))
|
458
483
|
if match := _FLOAT_PATTERN.search(text):
|
@@ -475,6 +500,9 @@ def _object_hook(
|
|
475
500
|
return Time.parse_common_iso(match.group(1))
|
476
501
|
if match := _TIME_DELTA_PATTERN.search(text):
|
477
502
|
return TimeDelta.parse_common_iso(match.group(1))
|
503
|
+
if match := _TIME_PERIOD_PATTERN.search(text):
|
504
|
+
start, end = map(Time.parse_common_iso, match.group(1).split(","))
|
505
|
+
return TimePeriod(start, end)
|
478
506
|
if match := _UUID_PATTERN.search(text):
|
479
507
|
return UUID(match.group(1))
|
480
508
|
if match := _VERSION_PATTERN.search(text):
|
@@ -483,6 +511,11 @@ def _object_hook(
|
|
483
511
|
return YearMonth.parse_common_iso(match.group(1))
|
484
512
|
if match := _ZONED_DATE_TIME_PATTERN.search(text):
|
485
513
|
return ZonedDateTime.parse_common_iso(match.group(1))
|
514
|
+
if match := _ZONED_DATE_TIME_PERIOD_PATTERN.search(text):
|
515
|
+
start, end = map(
|
516
|
+
ZonedDateTime.parse_common_iso, match.group(1).split(",")
|
517
|
+
)
|
518
|
+
return ZonedDateTimePeriod(start, end)
|
486
519
|
if (
|
487
520
|
exc_class := _object_hook_exception_class(
|
488
521
|
text, data=data, objects=objects, redirects=redirects
|
utilities/polars.py
CHANGED
@@ -15,11 +15,12 @@ from uuid import UUID
|
|
15
15
|
from zoneinfo import ZoneInfo
|
16
16
|
|
17
17
|
import polars as pl
|
18
|
+
import whenever
|
18
19
|
from polars import (
|
19
20
|
Boolean,
|
20
21
|
DataFrame,
|
21
|
-
Date,
|
22
22
|
Datetime,
|
23
|
+
Duration,
|
23
24
|
Expr,
|
24
25
|
Float64,
|
25
26
|
Int64,
|
@@ -43,16 +44,16 @@ from polars import (
|
|
43
44
|
from polars._typing import PolarsDataType
|
44
45
|
from polars.datatypes import DataType, DataTypeClass
|
45
46
|
from polars.exceptions import (
|
46
|
-
ColumnNotFoundError,
|
47
|
+
ColumnNotFoundError, # pyright: ignore[reportAttributeAccessIssue]
|
47
48
|
NoRowsReturnedError,
|
48
|
-
OutOfBoundsError,
|
49
|
+
OutOfBoundsError, # pyright: ignore[reportAttributeAccessIssue]
|
49
50
|
PolarsInefficientMapWarning,
|
50
51
|
)
|
51
52
|
from polars.schema import Schema
|
52
53
|
from polars.testing import assert_frame_equal, assert_series_equal
|
53
|
-
from whenever import ZonedDateTime
|
54
|
+
from whenever import DateDelta, DateTimeDelta, PlainDateTime, TimeDelta, ZonedDateTime
|
54
55
|
|
55
|
-
from utilities.dataclasses import
|
56
|
+
from utilities.dataclasses import yield_fields
|
56
57
|
from utilities.errors import ImpossibleCaseError
|
57
58
|
from utilities.functions import (
|
58
59
|
EnsureIntError,
|
@@ -93,14 +94,18 @@ from utilities.typing import (
|
|
93
94
|
get_args,
|
94
95
|
get_type_hints,
|
95
96
|
is_frozenset_type,
|
96
|
-
is_instance_gen,
|
97
97
|
is_list_type,
|
98
98
|
is_literal_type,
|
99
99
|
is_optional_type,
|
100
100
|
is_set_type,
|
101
|
-
is_union_type,
|
102
101
|
)
|
103
102
|
from utilities.warnings import suppress_warnings
|
103
|
+
from utilities.whenever import (
|
104
|
+
DatePeriod,
|
105
|
+
TimePeriod,
|
106
|
+
ZonedDateTimePeriod,
|
107
|
+
to_py_time_delta,
|
108
|
+
)
|
104
109
|
from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
|
105
110
|
|
106
111
|
if TYPE_CHECKING:
|
@@ -1052,14 +1057,35 @@ def dataclass_to_dataframe(
|
|
1052
1057
|
|
1053
1058
|
def _dataclass_to_dataframe_cast(series: Series, /) -> Series:
|
1054
1059
|
if series.dtype == Object:
|
1060
|
+
if series.map_elements(
|
1061
|
+
make_isinstance(whenever.Date), return_dtype=Boolean
|
1062
|
+
).all():
|
1063
|
+
return series.map_elements(lambda x: x.py_date(), return_dtype=pl.Date)
|
1064
|
+
if series.map_elements(make_isinstance(DateDelta), return_dtype=Boolean).all():
|
1065
|
+
return series.map_elements(to_py_time_delta, return_dtype=Duration)
|
1066
|
+
if series.map_elements(
|
1067
|
+
make_isinstance(DateTimeDelta), return_dtype=Boolean
|
1068
|
+
).all():
|
1069
|
+
return series.map_elements(to_py_time_delta, return_dtype=Duration)
|
1055
1070
|
is_path = series.map_elements(make_isinstance(Path), return_dtype=Boolean).all()
|
1056
1071
|
is_uuid = series.map_elements(make_isinstance(UUID), return_dtype=Boolean).all()
|
1057
1072
|
if is_path or is_uuid:
|
1058
1073
|
with suppress_warnings(category=PolarsInefficientMapWarning):
|
1059
1074
|
return series.map_elements(str, return_dtype=String)
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1075
|
+
if series.map_elements(
|
1076
|
+
make_isinstance(whenever.Time), return_dtype=Boolean
|
1077
|
+
).all():
|
1078
|
+
return series.map_elements(lambda x: x.py_time(), return_dtype=pl.Time)
|
1079
|
+
if series.map_elements(make_isinstance(TimeDelta), return_dtype=Boolean).all():
|
1080
|
+
return series.map_elements(to_py_time_delta, return_dtype=Duration)
|
1081
|
+
if series.map_elements(
|
1082
|
+
make_isinstance(ZonedDateTime), return_dtype=Boolean
|
1083
|
+
).all():
|
1084
|
+
return_dtype = zoned_datetime_dtype(time_zone=one({dt.tz for dt in series}))
|
1085
|
+
return series.map_elements(
|
1086
|
+
lambda x: x.py_datetime(), return_dtype=return_dtype
|
1087
|
+
)
|
1088
|
+
raise NotImplementedError(series) # pragma: no cover
|
1063
1089
|
return series
|
1064
1090
|
|
1065
1091
|
|
@@ -1101,20 +1127,14 @@ def dataclass_to_schema(
|
|
1101
1127
|
for field in yield_fields(
|
1102
1128
|
obj, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
|
1103
1129
|
):
|
1104
|
-
if is_dataclass_instance(field.value)
|
1130
|
+
if is_dataclass_instance(field.value) and not (
|
1131
|
+
isinstance(field.type_, type)
|
1132
|
+
and issubclass(field.type_, (DatePeriod, TimePeriod, ZonedDateTimePeriod))
|
1133
|
+
):
|
1105
1134
|
dtypes = dataclass_to_schema(
|
1106
1135
|
field.value, globalns=globalns, localns=localns
|
1107
1136
|
)
|
1108
1137
|
dtype = struct_dtype(**dtypes)
|
1109
|
-
elif field.type_ is dt.datetime:
|
1110
|
-
dtype = _dataclass_to_schema_datetime(field)
|
1111
|
-
elif is_union_type(field.type_) and set(
|
1112
|
-
get_args(field.type_, optional_drop_none=True)
|
1113
|
-
) == {dt.date, dt.datetime}:
|
1114
|
-
if is_instance_gen(field.value, dt.date):
|
1115
|
-
dtype = Date
|
1116
|
-
else:
|
1117
|
-
dtype = _dataclass_to_schema_datetime(field)
|
1118
1138
|
else:
|
1119
1139
|
dtype = _dataclass_to_schema_one(
|
1120
1140
|
field.type_, globalns=globalns, localns=localns
|
@@ -1123,14 +1143,6 @@ def dataclass_to_schema(
|
|
1123
1143
|
return out
|
1124
1144
|
|
1125
1145
|
|
1126
|
-
def _dataclass_to_schema_datetime(
|
1127
|
-
field: _YieldFieldsInstance[dt.datetime], /
|
1128
|
-
) -> PolarsDataType:
|
1129
|
-
if field.value.tzinfo is None:
|
1130
|
-
return Datetime
|
1131
|
-
return zoned_datetime_dtype(time_zone=ensure_time_zone(field.value.tzinfo))
|
1132
|
-
|
1133
|
-
|
1134
1146
|
def _dataclass_to_schema_one(
|
1135
1147
|
obj: Any,
|
1136
1148
|
/,
|
@@ -1138,20 +1150,35 @@ def _dataclass_to_schema_one(
|
|
1138
1150
|
globalns: StrMapping | None = None,
|
1139
1151
|
localns: StrMapping | None = None,
|
1140
1152
|
) -> PolarsDataType:
|
1141
|
-
if obj
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1153
|
+
if isinstance(obj, type):
|
1154
|
+
if issubclass(obj, bool):
|
1155
|
+
return Boolean
|
1156
|
+
if issubclass(obj, int):
|
1157
|
+
return Int64
|
1158
|
+
if issubclass(obj, float):
|
1159
|
+
return Float64
|
1160
|
+
if issubclass(obj, str):
|
1161
|
+
return String
|
1162
|
+
if issubclass(
|
1163
|
+
obj,
|
1164
|
+
(
|
1165
|
+
DateDelta,
|
1166
|
+
DatePeriod,
|
1167
|
+
DateTimeDelta,
|
1168
|
+
Path,
|
1169
|
+
PlainDateTime,
|
1170
|
+
TimeDelta,
|
1171
|
+
TimePeriod,
|
1172
|
+
UUID,
|
1173
|
+
ZonedDateTime,
|
1174
|
+
ZonedDateTimePeriod,
|
1175
|
+
whenever.Date,
|
1176
|
+
whenever.Time,
|
1177
|
+
),
|
1178
|
+
):
|
1179
|
+
return Object
|
1180
|
+
if issubclass(obj, enum.Enum):
|
1181
|
+
return pl.Enum([e.name for e in obj])
|
1155
1182
|
if is_dataclass_class(obj):
|
1156
1183
|
out: dict[str, Any] = {}
|
1157
1184
|
for field in yield_fields(obj, globalns=globalns, localns=localns):
|
@@ -2444,7 +2471,13 @@ def struct_from_dataclass(
|
|
2444
2471
|
def _struct_from_dataclass_one(
|
2445
2472
|
ann: Any, /, *, time_zone: TimeZoneLike | None = None
|
2446
2473
|
) -> PolarsDataType:
|
2447
|
-
mapping = {
|
2474
|
+
mapping = {
|
2475
|
+
bool: Boolean,
|
2476
|
+
whenever.Date: pl.Date,
|
2477
|
+
float: Float64,
|
2478
|
+
int: Int64,
|
2479
|
+
str: String,
|
2480
|
+
}
|
2448
2481
|
with suppress(KeyError):
|
2449
2482
|
return mapping[ann]
|
2450
2483
|
if ann is dt.datetime:
|
utilities/pytest_regressions.py
CHANGED
@@ -91,7 +91,9 @@ class PolarsRegressionFixture:
|
|
91
91
|
def check(self, obj: Series | DataFrame, /, *, suffix: str | None = None) -> None:
|
92
92
|
"""Check the Series/DataFrame summary against the baseline."""
|
93
93
|
from polars import DataFrame, Series, col
|
94
|
-
from polars.exceptions import
|
94
|
+
from polars.exceptions import (
|
95
|
+
InvalidOperationError, # pyright: ignore[reportAttributeAccessIssue]
|
96
|
+
)
|
95
97
|
|
96
98
|
data: StrMapping = {
|
97
99
|
"describe": obj.describe(percentiles=[i / 10 for i in range(1, 10)]).rows(
|
utilities/traceback.py
CHANGED
@@ -284,7 +284,7 @@ def _make_except_hook_inner(
|
|
284
284
|
except SendToSlackError as error:
|
285
285
|
_ = stderr.write(f"{error}\n")
|
286
286
|
if to_bool(pudb): # pragma: no cover
|
287
|
-
from pudb import post_mortem
|
287
|
+
from pudb import post_mortem # pyright: ignore[reportMissingImports]
|
288
288
|
|
289
289
|
post_mortem(tb=traceback, e_type=exc_type, e_value=exc_val)
|
290
290
|
|
utilities/typing.py
CHANGED
@@ -23,6 +23,7 @@ from typing import get_type_hints as _get_type_hints
|
|
23
23
|
from uuid import UUID
|
24
24
|
from warnings import warn
|
25
25
|
|
26
|
+
import whenever
|
26
27
|
from whenever import (
|
27
28
|
Date,
|
28
29
|
DateDelta,
|
@@ -122,7 +123,6 @@ def get_type_hints(
|
|
122
123
|
warn_name_errors: bool = False,
|
123
124
|
) -> dict[str, Any]:
|
124
125
|
"""Get the type hints of an object."""
|
125
|
-
result: dict[str, Any] = obj.__annotations__
|
126
126
|
_ = {
|
127
127
|
Date,
|
128
128
|
DateDelta,
|
@@ -136,10 +136,17 @@ def get_type_hints(
|
|
136
136
|
TimeDelta,
|
137
137
|
UUID,
|
138
138
|
ZonedDateTime,
|
139
|
-
|
139
|
+
whenever.Date,
|
140
|
+
whenever.DateDelta,
|
141
|
+
whenever.DateTimeDelta,
|
142
|
+
whenever.PlainDateTime,
|
143
|
+
whenever.Time,
|
144
|
+
whenever.TimeDelta,
|
145
|
+
whenever.ZonedDateTime,
|
140
146
|
}
|
141
147
|
globalns_use = globals() | ({} if globalns is None else dict(globalns))
|
142
148
|
localns_use = {} if localns is None else dict(localns)
|
149
|
+
result: dict[str, Any] = obj.__annotations__
|
143
150
|
try:
|
144
151
|
hints = _get_type_hints(obj, globalns=globalns_use, localns=localns_use)
|
145
152
|
except NameError as error:
|
utilities/whenever.py
CHANGED
@@ -367,6 +367,14 @@ def get_now_local() -> ZonedDateTime:
|
|
367
367
|
NOW_LOCAL = get_now_local()
|
368
368
|
|
369
369
|
|
370
|
+
def get_now_plain(time_zone: TimeZoneLike = UTC, /) -> PlainDateTime:
|
371
|
+
"""Get the current zoned datetime."""
|
372
|
+
return get_now(time_zone).to_plain()
|
373
|
+
|
374
|
+
|
375
|
+
NOW_PLAIN = get_now_plain()
|
376
|
+
|
377
|
+
|
370
378
|
##
|
371
379
|
|
372
380
|
|
@@ -1927,6 +1935,7 @@ __all__ = [
|
|
1927
1935
|
"MINUTE",
|
1928
1936
|
"MONTH",
|
1929
1937
|
"NOW_LOCAL",
|
1938
|
+
"NOW_PLAIN",
|
1930
1939
|
"SECOND",
|
1931
1940
|
"TIME_DELTA_MAX",
|
1932
1941
|
"TIME_DELTA_MIN",
|
@@ -1966,6 +1975,7 @@ __all__ = [
|
|
1966
1975
|
"from_timestamp_nanos",
|
1967
1976
|
"get_now",
|
1968
1977
|
"get_now_local",
|
1978
|
+
"get_now_plain",
|
1969
1979
|
"get_today",
|
1970
1980
|
"get_today_local",
|
1971
1981
|
"mean_datetime",
|
File without changes
|
File without changes
|
File without changes
|