dycw-utilities 0.110.8__py3-none-any.whl → 0.111.0__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.110.8.dist-info → dycw_utilities-0.111.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.110.8.dist-info → dycw_utilities-0.111.0.dist-info}/RECORD +13 -13
- utilities/__init__.py +1 -1
- utilities/datetime.py +4 -21
- utilities/functions.py +0 -47
- utilities/orjson.py +1 -0
- utilities/parse.py +8 -11
- utilities/period.py +10 -5
- utilities/polars.py +2 -2
- utilities/sqlalchemy_polars.py +12 -12
- utilities/typing.py +161 -4
- {dycw_utilities-0.110.8.dist-info → dycw_utilities-0.111.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.110.8.dist-info → dycw_utilities-0.111.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=fjbCo5KvSkqKWLmPeMS8U77JZlYaYPd9js1ZVmdD1FE,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
|
4
4
|
utilities/asyncio.py,sha256=41oQUurWMvadFK5gFnaG21hMM0Vmfn2WS6OpC0R9mas,14757
|
@@ -12,13 +12,13 @@ utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
|
12
12
|
utilities/cryptography.py,sha256=HyOewI20cl3uRXsKivhIaeLVDInQdzgXZGaly7hS5dE,771
|
13
13
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
14
14
|
utilities/dataclasses.py,sha256=Q197PVnE_vUMn_SNnqJBCo4eRy4bdHtgMHWRbSJPtFk,26670
|
15
|
-
utilities/datetime.py,sha256=
|
15
|
+
utilities/datetime.py,sha256=XrVHvyHyYMBeU2ClC7xJKwB68l-F2ZCmDznyNXS4hT8,36862
|
16
16
|
utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
|
17
17
|
utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
|
18
18
|
utilities/eventkit.py,sha256=6M5Xu1SzN-juk9PqBHwy5dS-ta7T0qA6SMpDsakOJ0E,13039
|
19
19
|
utilities/fastapi.py,sha256=uwqOGbGwzIbP-lfm-ApG1ZEN3BA_TDsaiuTghhLmxb8,2413
|
20
20
|
utilities/fpdf2.py,sha256=zM3gwOYcAfv7P4qhbyvzPmRY4PPAiAQ-ZnPC6I9SZ1M,1832
|
21
|
-
utilities/functions.py,sha256=
|
21
|
+
utilities/functions.py,sha256=jgt592voaHNtX56qX0SRvFveVCRmSIxCZmqvpLZCnY8,27305
|
22
22
|
utilities/functools.py,sha256=WrpHt7NLNWSUn9A1Q_ZIWlNaYZOEI4IFKyBG9HO3BC4,1643
|
23
23
|
utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
24
24
|
utilities/git.py,sha256=wpt5dZ5Oi5931pN24_VLZYaQOvmR0OcQuVtgHzFUN1k,2359
|
@@ -39,14 +39,14 @@ utilities/more_itertools.py,sha256=CPUxrMAcTwRxbzbhiqPKi3Xx9hxqI0t6gkWjutaibGk,5
|
|
39
39
|
utilities/numpy.py,sha256=cBgCBet8YfZP_rb4nkCJHZx9_03qPEinVENMk1dGVYQ,25683
|
40
40
|
utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
|
41
41
|
utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
|
42
|
-
utilities/orjson.py,sha256=
|
42
|
+
utilities/orjson.py,sha256=DBm2zPP04kcHpY3l1etL24ksNynu-R3duFyx3U-RjqQ,36948
|
43
43
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
44
|
-
utilities/parse.py,sha256
|
44
|
+
utilities/parse.py,sha256=-sDMqA4nB4CFctc86uIlSoKmIUisTUd3qyBcuUnf1D0,18957
|
45
45
|
utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
|
46
|
-
utilities/period.py,sha256=
|
46
|
+
utilities/period.py,sha256=RWfcNVoNlW07RNdU47g_zuLZMKbtgfK4bE6G-9tVjY8,11024
|
47
47
|
utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
|
48
48
|
utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
|
49
|
-
utilities/polars.py,sha256=
|
49
|
+
utilities/polars.py,sha256=lr0l3JE2d5Rj1UbbFtagcv1z3Vw5YXjSfAwzVa9cFXw,58374
|
50
50
|
utilities/polars_ols.py,sha256=efhXf0gjrHUpQrvS6a7g8yJQJWf_ATKtJnqqF2inCOU,5680
|
51
51
|
utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
|
52
52
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -67,7 +67,7 @@ utilities/shelve.py,sha256=HZsMwK4tcIfg3sh0gApx4-yjQnrY4o3V3ZRimvRhoW0,738
|
|
67
67
|
utilities/slack_sdk.py,sha256=SeDNMh24IPiEBWoGMdgvrflUaFa9TGlTS03H9-NKaQw,4132
|
68
68
|
utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
|
69
69
|
utilities/sqlalchemy.py,sha256=GWzp54TP3F2mGhxPTn0c56KxxDeN9VKLMagcRSELhf4,35453
|
70
|
-
utilities/sqlalchemy_polars.py,sha256=
|
70
|
+
utilities/sqlalchemy_polars.py,sha256=wjJpoUo-yO9E2ujpG_06vV5r2OdvBiQ4yvV6wKCa2Tk,15605
|
71
71
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
72
72
|
utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
|
73
73
|
utilities/sys.py,sha256=h0Xr7Vj86wNalvwJVP1wj5Y0kD_VWm1vzuXZ_jw94mE,2743
|
@@ -78,7 +78,7 @@ utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
|
78
78
|
utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
|
79
79
|
utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
|
80
80
|
utilities/types.py,sha256=kVY71hZkcnyYNIlYSse0mLm8yeP3OBkzhDPMME6jXxo,18126
|
81
|
-
utilities/typing.py,sha256=
|
81
|
+
utilities/typing.py,sha256=jtc6EiGZGG0E745jo3NeLqo_HdHt7Zdaco3kCAEWIYU,11177
|
82
82
|
utilities/tzdata.py,sha256=2ZsPmhTVM9Ptrxb4QrWKtKOB9RiH8IOO-A1u7ULdVbg,176
|
83
83
|
utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
|
84
84
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
87
87
|
utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
|
88
88
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
89
89
|
utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
|
90
|
-
dycw_utilities-0.
|
91
|
-
dycw_utilities-0.
|
92
|
-
dycw_utilities-0.
|
93
|
-
dycw_utilities-0.
|
90
|
+
dycw_utilities-0.111.0.dist-info/METADATA,sha256=9UDVxMivd6ye3O57NEI2wEddqVpW9OE_rc_uhtMXD-c,13004
|
91
|
+
dycw_utilities-0.111.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
92
|
+
dycw_utilities-0.111.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
93
|
+
dycw_utilities-0.111.0.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/datetime.py
CHANGED
@@ -22,6 +22,7 @@ from utilities.math import SafeRoundError, round_, safe_round
|
|
22
22
|
from utilities.platform import SYSTEM
|
23
23
|
from utilities.sentinel import Sentinel, sentinel
|
24
24
|
from utilities.types import MaybeStr
|
25
|
+
from utilities.typing import is_instance_gen
|
25
26
|
from utilities.zoneinfo import (
|
26
27
|
UTC,
|
27
28
|
HongKong,
|
@@ -140,9 +141,9 @@ def are_equal_dates_or_datetimes(
|
|
140
141
|
x: DateOrDateTime, y: DateOrDateTime, /, *, strict: bool = False
|
141
142
|
) -> bool:
|
142
143
|
"""Check if x == y for dates/datetimes."""
|
143
|
-
if
|
144
|
+
if is_instance_gen(x, dt.date) and is_instance_gen(y, dt.date):
|
144
145
|
return x == y
|
145
|
-
if
|
146
|
+
if is_instance_gen(x, dt.datetime) and is_instance_gen(y, dt.datetime):
|
146
147
|
return are_equal_datetimes(x, y, strict=strict)
|
147
148
|
raise AreEqualDatesOrDateTimesError(x=x, y=y)
|
148
149
|
|
@@ -210,7 +211,7 @@ def are_equal_months(x: DateOrMonth, y: DateOrMonth, /) -> bool:
|
|
210
211
|
|
211
212
|
def check_date_not_datetime(date: dt.date, /) -> None:
|
212
213
|
"""Check if a date is not a datetime."""
|
213
|
-
if not
|
214
|
+
if not is_instance_gen(date, dt.date):
|
214
215
|
raise CheckDateNotDateTimeError(date=date)
|
215
216
|
|
216
217
|
|
@@ -604,14 +605,6 @@ YEAR = get_years(n=1)
|
|
604
605
|
##
|
605
606
|
|
606
607
|
|
607
|
-
def is_instance_date_not_datetime(obj: Any, /) -> TypeGuard[dt.date]:
|
608
|
-
"""Check if an object is a date, and not a datetime."""
|
609
|
-
return isinstance(obj, dt.date) and not isinstance(obj, dt.datetime)
|
610
|
-
|
611
|
-
|
612
|
-
##
|
613
|
-
|
614
|
-
|
615
608
|
def is_integral_timedelta(timedelta: dt.timedelta, /) -> bool:
|
616
609
|
"""Check if a timedelta is integral."""
|
617
610
|
return (timedelta.seconds == 0) and (timedelta.microseconds == 0)
|
@@ -628,14 +621,6 @@ def is_local_datetime(obj: Any, /) -> TypeGuard[dt.datetime]:
|
|
628
621
|
##
|
629
622
|
|
630
623
|
|
631
|
-
def is_subclass_date_not_datetime(cls: type[Any], /) -> TypeGuard[type[dt.date]]:
|
632
|
-
"""Check if a class is a date, and not a datetime."""
|
633
|
-
return issubclass(cls, dt.date) and not issubclass(cls, dt.datetime)
|
634
|
-
|
635
|
-
|
636
|
-
##
|
637
|
-
|
638
|
-
|
639
624
|
_FRIDAY = 5
|
640
625
|
|
641
626
|
|
@@ -1359,10 +1344,8 @@ __all__ = [
|
|
1359
1344
|
"get_today_local",
|
1360
1345
|
"get_today_tokyo",
|
1361
1346
|
"get_years",
|
1362
|
-
"is_instance_date_not_datetime",
|
1363
1347
|
"is_integral_timedelta",
|
1364
1348
|
"is_local_datetime",
|
1365
|
-
"is_subclass_date_not_datetime",
|
1366
1349
|
"is_weekday",
|
1367
1350
|
"is_zero_time",
|
1368
1351
|
"is_zoned_datetime",
|
utilities/functions.py
CHANGED
@@ -22,7 +22,6 @@ from typing import (
|
|
22
22
|
Literal,
|
23
23
|
TypeGuard,
|
24
24
|
TypeVar,
|
25
|
-
assert_never,
|
26
25
|
cast,
|
27
26
|
overload,
|
28
27
|
override,
|
@@ -660,28 +659,6 @@ def is_hashable(obj: Any, /) -> TypeGuard[Hashable]:
|
|
660
659
|
##
|
661
660
|
|
662
661
|
|
663
|
-
def is_instance_not_bool_int(
|
664
|
-
obj: Any, class_or_tuple: TypeLike[Any], /
|
665
|
-
) -> TypeGuard[int]:
|
666
|
-
"""Check if an instance relationship holds, except bool<int."""
|
667
|
-
match class_or_tuple:
|
668
|
-
case type() as type_:
|
669
|
-
return _is_instance_not_bool_int(obj, type_)
|
670
|
-
case tuple() as types:
|
671
|
-
return any(_is_instance_not_bool_int(obj, p) for p in types)
|
672
|
-
case _ as never:
|
673
|
-
assert_never(never)
|
674
|
-
|
675
|
-
|
676
|
-
def _is_instance_not_bool_int(obj: Any, type_: type[Any], /) -> bool:
|
677
|
-
return isinstance(obj, type_) and not (
|
678
|
-
isinstance(obj, bool) and issubclass(type_, int) and not issubclass(type_, bool)
|
679
|
-
)
|
680
|
-
|
681
|
-
|
682
|
-
##
|
683
|
-
|
684
|
-
|
685
662
|
@overload
|
686
663
|
def is_iterable_of(obj: Any, cls: type[_T], /) -> TypeGuard[Iterable[_T]]: ...
|
687
664
|
@overload
|
@@ -796,28 +773,6 @@ def is_string_mapping(obj: Any, /) -> TypeGuard[StrMapping]:
|
|
796
773
|
##
|
797
774
|
|
798
775
|
|
799
|
-
def is_subclass_not_bool_int(cls: type[Any], class_or_tuple: TypeLike[Any], /) -> bool:
|
800
|
-
"""Check if a subclass relationship holds, except bool<int."""
|
801
|
-
match class_or_tuple:
|
802
|
-
case type() as parent:
|
803
|
-
return _is_subclass_int_not_bool_one(cls, parent)
|
804
|
-
case tuple() as parents:
|
805
|
-
return any(_is_subclass_int_not_bool_one(cls, p) for p in parents)
|
806
|
-
case _ as never:
|
807
|
-
assert_never(never)
|
808
|
-
|
809
|
-
|
810
|
-
def _is_subclass_int_not_bool_one(cls: type[Any], parent: type[Any], /) -> bool:
|
811
|
-
return issubclass(cls, parent) and not (
|
812
|
-
issubclass(cls, bool)
|
813
|
-
and issubclass(parent, int)
|
814
|
-
and not issubclass(parent, bool)
|
815
|
-
)
|
816
|
-
|
817
|
-
|
818
|
-
##
|
819
|
-
|
820
|
-
|
821
776
|
def is_tuple(obj: Any, /) -> TypeGuard[tuple[Any, ...]]:
|
822
777
|
"""Check if an object is a tuple or string mapping."""
|
823
778
|
return make_isinstance(tuple)(obj)
|
@@ -1075,7 +1030,6 @@ __all__ = [
|
|
1075
1030
|
"is_dataclass_class",
|
1076
1031
|
"is_dataclass_instance",
|
1077
1032
|
"is_hashable",
|
1078
|
-
"is_instance_not_bool_int",
|
1079
1033
|
"is_iterable_of",
|
1080
1034
|
"is_none",
|
1081
1035
|
"is_not_none",
|
@@ -1083,7 +1037,6 @@ __all__ = [
|
|
1083
1037
|
"is_sized",
|
1084
1038
|
"is_sized_not_str",
|
1085
1039
|
"is_string_mapping",
|
1086
|
-
"is_subclass_not_bool_int",
|
1087
1040
|
"is_tuple",
|
1088
1041
|
"is_tuple_or_str_mapping",
|
1089
1042
|
"make_isinstance",
|
utilities/orjson.py
CHANGED
utilities/parse.py
CHANGED
@@ -9,12 +9,7 @@ from re import DOTALL
|
|
9
9
|
from types import NoneType
|
10
10
|
from typing import TYPE_CHECKING, Any, override
|
11
11
|
|
12
|
-
from utilities.datetime import (
|
13
|
-
is_instance_date_not_datetime,
|
14
|
-
is_subclass_date_not_datetime,
|
15
|
-
)
|
16
12
|
from utilities.enum import ParseEnumError, parse_enum
|
17
|
-
from utilities.functions import is_subclass_not_bool_int
|
18
13
|
from utilities.iterables import OneEmptyError, OneNonUniqueError, one, one_str
|
19
14
|
from utilities.math import ParseNumberError, parse_number
|
20
15
|
from utilities.re import ExtractGroupError, extract_group
|
@@ -36,10 +31,12 @@ from utilities.typing import (
|
|
36
31
|
get_args,
|
37
32
|
is_dict_type,
|
38
33
|
is_frozenset_type,
|
34
|
+
is_instance_gen,
|
39
35
|
is_list_type,
|
40
36
|
is_literal_type,
|
41
37
|
is_optional_type,
|
42
38
|
is_set_type,
|
39
|
+
is_subclass_gen,
|
43
40
|
is_tuple_type,
|
44
41
|
is_union_type,
|
45
42
|
)
|
@@ -169,12 +166,12 @@ def _parse_object_type(
|
|
169
166
|
raise _ParseObjectParseError(type_=cls, text=text) from None
|
170
167
|
if issubclass(cls, str):
|
171
168
|
return text
|
172
|
-
if
|
169
|
+
if is_subclass_gen(cls, bool):
|
173
170
|
try:
|
174
171
|
return parse_bool(text)
|
175
172
|
except ParseBoolError:
|
176
173
|
raise _ParseObjectParseError(type_=cls, text=text) from None
|
177
|
-
if
|
174
|
+
if is_subclass_gen(cls, int):
|
178
175
|
try:
|
179
176
|
return int(text)
|
180
177
|
except ValueError:
|
@@ -201,14 +198,14 @@ def _parse_object_type(
|
|
201
198
|
return parse_version(text)
|
202
199
|
except ParseVersionError:
|
203
200
|
raise _ParseObjectParseError(type_=cls, text=text) from None
|
204
|
-
if
|
201
|
+
if is_subclass_gen(cls, dt.date):
|
205
202
|
from utilities.whenever import ParseDateError, parse_date
|
206
203
|
|
207
204
|
try:
|
208
205
|
return parse_date(text)
|
209
206
|
except ParseDateError:
|
210
207
|
raise _ParseObjectParseError(type_=cls, text=text) from None
|
211
|
-
if
|
208
|
+
if is_subclass_gen(cls, dt.datetime):
|
212
209
|
from utilities.whenever import ParseDateTimeError, parse_datetime
|
213
210
|
|
214
211
|
try:
|
@@ -473,11 +470,11 @@ def serialize_object(
|
|
473
470
|
obj, bool | int | float | str | Path | Sentinel | Version
|
474
471
|
):
|
475
472
|
return str(obj)
|
476
|
-
if
|
473
|
+
if is_instance_gen(obj, dt.date):
|
477
474
|
from utilities.whenever import serialize_date
|
478
475
|
|
479
476
|
return serialize_date(obj)
|
480
|
-
if
|
477
|
+
if is_instance_gen(obj, dt.datetime):
|
481
478
|
from utilities.whenever import serialize_datetime
|
482
479
|
|
483
480
|
return serialize_datetime(obj)
|
utilities/period.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import datetime as dt
|
4
4
|
from dataclasses import dataclass, field
|
5
5
|
from functools import cached_property
|
6
|
+
from itertools import permutations
|
6
7
|
from typing import (
|
7
8
|
TYPE_CHECKING,
|
8
9
|
Generic,
|
@@ -15,10 +16,11 @@ from typing import (
|
|
15
16
|
override,
|
16
17
|
)
|
17
18
|
|
18
|
-
from utilities.datetime import ZERO_TIME
|
19
|
+
from utilities.datetime import ZERO_TIME
|
19
20
|
from utilities.functions import get_class_name
|
20
21
|
from utilities.iterables import OneUniqueNonUniqueError, always_iterable, one_unique
|
21
22
|
from utilities.sentinel import Sentinel, sentinel
|
23
|
+
from utilities.typing import is_instance_gen
|
22
24
|
from utilities.whenever import (
|
23
25
|
serialize_date,
|
24
26
|
serialize_local_datetime,
|
@@ -32,6 +34,7 @@ if TYPE_CHECKING:
|
|
32
34
|
from utilities.iterables import MaybeIterable
|
33
35
|
from utilities.types import DateOrDateTime
|
34
36
|
|
37
|
+
|
35
38
|
type _DateOrDateTime = Literal["date", "datetime"]
|
36
39
|
_TPeriod = TypeVar("_TPeriod", dt.date, dt.datetime)
|
37
40
|
|
@@ -54,9 +57,11 @@ class Period(Generic[_TPeriod]):
|
|
54
57
|
max_duration: dt.timedelta | None = field(default=None, repr=False, kw_only=True)
|
55
58
|
|
56
59
|
def __post_init__(self) -> None:
|
57
|
-
if
|
58
|
-
|
59
|
-
|
60
|
+
if any(
|
61
|
+
is_instance_gen(left, cls) is not is_instance_gen(right, cls)
|
62
|
+
for left, right in permutations([self.start, self.end], 2)
|
63
|
+
for cls in [dt.date, dt.datetime]
|
64
|
+
):
|
60
65
|
raise _PeriodDateAndDateTimeMixedError(start=self.start, end=self.end)
|
61
66
|
for date in [self.start, self.end]:
|
62
67
|
if isinstance(date, dt.datetime):
|
@@ -166,7 +171,7 @@ class Period(Generic[_TPeriod]):
|
|
166
171
|
@cached_property
|
167
172
|
def kind(self) -> _DateOrDateTime:
|
168
173
|
"""The kind of the period."""
|
169
|
-
return "date" if
|
174
|
+
return "date" if is_instance_gen(self.start, dt.date) else "datetime"
|
170
175
|
|
171
176
|
def replace(
|
172
177
|
self,
|
utilities/polars.py
CHANGED
@@ -56,7 +56,6 @@ from polars.exceptions import (
|
|
56
56
|
from polars.testing import assert_frame_equal
|
57
57
|
|
58
58
|
from utilities.dataclasses import _YieldFieldsInstance, yield_fields
|
59
|
-
from utilities.datetime import is_instance_date_not_datetime
|
60
59
|
from utilities.errors import ImpossibleCaseError
|
61
60
|
from utilities.functions import (
|
62
61
|
EnsureIntError,
|
@@ -95,6 +94,7 @@ from utilities.typing import (
|
|
95
94
|
get_args,
|
96
95
|
get_type_hints,
|
97
96
|
is_frozenset_type,
|
97
|
+
is_instance_gen,
|
98
98
|
is_list_type,
|
99
99
|
is_literal_type,
|
100
100
|
is_optional_type,
|
@@ -945,7 +945,7 @@ def dataclass_to_schema(
|
|
945
945
|
dt.date,
|
946
946
|
dt.datetime,
|
947
947
|
}:
|
948
|
-
if
|
948
|
+
if is_instance_gen(field.value, dt.date):
|
949
949
|
dtype = Date
|
950
950
|
else:
|
951
951
|
dtype = _dataclass_to_schema_datetime(field)
|
utilities/sqlalchemy_polars.py
CHANGED
@@ -28,7 +28,6 @@ from sqlalchemy.exc import DuplicateColumnError
|
|
28
28
|
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
29
29
|
|
30
30
|
from utilities.asyncio import timeout_dur
|
31
|
-
from utilities.datetime import is_subclass_date_not_datetime
|
32
31
|
from utilities.functions import identity
|
33
32
|
from utilities.iterables import (
|
34
33
|
CheckDuplicatesError,
|
@@ -49,6 +48,7 @@ from utilities.sqlalchemy import (
|
|
49
48
|
upsert_items,
|
50
49
|
)
|
51
50
|
from utilities.text import snake_case
|
51
|
+
from utilities.typing import is_subclass_gen
|
52
52
|
from utilities.zoneinfo import UTC
|
53
53
|
|
54
54
|
if TYPE_CHECKING:
|
@@ -201,14 +201,14 @@ def _insert_dataframe_check_df_and_db_types(
|
|
201
201
|
dtype: PolarsDataType, db_col_type: type, /
|
202
202
|
) -> bool:
|
203
203
|
return (
|
204
|
-
((dtype == pl.Boolean) and
|
205
|
-
or ((dtype == Date) and
|
206
|
-
or ((dtype == Datetime) and
|
204
|
+
((dtype == pl.Boolean) and is_subclass_gen(db_col_type, bool))
|
205
|
+
or ((dtype == Date) and is_subclass_gen(db_col_type, dt.date))
|
206
|
+
or ((dtype == Datetime) and is_subclass_gen(db_col_type, dt.datetime))
|
207
207
|
or ((dtype == Float64) and issubclass(db_col_type, float))
|
208
|
-
or ((dtype == Int32) and
|
209
|
-
or ((dtype == Int64) and
|
210
|
-
or ((dtype == UInt32) and
|
211
|
-
or ((dtype == UInt64) and
|
208
|
+
or ((dtype == Int32) and is_subclass_gen(db_col_type, int))
|
209
|
+
or ((dtype == Int64) and is_subclass_gen(db_col_type, int))
|
210
|
+
or ((dtype == UInt32) and is_subclass_gen(db_col_type, int))
|
211
|
+
or ((dtype == UInt64) and is_subclass_gen(db_col_type, int))
|
212
212
|
or ((dtype == String) and issubclass(db_col_type, str))
|
213
213
|
)
|
214
214
|
|
@@ -414,15 +414,15 @@ def _select_to_dataframe_map_table_column_type_to_dtype(
|
|
414
414
|
"""Map a table column type to a polars type."""
|
415
415
|
type_use = type_() if isinstance(type_, type) else type_
|
416
416
|
py_type = type_use.python_type
|
417
|
-
if
|
417
|
+
if is_subclass_gen(py_type, bool):
|
418
418
|
return pl.Boolean
|
419
419
|
if issubclass(py_type, bytes):
|
420
420
|
return Binary
|
421
421
|
if issubclass(py_type, decimal.Decimal):
|
422
422
|
return pl.Decimal
|
423
|
-
if
|
423
|
+
if is_subclass_gen(py_type, dt.date):
|
424
424
|
return pl.Date
|
425
|
-
if
|
425
|
+
if is_subclass_gen(py_type, dt.datetime):
|
426
426
|
has_tz: bool = type_use.timezone
|
427
427
|
return zoned_datetime(time_zone=time_zone) if has_tz else Datetime()
|
428
428
|
if issubclass(py_type, dt.time):
|
@@ -431,7 +431,7 @@ def _select_to_dataframe_map_table_column_type_to_dtype(
|
|
431
431
|
return pl.Duration
|
432
432
|
if issubclass(py_type, float):
|
433
433
|
return Float64
|
434
|
-
if
|
434
|
+
if is_subclass_gen(py_type, int):
|
435
435
|
return Int64
|
436
436
|
if issubclass(py_type, UUID | str):
|
437
437
|
return String
|
utilities/typing.py
CHANGED
@@ -14,8 +14,10 @@ from typing import (
|
|
14
14
|
Self,
|
15
15
|
TypeAliasType,
|
16
16
|
TypeGuard,
|
17
|
+
TypeVar,
|
17
18
|
Union, # pyright: ignore[reportDeprecated]
|
18
19
|
get_origin,
|
20
|
+
overload,
|
19
21
|
override,
|
20
22
|
)
|
21
23
|
from typing import get_args as _get_args
|
@@ -27,6 +29,16 @@ from utilities.iterables import unique_everseen
|
|
27
29
|
from utilities.sentinel import Sentinel
|
28
30
|
from utilities.types import StrMapping
|
29
31
|
|
32
|
+
_T = TypeVar("_T")
|
33
|
+
_T1 = TypeVar("_T1")
|
34
|
+
_T2 = TypeVar("_T2")
|
35
|
+
_T3 = TypeVar("_T3")
|
36
|
+
_T4 = TypeVar("_T4")
|
37
|
+
_T5 = TypeVar("_T5")
|
38
|
+
|
39
|
+
|
40
|
+
##
|
41
|
+
|
30
42
|
|
31
43
|
def contains_self(obj: Any, /) -> bool:
|
32
44
|
"""Check if an annotation contains `Self`."""
|
@@ -65,6 +77,52 @@ def _get_literal_elements_inner(obj: Any, /) -> list[Any]:
|
|
65
77
|
##
|
66
78
|
|
67
79
|
|
80
|
+
def get_type_classes(obj: Any, /) -> tuple[type[Any], ...]:
|
81
|
+
"""Get the type classes from a type/tuple/Union type."""
|
82
|
+
types: Sequence[type[Any]] = []
|
83
|
+
if isinstance(obj, type):
|
84
|
+
types.append(obj)
|
85
|
+
elif isinstance(obj, tuple):
|
86
|
+
for arg in obj:
|
87
|
+
if isinstance(arg, type):
|
88
|
+
types.append(arg)
|
89
|
+
elif isinstance(arg, tuple):
|
90
|
+
types.extend(get_type_classes(arg))
|
91
|
+
elif is_union_type(arg):
|
92
|
+
types.extend(get_union_type_classes(arg))
|
93
|
+
else:
|
94
|
+
raise _GetTypeClassesTupleError(obj=obj, inner=arg)
|
95
|
+
elif is_union_type(obj):
|
96
|
+
types.extend(get_union_type_classes(obj))
|
97
|
+
else:
|
98
|
+
raise _GetTypeClassesTypeError(obj=obj)
|
99
|
+
return tuple(types)
|
100
|
+
|
101
|
+
|
102
|
+
@dataclass(kw_only=True, slots=True)
|
103
|
+
class GetTypeClassesError(Exception):
|
104
|
+
obj: Any
|
105
|
+
|
106
|
+
|
107
|
+
@dataclass(kw_only=True, slots=True)
|
108
|
+
class _GetTypeClassesTypeError(GetTypeClassesError):
|
109
|
+
@override
|
110
|
+
def __str__(self) -> str:
|
111
|
+
return f"Object must be a type, tuple or Union type; got {self.obj}"
|
112
|
+
|
113
|
+
|
114
|
+
@dataclass(kw_only=True, slots=True)
|
115
|
+
class _GetTypeClassesTupleError(GetTypeClassesError):
|
116
|
+
inner: Any
|
117
|
+
|
118
|
+
@override
|
119
|
+
def __str__(self) -> str:
|
120
|
+
return f"Tuple must contain types, tuples or Union types only; got {self.inner}"
|
121
|
+
|
122
|
+
|
123
|
+
##
|
124
|
+
|
125
|
+
|
68
126
|
def get_type_hints(
|
69
127
|
obj: Any,
|
70
128
|
/,
|
@@ -96,8 +154,9 @@ def get_type_hints(
|
|
96
154
|
|
97
155
|
|
98
156
|
def get_union_type_classes(obj: Any, /) -> tuple[type[Any], ...]:
|
157
|
+
"""Get the type classes from a Union type."""
|
99
158
|
if not is_union_type(obj):
|
100
|
-
raise
|
159
|
+
raise _GetUnionTypeClassesUnionTypeError(obj=obj)
|
101
160
|
types_: Sequence[type[Any]] = []
|
102
161
|
for arg in get_args(obj):
|
103
162
|
if isinstance(arg, type):
|
@@ -105,7 +164,7 @@ def get_union_type_classes(obj: Any, /) -> tuple[type[Any], ...]:
|
|
105
164
|
elif is_union_type(arg):
|
106
165
|
types_.extend(get_union_type_classes(arg))
|
107
166
|
else:
|
108
|
-
raise
|
167
|
+
raise _GetUnionTypeClassesInternalTypeError(obj=obj, inner=arg)
|
109
168
|
return tuple(types_)
|
110
169
|
|
111
170
|
|
@@ -115,14 +174,14 @@ class GetUnionTypeClassesError(Exception):
|
|
115
174
|
|
116
175
|
|
117
176
|
@dataclass(kw_only=True, slots=True)
|
118
|
-
class
|
177
|
+
class _GetUnionTypeClassesUnionTypeError(GetUnionTypeClassesError):
|
119
178
|
@override
|
120
179
|
def __str__(self) -> str:
|
121
180
|
return f"Object must be a Union type; got {self.obj}"
|
122
181
|
|
123
182
|
|
124
183
|
@dataclass(kw_only=True, slots=True)
|
125
|
-
class
|
184
|
+
class _GetUnionTypeClassesInternalTypeError(GetUnionTypeClassesError):
|
126
185
|
inner: Any
|
127
186
|
|
128
187
|
@override
|
@@ -149,6 +208,50 @@ def is_frozenset_type(obj: Any, /) -> bool:
|
|
149
208
|
##
|
150
209
|
|
151
210
|
|
211
|
+
@overload
|
212
|
+
def is_instance_gen(obj: Any, type_: type[_T], /) -> TypeGuard[_T]: ...
|
213
|
+
@overload
|
214
|
+
def is_instance_gen(obj: Any, type_: tuple[_T1], /) -> TypeGuard[_T1]: ...
|
215
|
+
@overload
|
216
|
+
def is_instance_gen(obj: Any, type_: tuple[_T1, _T2], /) -> TypeGuard[_T1 | _T2]: ...
|
217
|
+
@overload
|
218
|
+
def is_instance_gen(
|
219
|
+
obj: Any, type_: tuple[_T1, _T2, _T3], /
|
220
|
+
) -> TypeGuard[_T1 | _T2 | _T3]: ...
|
221
|
+
@overload
|
222
|
+
def is_instance_gen(
|
223
|
+
obj: Any, type_: tuple[_T1, _T2, _T3, _T4], /
|
224
|
+
) -> TypeGuard[_T1 | _T2 | _T3 | _T4]: ...
|
225
|
+
@overload
|
226
|
+
def is_instance_gen(
|
227
|
+
obj: Any, type_: tuple[_T1, _T2, _T3, _T4, _T5], /
|
228
|
+
) -> TypeGuard[_T1 | _T2 | _T3 | _T4 | _T5]: ...
|
229
|
+
@overload
|
230
|
+
def is_instance_gen(obj: Any, type_: Any, /) -> bool: ...
|
231
|
+
def is_instance_gen(obj: Any, type_: Any, /) -> bool:
|
232
|
+
"""Check if an instance relationship holds, except bool<int."""
|
233
|
+
return any(_is_instance_gen_one(obj, t) for t in get_type_classes(type_))
|
234
|
+
|
235
|
+
|
236
|
+
def _is_instance_gen_one(obj: Any, type_: type[_T], /) -> TypeGuard[_T]:
|
237
|
+
return (
|
238
|
+
isinstance(obj, type_)
|
239
|
+
and not (
|
240
|
+
isinstance(obj, bool)
|
241
|
+
and issubclass(type_, int)
|
242
|
+
and not issubclass(type_, bool)
|
243
|
+
)
|
244
|
+
and not (
|
245
|
+
isinstance(obj, dt.datetime)
|
246
|
+
and issubclass(type_, dt.date)
|
247
|
+
and not issubclass(type_, dt.datetime)
|
248
|
+
)
|
249
|
+
)
|
250
|
+
|
251
|
+
|
252
|
+
##
|
253
|
+
|
254
|
+
|
152
255
|
def is_list_type(obj: Any, /) -> bool:
|
153
256
|
"""Check if an object is a list type annotation."""
|
154
257
|
return _is_annotation_of_type(obj, list)
|
@@ -222,6 +325,56 @@ def is_set_type(obj: Any, /) -> bool:
|
|
222
325
|
##
|
223
326
|
|
224
327
|
|
328
|
+
@overload
|
329
|
+
def is_subclass_gen(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]: ...
|
330
|
+
@overload
|
331
|
+
def is_subclass_gen(
|
332
|
+
cls: type[Any], parent: tuple[type[_T1]], /
|
333
|
+
) -> TypeGuard[type[_T1]]: ...
|
334
|
+
@overload
|
335
|
+
def is_subclass_gen(
|
336
|
+
cls: type[Any], parent: tuple[type[_T1], type[_T2]], /
|
337
|
+
) -> TypeGuard[type[_T1 | _T2]]: ...
|
338
|
+
@overload
|
339
|
+
def is_subclass_gen(
|
340
|
+
cls: type[Any], parent: tuple[type[_T1], type[_T2], type[_T3]], /
|
341
|
+
) -> TypeGuard[type[_T1 | _T2 | _T3]]: ...
|
342
|
+
@overload
|
343
|
+
def is_subclass_gen(
|
344
|
+
cls: type[Any], parent: tuple[type[_T1], type[_T2], type[_T3], type[_T4]], /
|
345
|
+
) -> TypeGuard[type[_T1 | _T2 | _T3 | _T4]]: ...
|
346
|
+
@overload
|
347
|
+
def is_subclass_gen(
|
348
|
+
cls: type[Any],
|
349
|
+
parent: tuple[type[_T1], type[_T2], type[_T3], type[_T4], type[_T5]],
|
350
|
+
/,
|
351
|
+
) -> TypeGuard[type[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
|
352
|
+
@overload
|
353
|
+
def is_subclass_gen(cls: type[Any], parent: Any, /) -> bool: ...
|
354
|
+
def is_subclass_gen(cls: type[Any], parent: Any, /) -> bool:
|
355
|
+
"""Generalized `issubclass`."""
|
356
|
+
return any(_is_subclass_gen_one(cls, p) for p in get_type_classes(parent))
|
357
|
+
|
358
|
+
|
359
|
+
def _is_subclass_gen_one(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]:
|
360
|
+
return (
|
361
|
+
issubclass(cls, parent)
|
362
|
+
and not (
|
363
|
+
issubclass(cls, bool)
|
364
|
+
and issubclass(parent, int)
|
365
|
+
and not issubclass(parent, bool)
|
366
|
+
)
|
367
|
+
and not (
|
368
|
+
issubclass(cls, dt.datetime)
|
369
|
+
and issubclass(parent, dt.date)
|
370
|
+
and not issubclass(parent, dt.datetime)
|
371
|
+
)
|
372
|
+
)
|
373
|
+
|
374
|
+
|
375
|
+
##
|
376
|
+
|
377
|
+
|
225
378
|
def is_tuple_type(obj: Any, /) -> bool:
|
226
379
|
"""Check if an object is a tuple type annotation."""
|
227
380
|
return _is_annotation_of_type(obj, tuple)
|
@@ -247,13 +400,16 @@ def _is_annotation_of_type(obj: Any, origin: Any, /) -> bool:
|
|
247
400
|
|
248
401
|
|
249
402
|
__all__ = [
|
403
|
+
"GetTypeClassesError",
|
250
404
|
"GetUnionTypeClassesError",
|
251
405
|
"contains_self",
|
252
406
|
"get_literal_elements",
|
407
|
+
"get_type_classes",
|
253
408
|
"get_type_hints",
|
254
409
|
"get_union_type_classes",
|
255
410
|
"is_dict_type",
|
256
411
|
"is_frozenset_type",
|
412
|
+
"is_instance_gen",
|
257
413
|
"is_list_type",
|
258
414
|
"is_literal_type",
|
259
415
|
"is_mapping_type",
|
@@ -262,6 +418,7 @@ __all__ = [
|
|
262
418
|
"is_optional_type",
|
263
419
|
"is_sequence_type",
|
264
420
|
"is_set_type",
|
421
|
+
"is_subclass_gen",
|
265
422
|
"is_tuple_type",
|
266
423
|
"is_union_type",
|
267
424
|
]
|
File without changes
|
File without changes
|