dycw-utilities 0.125.1__py3-none-any.whl → 0.125.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.125.1
3
+ Version: 0.125.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=J1Gf3FyEmeotls_D_lGJIjDWsDDZcqnYOQZ1egNnqYs,60
1
+ utilities/__init__.py,sha256=S9hi5nUNGrmUoeHSNNw9CdOdoY68OUGHaOm0V7chzZo,60
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/asyncio.py,sha256=gr2eUx0E6LiCup6VKgUGwh8lAUriGdX2TlK-PZdlvfo,28284
4
4
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
@@ -24,7 +24,7 @@ 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
26
  utilities/hypothesis.py,sha256=a75izXg9aCBhhDkj_ZgK3TDzlzk38evP8TO7JbYYQvg,46264
27
- utilities/importlib.py,sha256=ueY3R39hWrUtrVXs39utM2xDig-eyJfYn1FBVxWb3_w,368
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=prKXBdF5QfLTGC-q4567DwO8xzUng_Z-2a4wBkMqyDo,45360
30
30
  utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
@@ -35,7 +35,7 @@ utilities/luigi.py,sha256=fpH9MbxJDuo6-k9iCXRayFRtiVbUtibCJKugf7ygpv0,5988
35
35
  utilities/math.py,sha256=-mQgbah-dPJwOEWf3SonrFoVZ2AVxMgpeQ3dfVa-oJA,26764
36
36
  utilities/memory_profiler.py,sha256=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
37
37
  utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
38
- utilities/more_itertools.py,sha256=fYsGyIPB2s1KRWoH3AkV3CxJxnMLvmidqBf8l1VQnyE,7193
38
+ utilities/more_itertools.py,sha256=tBbjjKx8_Uv_TCjxhPwrGfAx_jRHtvLIZqXVWAsjzqA,8842
39
39
  utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
40
40
  utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
41
41
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
87
87
  utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
89
  utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
90
- dycw_utilities-0.125.1.dist-info/METADATA,sha256=kZ35w8nseblmp9RraacEJcl9ZAIQls6J8FPSiaX3eT0,12852
91
- dycw_utilities-0.125.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.125.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.125.1.dist-info/RECORD,,
90
+ dycw_utilities-0.125.3.dist-info/METADATA,sha256=vMdoTZVKyi-BNSOGgeJYjY6uY-icfbVmvUlAo_SwJmM,12852
91
+ dycw_utilities-0.125.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.125.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.125.3.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.125.1"
3
+ __version__ = "0.125.3"
utilities/importlib.py CHANGED
@@ -4,11 +4,13 @@ from importlib import import_module
4
4
  from importlib.util import find_spec
5
5
 
6
6
 
7
- def is_valid_import(module: str, name: str) -> bool:
7
+ def is_valid_import(module: str, /, *, name: str | None = None) -> bool:
8
8
  """Check if an import is valid."""
9
9
  spec = find_spec(module)
10
10
  if spec is None:
11
11
  return False
12
+ if name is None:
13
+ return True
12
14
  mod = import_module(module)
13
15
  return hasattr(mod, name)
14
16
 
@@ -21,12 +21,14 @@ from more_itertools import bucket, partition, split_into
21
21
  from more_itertools import peekable as _peekable
22
22
 
23
23
  from utilities.functions import get_class_name
24
+ from utilities.iterables import OneNonUniqueError, one
25
+ from utilities.reprlib import get_repr
24
26
  from utilities.sentinel import Sentinel, sentinel
27
+ from utilities.types import THashable
25
28
 
26
29
  if TYPE_CHECKING:
27
30
  from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
28
31
 
29
- from utilities.types import THashable
30
32
 
31
33
  _T = TypeVar("_T")
32
34
  _U = TypeVar("_U")
@@ -35,6 +37,26 @@ _U = TypeVar("_U")
35
37
  ##
36
38
 
37
39
 
