dycw-utilities 0.155.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.155.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
@@ -13,7 +13,6 @@ 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
15
  Requires-Dist: hypothesis<6.138,>=6.137.1; extra == 'test'
16
- Requires-Dist: pudb<2025.2,>=2025.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=OFslnM101fl40brwBMTE9hAplv30EfT_NmRkbGPfu-g,60
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=hoa1Szk3oLa07W4nl6Uhy2vsToIxx8AYjJg9PLc9JvQ,43516
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
@@ -45,7 +45,7 @@ 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=DxGDEw3KRxQJkuJ1S0fduXfCiyXJ-9mul0kYj3lFt_Q,78530
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
@@ -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=TjO7em98FDFLvROZ7gi2UJftFWNuSTkbCrf7mk-fg28,9416
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=Z-_XDaWyT_6wIo3qfNK-hvRlzxP2Jxa9PgXzm5rDYRA,13790
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=gPnFKWws4_tjiHPLzX1AukSwDjfMIO9Iim0DDNQyAqY,57532
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.155.0.dist-info/METADATA,sha256=AFafkgKG-CWYCGGKjJxAXn1dYS1wM_OkjeV2m7ZnaQs,1696
91
- dycw_utilities-0.155.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.155.0.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
93
- dycw_utilities-0.155.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.155.0.dist-info/RECORD,,
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
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.155.0"
3
+ __version__ = "0.155.1"
utilities/hypothesis.py CHANGED
@@ -236,6 +236,7 @@ def date_time_deltas(
236
236
  min_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
237
237
  max_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
238
238
  parsable: MaybeSearchStrategy[bool] = False,
239
+ nativable: MaybeSearchStrategy[bool] = False,
239
240
  ) -> DateTimeDelta:
240
241
  """Strategy for generating date deltas."""
241
242
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
@@ -257,7 +258,13 @@ def date_time_deltas(
257
258
  if draw2(draw, parsable):
258
259
  min_nanos = max(min_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MIN))
259
260
  max_nanos = min(max_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MAX))
260
- nanos = draw(integers(min_value=min_nanos, max_value=max_nanos))
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))
261
268
  return to_date_time_delta(nanos)
262
269
 
263
270
 
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,
@@ -50,9 +51,9 @@ from polars.exceptions import (
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 _YieldFieldsInstance, yield_fields
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
- else: # pragma: no cover
1061
- msg = f"{is_path=}, f{is_uuid=}"
1062
- raise NotImplementedError(msg)
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 is bool:
1142
- return Boolean
1143
- if obj is int:
1144
- return Int64
1145
- if obj is float:
1146
- return Float64
1147
- if obj is str:
1148
- return String
1149
- if obj is dt.date:
1150
- return Date
1151
- if obj in {Path, UUID}:
1152
- return Object
1153
- if isinstance(obj, type) and issubclass(obj, enum.Enum):
1154
- return pl.Enum([e.name for e in obj])
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 = {bool: Boolean, dt.date: Date, float: Float64, int: Int64, str: String}
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/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
- dt,
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",