dycw-utilities 0.125.8__py3-none-any.whl → 0.125.10__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.8
3
+ Version: 0.125.10
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -174,7 +174,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
174
174
  Provides-Extra: zzz-test-sentinel
175
175
  Provides-Extra: zzz-test-shelve
176
176
  Provides-Extra: zzz-test-slack-sdk
177
- Requires-Dist: aiohttp<3.12.1,>=3.12.0; extra == 'zzz-test-slack-sdk'
177
+ Requires-Dist: aiohttp<3.12.3,>=3.12.2; extra == 'zzz-test-slack-sdk'
178
178
  Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
179
179
  Provides-Extra: zzz-test-socket
180
180
  Provides-Extra: zzz-test-sqlalchemy
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=NMu0lmH61a5876HTROVqniekCmmFD7fjHMGBEq4s2LA,60
1
+ utilities/__init__.py,sha256=hCotYGEOz7s5mhsPf8wWq526AJNjwEkUazmqfzo-lkU,61
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/asyncio.py,sha256=3OVbJKTYCucrIkM7WSMMkQA9jYJFCQMnGNM4H5rBNgA,29154
4
4
  utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
@@ -26,7 +26,7 @@ utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
26
26
  utilities/hypothesis.py,sha256=a75izXg9aCBhhDkj_ZgK3TDzlzk38evP8TO7JbYYQvg,46264
27
27
  utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
28
28
  utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
29
- utilities/iterables.py,sha256=wqq2lKfEAq1qncE-lR848DHX627aerFaq4BXmLen-NE,43589
29
+ utilities/iterables.py,sha256=mDqw2_0MUVp-P8FklgcaVTi2TXduH0MxbhTDzzhSBho,44915
30
30
  utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
31
31
  utilities/libcst.py,sha256=L0IkwAWXPuNpYyceeJGOC8CAwHdVPRpbXC8MQphCXh4,4954
32
32
  utilities/lightweight_charts.py,sha256=0xNfcsrgFI0R9xL25LtSm-W5yhfBI93qQNT6HyaXAhg,2769
@@ -46,7 +46,7 @@ utilities/parse.py,sha256=vsZ2jf_ceSI_Kta9titixufysJaVXh0Whjz1T4awJZw,18938
46
46
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
47
47
  utilities/period.py,sha256=o4wXYEXVlFomop4-Ra4L0yRP4i99NZFjIe_fa7NdZck,11024
48
48
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
49
- utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
49
+ utilities/platform.py,sha256=48IOKx1IC6ZJXWG-b56ZQptITcNFhWRjELW72o2dGTA,2398
50
50
  utilities/polars.py,sha256=QlmUpYTqHNkcLnWOQh1TW22W2QyLzvifCvBcbsqhpdE,63272
51
51
  utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
52
52
  utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
@@ -88,7 +88,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
88
88
  utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
89
89
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
90
90
  utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