40
+ @overload
41
+ def bucket_mapping(
42
+ iterable: Iterable[_T],
43
+ func: Callable[[_T], THashable],
44
+ /,
45
+ *,
46
+ transform: Callable[[_T], _U],
47
+ list: bool = False,
48
+ unique: Literal[True],
49
+ ) -> Mapping[THashable, _U]: ...
50
+ @overload
51
+ def bucket_mapping(
52
+ iterable: Iterable[_T],
53
+ func: Callable[[_T], THashable],
54
+ /,
55
+ *,
56
+ transform: Callable[[_T], _U] | None = None,
57
+ list: bool = False,
58
+ unique: Literal[True],
59
+ ) -> Mapping[THashable, _T]: ...
38
60
  @overload
39
61
  def bucket_mapping(
40
62
  iterable: Iterable[_T],
@@ -79,11 +101,14 @@ def bucket_mapping(
79
101
  *,
80
102
  transform: Callable[[_T], _U] | None = None,
81
103
  list: bool = False,
104
+ unique: bool = False,
82
105
  ) -> (
83
106
  Mapping[THashable, Iterator[_T]]
84
107
  | Mapping[THashable, Iterator[_U]]
85
108
  | Mapping[THashable, Sequence[_T]]
86
109
  | Mapping[THashable, Sequence[_U]]
110
+ | Mapping[THashable, _T]
111
+ | Mapping[THashable, _U]
87
112
  ): ...
88
113
  def bucket_mapping(
89
114
  iterable: Iterable[_T],
@@ -92,26 +117,55 @@ def bucket_mapping(
92
117
  *,
93
118
  transform: Callable[[_T], _U] | None = None,
94
119
  list: bool = False, # noqa: A002
120
+ unique: bool = False,
95
121
  ) -> (
96
122
  Mapping[THashable, Iterator[_T]]
97
123
  | Mapping[THashable, Iterator[_U]]
98
124
  | Mapping[THashable, Sequence[_T]]
99
125
  | Mapping[THashable, Sequence[_U]]
126
+ | Mapping[THashable, _T]
127
+ | Mapping[THashable, _U]
100
128
  ):
101
129
  """Bucket the values of iterable into a mapping."""
102
130
  b = bucket(iterable, func)
103
131
  mapping = {key: b[key] for key in b}
104
132
  match transform, list:
105
133
  case None, False:
106
- return mapping
134
+ ...
107
135
  case None, True:
108
- return {k: builtins.list(v) for k, v in mapping.items()}
136
+ mapping = {k: builtins.list(v) for k, v in mapping.items()}
109
137
  case _, False:
110
- return {k: map(transform, v) for k, v in mapping.items()}
138
+ mapping = {k: map(transform, v) for k, v in mapping.items()}
111
139
  case _, True:
112
- return {k: builtins.list(map(transform, v)) for k, v in mapping.items()}
140
+ mapping = {k: builtins.list(map(transform, v)) for k, v in mapping.items()}
113
141
  case _ as never:
114
142
  assert_never(never)
143
+ if not unique:
144
+ return mapping
145
+ results = {}
146
+ error_no_transform: dict[THashable, tuple[_T, _T]] = {}
147
+ for key, value in mapping.items():
148
+ try:
149
+ results[key] = one(value)
150
+ except OneNonUniqueError as error:
151
+ error_no_transform[key] = (error.first, error.second)
152
+ if len(error_no_transform) >= 1:
153
+ raise BucketMappingError(errors=error_no_transform)
154
+ return results
155
+
156
+
157
+ @dataclass(kw_only=True, slots=True)
158
+ class BucketMappingError(Exception, Generic[THashable, _U]):
159
+ errors: Mapping[THashable, tuple[_U, _U]]
160
+
161
+ @override
162
+ def __str__(self) -> str:
163
+ parts = [
164
+ f"{get_repr(key)} (#1: {get_repr(first)}, #2: {get_repr(second)})"
165
+ for key, (first, second) in self.errors.items()
166
+ ]
167
+ desc = ", ".join(parts)
168
+ return f"Buckets must contain exactly one item each; got {desc}"
115
169
 
116
170
 
117
171
  ##
@@ -264,6 +318,7 @@ def _yield_splits3(
264
318
 
265
319
 
266
320
  __all__ = [
321
+ "BucketMappingError",
267
322
  "Split",
268
323
  "bucket_mapping",
269
324
  "partition_list",