dycw-utilities 0.126.5__py3-none-any.whl → 0.126.7__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.126.5
3
+ Version: 0.126.7
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,10 +1,10 @@
1
- utilities/__init__.py,sha256=VDrLXzcqWS0oyXCkhWVW6135Pzimt4LjwrWZrMWdt-I,60
1
+ utilities/__init__.py,sha256=k4Q-FdII9KXkuYXSZgXMDtdGhp9lcKoxpPtcuL_YIlQ,60
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/asyncio.py,sha256=K5Kj7rsM0nA17-b7d7mrNgPR1U_NbkfQmTruq5LBLRA,51778
4
4
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
5
5
  utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
6
6
  utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
7
- utilities/click.py,sha256=SRVkUkWxyO_7AaYvvDl0hNCzdpneX04lf1O97i9MfPw,14311
7
+ utilities/click.py,sha256=CianelgUj_M2SBpuvhmRPKOGxuvLM04FHllUWtL7hok,14647
8
8
  utilities/concurrent.py,sha256=s2scTEd2AhXVTW4hpASU2qxV_DiVLALfms55cCQzCvM,2886
9
9
  utilities/contextlib.py,sha256=lpaLJBy3X0UGLWjM98jkQZZq8so4fRmoK-Bheq0uOW4,1027
10
10
  utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
@@ -23,7 +23,7 @@ utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
23
23
  utilities/git.py,sha256=wpt5dZ5Oi5931pN24_VLZYaQOvmR0OcQuVtgHzFUN1k,2359
24
24
  utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
25
25
  utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
26
- utilities/hypothesis.py,sha256=DwhPpnz9w5H9YxLpoXTRjhTpadDjrX5-yUVT-Kq6Tgc,45216
26
+ utilities/hypothesis.py,sha256=tgUQ3egBETjo_A8xzEmfuYMGYf8o30PtZD8FqBfwsPM,45267
27
27
  utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
28
28
  utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
29
29
  utilities/iterables.py,sha256=mDqw2_0MUVp-P8FklgcaVTi2TXduH0MxbhTDzzhSBho,44915
@@ -61,7 +61,7 @@ utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq
61
61
  utilities/python_dotenv.py,sha256=iWcnpXbH7S6RoXHiLlGgyuH6udCupAcPd_gQ0eAenQ0,3190
62
62
  utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
63
63
  utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
64
- utilities/redis.py,sha256=U-LN6Li3vPTQ2sGGh85_1eD53payujg4qYX0TfmbLSs,32205
64
+ utilities/redis.py,sha256=XywncvQak9AYgdeN2M3vDJM9MEJsIFSfOiWVCKZVGjY,32697
65
65
  utilities/reprlib.py,sha256=Re9bk3n-kC__9DxQmRlevqFA86pE6TtVfWjUgpbVOv0,1849
66
66
  utilities/rich.py,sha256=t50MwwVBsoOLxzmeVFSVpjno4OW6Ufum32skXbV8-Bs,1911
67
67
  utilities/scipy.py,sha256=X6ROnHwiUhAmPhM0jkfEh0-Fd9iRvwiqtCQMOLmOQF8,945
@@ -91,7 +91,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
91
91
  utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
92
92
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
93
93
  utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
94
- dycw_utilities-0.126.5.dist-info/METADATA,sha256=pPt4HJkJRAyBAY-fWmeS5sHMh0ulzqrabQQfnFkOupI,12849
95
- dycw_utilities-0.126.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
- dycw_utilities-0.126.5.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
97
- dycw_utilities-0.126.5.dist-info/RECORD,,
94
+ dycw_utilities-0.126.7.dist-info/METADATA,sha256=YC7obEXSZCW1WxlqW0Ljgu2ZI8uM1kUwPPrrODeVHvk,12849
95
+ dycw_utilities-0.126.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
+ dycw_utilities-0.126.7.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
97
+ dycw_utilities-0.126.7.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.126.5"
3
+ __version__ = "0.126.7"
utilities/click.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
4
  import pathlib
5
- from typing import TYPE_CHECKING, Generic, TypeVar, override
5
+ from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar, override
6
6
  from uuid import UUID
7
7
 
8
8
  import click
@@ -49,6 +49,19 @@ ExistingDirPath = click.Path(
49
49
  )
50
50
 
51
51
 
52
+ class _HelpOptionNames(TypedDict):
53
+ help_option_names: Sequence[str]
54
+
55
+
56
+ class _ContextSettings(TypedDict):
57
+ context_settings: _HelpOptionNames
58
+
59
+
60
+ CONTEXT_SETTINGS_HELP_OPTION_NAMES = _ContextSettings(
61
+ context_settings=_HelpOptionNames(help_option_names=["-h", "--help"])
62
+ )
63
+
64
+
52
65
  # parameters
53
66
 
54
67
 
@@ -480,6 +493,7 @@ def _make_metavar(param: Parameter, desc: str, /) -> str:
480
493
 
