dycw-utilities 0.112.1__py3-none-any.whl → 0.112.3__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.112.1
3
+ Version: 0.112.3
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=hV5Btasavyruats4m9DGhq8YssGlUGNVeNJRvxXPIiM,60
1
+ utilities/__init__.py,sha256=z7R-tij8PUsszk8bX9FviCXCNFWurmc0egXzyuNqIdY,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
@@ -46,7 +46,7 @@ utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
46
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=lr0l3JE2d5Rj1UbbFtagcv1z3Vw5YXjSfAwzVa9cFXw,58374
49
+ utilities/polars.py,sha256=q8a2hTX9tyPxtxUarIm2BxlnckIwF1RCpcJz03OoIoY,58458
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
@@ -62,7 +62,7 @@ utilities/redis.py,sha256=fAUbfOlCmxcxhh47PXQX63w0CU5iOFKfdUJ7jDn9ntM,22096
62
62
  utilities/reprlib.py,sha256=Re9bk3n-kC__9DxQmRlevqFA86pE6TtVfWjUgpbVOv0,1849
63
63
  utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
64
64
  utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
65
- utilities/sentinel.py,sha256=0X1GWWcnPxGCo7wDgVTOfhU5iKlXdvVZudlMM0iezDw,1246
65
+ utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
66
66
  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
@@ -73,12 +73,12 @@ utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
73
73
  utilities/sys.py,sha256=h0Xr7Vj86wNalvwJVP1wj5Y0kD_VWm1vzuXZ_jw94mE,2743
74
74
  utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
75
75
  utilities/tenacity.py,sha256=1PUvODiBVgeqIh7G5TRt5WWMSqjLYkEqP53itT97WQc,4914
76
- utilities/text.py,sha256=hfcBKF22fKT6s_U-ZdP-g5TjFQ0-NrIrQdvIwERWT80,10971
76
+ utilities/text.py,sha256=c65sonE-vMJtBR8-LIntXUqku7wDQC6p4z69DOSur7o,10947
77
77
  utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
78
78
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
79
79
  utilities/traceback.py,sha256=secexUnBsecfWV4ZuqP1W4pGF3prOeO1CRyJK-8zQDU,27402
80
80
  utilities/types.py,sha256=kVY71hZkcnyYNIlYSse0mLm8yeP3OBkzhDPMME6jXxo,18126
81
- utilities/typing.py,sha256=jtc6EiGZGG0E745jo3NeLqo_HdHt7Zdaco3kCAEWIYU,11177
81
+ utilities/typing.py,sha256=Tppx84LCtZFX8oOzDb2PeUIv9tjNqFWQLuMadN_rAes,13514
82
82
  utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
83
83
  utilities/tzlocal.py,sha256=3upDNFBvGh1l9njmLR2z2S6K6VxQSb7QizYGUbAH3JU,960
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=iLRP_-8CZtBpHKbGZGu-kjSMg1ZubJ-VSmgSy7Eudxw,17787
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
89
  utilities/zoneinfo.py,sha256=-Xm57PMMwDTYpxJdkiJG13wnbwK--I7XItBh5WVhD-o,1874
90
- dycw_utilities-0.112.1.dist-info/METADATA,sha256=0K3rDMRdIWtMVqAXUo7sXWuZjUFz7brciAZ2SbsYANg,13004
91
- dycw_utilities-0.112.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.112.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.112.1.dist-info/RECORD,,
90
+ dycw_utilities-0.112.3.dist-info/METADATA,sha256=t7BNtV2r3OiHx0TWQ9JFxwyrFWjwRWFsAZOMD8HKIDA,13004
91
+ dycw_utilities-0.112.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.112.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.112.3.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.112.1"
3
+ __version__ = "0.112.3"
utilities/polars.py CHANGED
@@ -941,10 +941,9 @@ def dataclass_to_schema(
941
941
  dtype = struct_dtype(**dtypes)
942
942
  elif field.type_ is dt.datetime:
943
943
  dtype = _dataclass_to_schema_datetime(field)
944
- elif is_union_type(field.type_) and set(get_args(field.type_)) == {
945
- dt.date,
946
- dt.datetime,
947
- }:
944
+ elif is_union_type(field.type_) and set(
945
+ get_args(field.type_, optional_drop_none=True)
946
+ ) == {dt.date, dt.datetime}:
948
947
  if is_instance_gen(field.value, dt.date):
949
948
  dtype = Date
950
949
  else:
@@ -1002,7 +1001,7 @@ def _dataclass_to_schema_one(
1002
1001
  if is_literal_type(obj):
1003
1002
  return pl.Enum(get_args(obj))
1004
1003
  if is_optional_type(obj):
1005
- inner_type = one(get_args(obj))
1004
+ inner_type = one(get_args(obj, optional_drop_none=True))
1006
1005
  return _dataclass_to_schema_one(inner_type, globalns=globalns, localns=localns)
1007
1006
  msg = f"{obj=}"
1008
1007
  raise NotImplementedError(msg)
@@ -1732,7 +1731,9 @@ def _struct_from_dataclass_one(
1732
1731
  ):
1733
1732
  return String
1734
1733
  if is_optional_type(ann):
1735
- return _struct_from_dataclass_one(one(get_args(ann)), time_zone=time_zone)
1734
+ return _struct_from_dataclass_one(
1735
+ one(get_args(ann, optional_drop_none=True)), time_zone=time_zone
1736
+ )
1736
1737
  if is_frozenset_type(ann) or is_list_type(ann) or is_set_type(ann):
1737
1738
  return List(_struct_from_dataclass_one(one(get_args(ann)), time_zone=time_zone))
1738
1739
  raise _StructFromDataClassTypeError(ann=ann)
utilities/sentinel.py CHANGED
@@ -40,7 +40,7 @@ sentinel = Sentinel()
40
40
 
41
41
  def parse_sentinel(text: str, /) -> Sentinel:
42
42
  """Parse text into the Sentinel value."""
43
- if text == "" or search("Sentinel", text, flags=IGNORECASE):
43
+ if search("^(|sentinel|<sentinel>)$", text, flags=IGNORECASE):
44
44
  return sentinel
45
45
  raise ParseSentinelError(text=text)
46
46
 
utilities/text.py CHANGED
@@ -25,9 +25,9 @@ DEFAULT_SEPARATOR = ","
25
25
 
26
26
  def parse_bool(text: str, /) -> bool:
27
27
  """Parse text into a boolean value."""
28
- if text == "0" or search("false", text, flags=IGNORECASE):
28
+ if search(r"^(0|False)$", text, flags=IGNORECASE):
29
29
  return False
30
- if text == "1" or search("true", text, flags=IGNORECASE):
30
+ if search(r"^(1|True)$", text, flags=IGNORECASE):
31
31
  return True
32
32
  raise ParseBoolError(text=text)
33
33
 
@@ -46,7 +46,7 @@ class ParseBoolError(Exception):
46
46
 
47
47
  def parse_none(text: str, /) -> None:
48
48
  """Parse text into the None value."""
49
- if text == "" or search("None", text, flags=IGNORECASE):
49
+ if search(r"^(|None)$", text, flags=IGNORECASE):
50
50
  return
51
51
  raise ParseNoneError(text=text)
52
52
 
utilities/typing.py CHANGED
@@ -48,14 +48,14 @@ def contains_self(obj: Any, /) -> bool:
48
48
  ##
49
49
 
50
50
 
51
- def get_args(obj: Any, /) -> tuple[Any, ...]:
51
+ def get_args(obj: Any, /, *, optional_drop_none: bool = False) -> tuple[Any, ...]:
52
52
  """Get the arguments of an annotation."""
53
53
  if isinstance(obj, TypeAliasType):
54
54
  return get_args(obj.__value__)
55
- if is_optional_type(obj):
56
- args = _get_args(obj)
57
- return tuple(a for a in args if a is not NoneType)
58
- return _get_args(obj)
55
+ args = _get_args(obj)
56
+ if is_optional_type(obj) and optional_drop_none:
57
+ args = tuple(a for a in args if a is not NoneType)
58
+ return args
59
59
 
60
60
 
61
61
  ##
@@ -230,10 +230,26 @@ def is_instance_gen(
230
230
  def is_instance_gen(obj: Any, type_: Any, /) -> bool: ...
231
231
  def is_instance_gen(obj: Any, type_: Any, /) -> bool:
232
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_))
233
+ # parent
234
+ if isinstance(type_, tuple):
235
+ return any(is_instance_gen(obj, t) for t in type_)
236
+ if is_literal_type(type_):
237
+ return obj in get_args(type_)
238
+ if is_union_type(type_):
239
+ return any(is_instance_gen(obj, t) for t in get_args(type_))
240
+ # tuple vs tuple
241
+ if isinstance(obj, tuple) and is_tuple_type(type_):
242
+ type_args = get_args(type_)
243
+ return (len(obj) == len(type_args)) and all(
244
+ is_instance_gen(o, t) for o, t in zip(obj, type_args, strict=True)
245
+ )
246
+ # basic
247
+ if isinstance(type_, type):
248
+ return any(_is_instance_gen_type(obj, t) for t in get_type_classes(type_))
249
+ raise IsInstanceGenError(obj=obj, type_=type_)
234
250
 
235
251
 
236
- def _is_instance_gen_one(obj: Any, type_: type[_T], /) -> TypeGuard[_T]:
252
+ def _is_instance_gen_type(obj: Any, type_: type[_T], /) -> TypeGuard[_T]:
237
253
  return (
238
254
  isinstance(obj, type_)
239
255
  and not (
@@ -249,6 +265,16 @@ def _is_instance_gen_one(obj: Any, type_: type[_T], /) -> TypeGuard[_T]:
249
265
  )
250
266
 
251
267
 
268
+ @dataclass(kw_only=True, slots=True)
269
+ class IsInstanceGenError(Exception):
270
+ obj: Any
271
+ type_: Any
272
+
273
+ @override
274
+ def __str__(self) -> str:
275
+ return f"Invalid arguments; got {self.obj!r} and {self.type_!r}"
276
+
277
+
252
278
  ##
253
279
 
254
280
 
@@ -350,13 +376,41 @@ def is_subclass_gen(
350
376
  /,
351
377
  ) -> TypeGuard[type[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
352
378
  @overload
353
- def is_subclass_gen(cls: type[Any], parent: Any, /) -> bool: ...
354
- def is_subclass_gen(cls: type[Any], parent: Any, /) -> bool:
379
+ def is_subclass_gen(cls: Any, parent: Any, /) -> bool: ...
380
+ def is_subclass_gen(cls: Any, parent: Any, /) -> bool:
355
381
  """Generalized `issubclass`."""
356
- return any(_is_subclass_gen_one(cls, p) for p in get_type_classes(parent))
382
+ # child
383
+ if isinstance(cls, tuple):
384
+ return all(is_subclass_gen(c, parent) for c in cls)
385
+ if is_literal_type(cls):
386
+ types = tuple(map(type, get_args(cls)))
387
+ return (
388
+ is_literal_type(parent) and set(get_args(cls)).issubset(get_args(parent))
389
+ ) or is_subclass_gen(types, parent)
390
+ if is_union_type(cls):
391
+ return all(is_subclass_gen(c, parent) for c in get_args(cls))
392
+ # parent
393
+ if isinstance(parent, tuple):
394
+ return any(is_subclass_gen(cls, p) for p in parent)
395
+ if is_literal_type(parent):
396
+ return is_literal_type(cls) and set(get_args(cls)).issubset(get_args(parent))
397
+ if is_union_type(parent):
398
+ return any(is_subclass_gen(cls, p) for p in get_args(parent))
399
+ # tuple vs tuple
400
+ if is_tuple_type(cls) and is_tuple_type(parent):
401
+ cls_args, parent_args = get_args(cls), get_args(parent)
402
+ return (len(cls_args) == len(parent_args)) and all(
403
+ is_subclass_gen(c, p) for c, p in zip(cls_args, parent_args, strict=True)
404
+ )
405
+ if is_tuple_type(cls) is not is_tuple_type(parent):
406
+ return False
407
+ # basic
408
+ if isinstance(cls, type):
409
+ return any(_is_subclass_gen_type(cls, p) for p in get_type_classes(parent))
410
+ raise IsSubclassGenError(cls=cls)
357
411
 
358
412
 
359
- def _is_subclass_gen_one(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]:
413
+ def _is_subclass_gen_type(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]:
360
414
  return (
361
415
  issubclass(cls, parent)
362
416
  and not (
@@ -372,6 +426,15 @@ def _is_subclass_gen_one(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[
372
426
  )
373
427
 
374
428
 
429
+ @dataclass(kw_only=True, slots=True)
430
+ class IsSubclassGenError(Exception):
431
+ cls: Any
432
+
433
+ @override
434
+ def __str__(self) -> str:
435
+ return f"Argument must be a class; got {self.cls!r}"
436
+
437
+
375
438
  ##
376
439
 
377
440
 
@@ -402,6 +465,8 @@ def _is_annotation_of_type(obj: Any, origin: Any, /) -> bool:
402
465
  __all__ = [
403
466
  "GetTypeClassesError",
404
467
  "GetUnionTypeClassesError",
468
+ "IsInstanceGenError",
469
+ "IsSubclassGenError",
405
470
  "contains_self",
406
471
  "get_literal_elements",
407
472
  "get_type_classes",