91
- dycw_utilities-0.125.8.dist-info/METADATA,sha256=TWxcCoaCSvt2oxBQq7vNPceZ3qJb1ba9ODTLakKrhxc,12851
92
- dycw_utilities-0.125.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
- dycw_utilities-0.125.8.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.125.8.dist-info/RECORD,,
91
+ dycw_utilities-0.125.10.dist-info/METADATA,sha256=TOOerRTXo6msfA1IIjYMyh-7-i0CR54HRLZc1GTpZdE,12852
92
+ dycw_utilities-0.125.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
93
+ dycw_utilities-0.125.10.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
+ dycw_utilities-0.125.10.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.125.8"
3
+ __version__ = "0.125.10"
utilities/iterables.py CHANGED
@@ -1190,6 +1190,56 @@ def product_dicts(mapping: Mapping[_K, Iterable[_V]], /) -> Iterator[Mapping[_K,
1190
1190
  ##
1191
1191
 
1192
1192
 
1193
+ def range_partitions(stop: int, num: int, total: int, /) -> range:
1194
+ """Partition a range."""
1195
+ if stop <= 0:
1196
+ raise _RangePartitionsStopError(stop=stop)
1197
+ if not (1 <= total <= stop):
1198
+ raise _RangePartitionsTotalError(stop=stop, total=total)
1199
+ if not (0 <= num < total):
1200
+ raise _RangePartitionsNumError(num=num, total=total)
1201
+ q, r = divmod(stop, total)
1202
+ start = num * q + min(num, r)
1203
+ end = start + q + (1 if num < r else 0)
1204
+ return range(start, end)
1205
+
1206
+
1207
+ @dataclass(kw_only=True, slots=True)
1208
+ class RangePartitionsError(Exception): ...
1209
+
1210
+
1211
+ @dataclass(kw_only=True, slots=True)
1212
+ class _RangePartitionsStopError(RangePartitionsError):
1213
+ stop: int
1214
+
1215
+ @override
1216
+ def __str__(self) -> str:
1217
+ return f"'stop' must be positive; got {self.stop}"
1218
+
1219
+
1220
+ @dataclass(kw_only=True, slots=True)
1221
+ class _RangePartitionsTotalError(RangePartitionsError):
1222
+ stop: int
1223
+ total: int
1224
+
1225
+ @override
1226
+ def __str__(self) -> str:
1227
+ return f"'total' must be in [1, {self.stop}]; got {self.total}"
1228
+
1229
+
1230
+ @dataclass(kw_only=True, slots=True)
1231
+ class _RangePartitionsNumError(RangePartitionsError):
1232
+ num: int
1233
+ total: int
1234
+
1235
+ @override
1236
+ def __str__(self) -> str:
1237
+ return f"'num' must be in [0, {self.total - 1}]; got {self.num}"
1238
+
1239
+
1240
+ ##
1241
+
1242
+
1193
1243
  @overload
1194
1244
  def reduce_mappings(
1195
1245
  func: Callable[[_V, _V], _V], sequence: Iterable[Mapping[_K, _V]], /
@@ -1458,6 +1508,7 @@ __all__ = [
1458
1508
  "OneUniqueEmptyError",
1459
1509
  "OneUniqueError",
1460
1510
  "OneUniqueNonUniqueError",
1511
+ "RangePartitionsError",
1461
1512
  "ResolveIncludeAndExcludeError",
1462
1513
  "SortIterableError",
1463
1514
  "always_iterable",
@@ -1502,6 +1553,7 @@ __all__ = [
1502
1553
  "one_unique",
1503
1554
  "pairwise_tail",
1504
1555
  "product_dicts",
1556
+ "range_partitions",
1505
1557
  "reduce_mappings",
1506
1558
  "resolve_include_and_exclude",
1507
1559
  "sort_iterable",
utilities/platform.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
+ from pathlib import Path
4
5
  from platform import system
5
6
  from typing import TYPE_CHECKING, Literal, assert_never, override
6
7
 
@@ -42,6 +43,32 @@ IS_NOT_MAC = not IS_MAC
42
43
  IS_NOT_LINUX = not IS_LINUX
43
44
 
44
45
 
46
+ ##
47
+
48
+
49
+ def get_max_pid() -> int | None:
50
+ """Get the maximum process ID."""
51
+ match SYSTEM:
52
+ case "windows": # pragma: no cover
53
+ return None
54
+ case "mac": # skipif-not-macos
55
+ return 99999
56
+ case "linux": # skipif-not-linux
57
+ try:
58
+ with Path("/proc/sys/kernel/pid_max").open() as fh:
59
+ return int(fh.read())
60
+ except FileNotFoundError: # pragma: no cover
61
+ return None
62
+ case _ as never:
63
+ assert_never(never)
64
+
65
+
66
+ MAX_PID = get_max_pid()
67
+
68
+
69
+ ##
70
+
71
+
45
72
  def maybe_yield_lower_case(text: Iterable[str], /) -> Iterator[str]:
46
73
  """Yield lower-cased text if the platform is case-insentive."""
47
74
  match SYSTEM:
@@ -62,9 +89,11 @@ __all__ = [
62
89
  "IS_NOT_MAC",
63
90
  "IS_NOT_WINDOWS",
64
91
  "IS_WINDOWS",
92
+ "MAX_PID",
65
93
  "SYSTEM",
66
94
  "GetSystemError",
67
95
  "System",
96
+ "get_max_pid",
68
97
  "get_system",
69
98
  "maybe_yield_lower_case",
70
99
  ]