481
494
 
482
495
  __all__ = [
496
+ "CONTEXT_SETTINGS_HELP_OPTION_NAMES",
483
497
  "Date",
484
498
  "DirPath",
485
499
  "Duration",
utilities/hypothesis.py CHANGED
@@ -24,6 +24,7 @@ from typing import (
24
24
  overload,
25
25
  override,
26
26
  )
27
+ from uuid import uuid4
27
28
 
28
29
  from hypothesis import HealthCheck, Phase, Verbosity, assume, settings
29
30
  from hypothesis.errors import InvalidArgument
@@ -1415,9 +1416,10 @@ def unique_strs(draw: DrawFn, /) -> str:
1415
1416
  """Strategy for generating unique strings."""
1416
1417
  now = get_now_local()
1417
1418
  pid = getpid()
1418
- ident = get_ident()
1419
- key = str(draw(uuids())).replace("-", "")
1420
- return f"{now:%Y%m%d%H%M%S%f}_{pid}_{ident}_{key}"
1419
+ ident = just(get_ident())
1420
+ key = str(uuid4()).replace("-", "")
1421
+ full = f"{now:%Y%m%d%H%M%S%f}_{pid}_{ident}_{key}"
1422
+ return draw(just(full))
1421
1423
 
1422
1424
 
1423
1425
  ##
utilities/redis.py CHANGED
@@ -35,6 +35,7 @@ from utilities.datetime import (
35
35
  from utilities.errors import ImpossibleCaseError
36
36
  from utilities.functions import ensure_int, get_class_name, identity
37
37
  from utilities.iterables import always_iterable, one
38
+ from utilities.orjson import deserialize, serialize
38
39
 
39
40
  if TYPE_CHECKING:
40
41
  from collections.abc import (
@@ -557,7 +558,7 @@ async def publish(
557
558
  data: _T,
558
559
  /,
559
560
  *,
560
- serializer: Callable[[bytes | str | _T], EncodableT],
561
+ serializer: Callable[[_T], EncodableT],
561
562
  timeout: Duration = _PUBLISH_TIMEOUT,
562
563
  ) -> ResponseT: ...
563
564
  @overload
@@ -577,7 +578,7 @@ async def publish(
577
578
  data: bytes | str | _T,
578
579
  /,
579
580
  *,
580
- serializer: Callable[[bytes | str | _T], EncodableT] | None = None,
581
+ serializer: Callable[[_T], EncodableT] | None = None,
581
582
  timeout: Duration = _PUBLISH_TIMEOUT,
582
583
  ) -> ResponseT: ...
583
584
  async def publish(
@@ -586,18 +587,19 @@ async def publish(
586
587
  data: bytes | str | _T,
587
588
  /,
588
589
  *,
589
- serializer: Callable[[bytes | str | _T], EncodableT] | None = None,
590
+ serializer: Callable[[_T], EncodableT] | None = None,
590
591
  timeout: Duration = _PUBLISH_TIMEOUT,
591
592
  ) -> ResponseT:
592
593
  """Publish an object to a channel."""
593
- if isinstance(data, bytes | str) and ( # skipif-ci-and-not-linux
594
- serializer is None
595
- ):
596
- data_use = data
597
- elif serializer is not None: # skipif-ci-and-not-linux
598
- data_use = serializer(data)
599
- else: # skipif-ci-and-not-linux
600
- raise PublishError(data=data, serializer=serializer)
594
+ match data, serializer: # skipif-ci-and-not-linux
595
+ case bytes() | str() as data_use, _:
596
+ ...
597
+ case _, None:
598
+ raise PublishError(data=data, serializer=serializer)
599
+ case _, Callable():
600
+ data_use = serializer(data)
601
+ case _ as never:
602
+ assert_never(never)
601
603
  async with timeout_dur(duration=timeout): # skipif-ci-and-not-linux
602
604
  return await redis.publish(channel, data_use) # skipif-ci-and-not-linux
603
605
 
@@ -654,7 +656,7 @@ class PublisherError(Exception):
654
656
 
655
657
 
656
658
  @dataclass(kw_only=True)
657
- class PublishService(Looper[tuple[str, bytes | str | _T]]):
659
+ class PublishService(Looper[tuple[str, _T]]):
658
660
  """Service to publish items to Redis."""
659
661
 
660
662
  # base
@@ -663,7 +665,7 @@ class PublishService(Looper[tuple[str, bytes | str | _T]]):
663
665
  empty_upon_exit: bool = field(default=True, repr=False)
664
666
  # self
665
667
  redis: Redis
666
- serializer: Callable[[Any], EncodableT] | None = None
668
+ serializer: Callable[[_T], EncodableT] = serialize
667
669
  publish_timeout: Duration = SECOND
668
670
 
669
671
  @override
@@ -698,6 +700,7 @@ def subscribe(
698
700
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
699
701
  sleep: Duration = _SUBSCRIBE_SLEEP,
700
702
  output: Literal["raw"],
703
+ filter_: Callable[[_RedisMessage], bool] | None = None,
701
704
  ) -> AsyncIterator[Task[None]]: ...
702
705
  @overload
703
706
  @asynccontextmanager
@@ -710,6 +713,7 @@ def subscribe(
710
713
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
711
714
  sleep: Duration = _SUBSCRIBE_SLEEP,
712
715
  output: Literal["bytes"],
716
+ filter_: Callable[[bytes], bool] | None = None,
713
717
  ) -> AsyncIterator[Task[None]]: ...
714
718
  @overload
715
719
  @asynccontextmanager
@@ -722,6 +726,7 @@ def subscribe(
722
726
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
723
727
  sleep: Duration = _SUBSCRIBE_SLEEP,
724
728
  output: Literal["text"] = "text",
729
+ filter_: Callable[[str], bool] | None = None,
725
730
  ) -> AsyncIterator[Task[None]]: ...
726
731
  @overload
727
732
  @asynccontextmanager
@@ -734,6 +739,7 @@ def subscribe(
734
739
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
735
740
  sleep: Duration = _SUBSCRIBE_SLEEP,
736
741
  output: Callable[[bytes], _T],
742
+ filter_: Callable[[_T], bool] | None = None,
737
743
  ) -> AsyncIterator[Task[None]]: ...
738
744
  @asynccontextmanager
739
745
  async def subscribe(
@@ -745,6 +751,7 @@ async def subscribe(
745
751
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
746
752
  sleep: Duration = _SUBSCRIBE_SLEEP,
747
753
  output: Literal["raw", "bytes", "text"] | Callable[[bytes], _T] = "text",
754
+ filter_: Callable[[Any], bool] | None = None,
748
755
  ) -> AsyncIterator[Task[None]]:
749
756
  """Subscribe to the data of a given channel(s)."""
750
757
  channels = list(always_iterable(channels)) # skipif-ci-and-not-linux
@@ -767,7 +774,15 @@ async def subscribe(
767
774
  assert_never(never)
768
775
 
769
776
  task = create_task( # skipif-ci-and-not-linux
770
- _subscribe_core(redis, channels, transform, queue, timeout=timeout, sleep=sleep)
777
+ _subscribe_core(
778
+ redis,
779
+ channels,
780
+ transform,
781
+ queue,
782
+ timeout=timeout,
783
+ sleep=sleep,
784
+ filter_=filter_,
785
+ )
771
786
  )
772
787
  try: # skipif-ci-and-not-linux
773
788
  yield task
@@ -786,6 +801,7 @@ async def _subscribe_core(
786
801
  *,
787
802
  timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
788
803
  sleep: Duration = _SUBSCRIBE_SLEEP,
804
+ filter_: Callable[[Any], bool] | None = None,
789
805
  ) -> None:
790
806
  timeout_use = ( # skipif-ci-and-not-linux
791
807
  None if timeout is None else datetime_duration_to_float(timeout)
@@ -798,10 +814,12 @@ async def _subscribe_core(
798
814
  while True:
799
815
  message = await pubsub.get_message(timeout=timeout_use)
800
816
  if is_subscribe_message(message):
801
- if isinstance(queue, EnhancedQueue):
802
- queue.put_right_nowait(transform(message))
803
- else:
804
- queue.put_nowait(transform(message))
817
+ transformed = transform(message)
818
+ if (filter_ is None) or filter_(transformed):
819
+ if isinstance(queue, EnhancedQueue):
820
+ queue.put_right_nowait(transformed)
821
+ else:
822
+ queue.put_nowait(transformed)
805
823
  else:
806
824
  await asyncio.sleep(sleep_use)
807
825
 
@@ -843,9 +861,10 @@ class SubscribeService(Looper[_T]):
843
861
  # self
844
862
  redis: Redis
845
863
  channel: str
846
- deserializer: Callable[[bytes], _T] | None = None
847
- subscribe_sleep: Duration = _SUBSCRIBE_SLEEP
864
+ deserializer: Callable[[bytes], _T] = deserialize
848
865
  subscribe_timeout: Duration | None = _SUBSCRIBE_TIMEOUT
866
+ subscribe_sleep: Duration = _SUBSCRIBE_SLEEP
867
+ filter_: Callable[[_T], bool] | None = None
849
868
  _is_subscribed: Event = field(default_factory=Event, init=False, repr=False)
850
869
 
851
870
  @override
@@ -864,12 +883,10 @@ class SubscribeService(Looper[_T]):
864
883
  self.redis,
865
884
  self.channel,
866
885
  self._queue,
867
- sleep=self.subscribe_sleep,
868
886
  timeout=self.subscribe_timeout,
869
- output=cast(
870
- "Any",
871
- "text" if self.deserializer is None else self.deserializer,
872
- ),
887
+ sleep=self.subscribe_sleep,
888
+ output=self.deserializer,
889
+ filter_=self.filter_,
873
890
  )
874
891
  )
875
892
  case _ as never: