dycw-utilities 0.166.26__py3-none-any.whl → 0.166.27__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.

Potentially problematic release.


This version of dycw-utilities might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.166.26
3
+ Version: 0.166.27
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,7 +1,7 @@
1
- utilities/__init__.py,sha256=ypZlN_qCHWy6_qsW0wrMXJ1XcN87MLMxxm-3gFVy0PI,61
1
+ utilities/__init__.py,sha256=HjzeHsUXRBIhhvlhaEdygkT-X4czww6QieOICBEASeM,61
2
2
  utilities/aeventkit.py,sha256=ddoleSwW9zdc2tjX5Ge0pMKtYwV_JMxhHYOxnWX2AGM,12609
3
3
  utilities/altair.py,sha256=nHdpWt8ZwdUwRQN970MvHd5bRWokNqzHcZQEdSHKRuE,9033
4
- utilities/asyncio.py,sha256=PUedzQ5deqlSECQ33sam9cRzI9TnygHz3FdOqWJWPTM,15288
4
+ utilities/asyncio.py,sha256=60l1IwjnRGeaVphAFiwDIHyfKoZYKY-XGpptUxGiU-M,17034
5
5
  utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
6
6
  utilities/atools.py,sha256=6neeCcgXxK2dlsc0xp15Za7nSucbCgFtAJepGI_-WXU,2549
7
7
  utilities/cachetools.py,sha256=v1-9sXHLdOLiwmkq6NB0OUbxeKBuVVN6wmAWefWoaHI,2744
@@ -27,7 +27,7 @@ utilities/hypothesis.py,sha256=CSCJFek07g71iSND7PCty7gyaOT_49QSbnTHn1wWidQ,45535
27
27
  utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
28
28
  utilities/inflect.py,sha256=v7YkOWSu8NAmVghPcf4F3YBZQoJCS47_DLf9jbfWIs0,581
29
29
  utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
30
- utilities/iterables.py,sha256=8_dljtugLjxL8GCO_udMoH5uldvxdAmsN6aZ3EdnAiA,42564
30
+ utilities/iterables.py,sha256=t2TsW-K3rVlS6y4_tqcc1fk9RwJV-bi7G_VwduMABK0,42558
31
31
  utilities/json.py,sha256=-WcGtSsCr9Y42wHZzAMnfvU6ihAfVftylFfRUORaDFo,2102
32
32
  utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
33
33
  utilities/libcst.py,sha256=ngD4wxnR3Kh-RBVmU5l5ST7cuZLhMZwyMDjHZe5mhTs,5581
@@ -91,8 +91,8 @@ utilities/zoneinfo.py,sha256=tdIScrTB2-B-LH0ukb1HUXKooLknOfJNwHk10MuMYvA,3619
91
91
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
92
92
  utilities/pytest_plugins/pytest_randomly.py,sha256=B1qYVlExGOxTywq2r1SMi5o7btHLk2PNdY_b1p98dkE,409
93
93
  utilities/pytest_plugins/pytest_regressions.py,sha256=mnHYBfdprz50UGVkVzV1bZERZN5CFfoF8YbokGxdFwU,1639
94
- dycw_utilities-0.166.26.dist-info/METADATA,sha256=JHYsRNyHleOHEHpUg4jB_MfIxTOCchthwx_YDzuUaWE,1700
95
- dycw_utilities-0.166.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
- dycw_utilities-0.166.26.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
97
- dycw_utilities-0.166.26.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
98
- dycw_utilities-0.166.26.dist-info/RECORD,,
94
+ dycw_utilities-0.166.27.dist-info/METADATA,sha256=zD73RDL_kiYeNBGjNY5R9krPiTk165csMHzG7DzD35I,1700
95
+ dycw_utilities-0.166.27.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
96
+ dycw_utilities-0.166.27.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
97
+ dycw_utilities-0.166.27.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
98
+ dycw_utilities-0.166.27.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.166.26"
3
+ __version__ = "0.166.27"
utilities/asyncio.py CHANGED
@@ -31,6 +31,7 @@ from typing import (
31
31
  Self,
32
32
  TextIO,
33
33
  assert_never,
34
+ cast,
34
35
  overload,
35
36
  override,
36
37
  )
@@ -38,6 +39,7 @@ from typing import (
38
39
  from utilities.functions import ensure_int, ensure_not_none
39
40
  from utilities.os import is_pytest
40
41
  from utilities.random import SYSTEM_RANDOM
42
+ from utilities.reprlib import get_repr
41
43
  from utilities.sentinel import Sentinel, sentinel
42
44
  from utilities.shelve import yield_shelf
43
45
  from utilities.text import to_bool
@@ -48,6 +50,7 @@ if TYPE_CHECKING:
48
50
  from asyncio import _CoroutineLike
49
51
  from asyncio.subprocess import Process
50
52
  from collections.abc import (
53
+ AsyncIterable,
51
54
  AsyncIterator,
52
55
  Callable,
53
56
  ItemsView,
@@ -346,6 +349,24 @@ class EnhancedTaskGroup(TaskGroup):
346
349
  ##
347
350
 
348
351
 
352
+ def chain_async[T](*iterables: Iterable[T] | AsyncIterable[T]) -> AsyncIterator[T]:
353
+ """Asynchronous version of `chain`."""
354
+
355
+ async def iterator() -> AsyncIterator[T]:
356
+ for it in iterables:
357
+ try:
358
+ async for item in cast("AsyncIterable[T]", it):
359
+ yield item
360
+ except TypeError:
361
+ for item in cast("Iterable[T]", it):
362
+ yield item
363
+
364
+ return iterator()
365
+
366
+
367
+ ##
368
+
369
+
349
370
  def get_coroutine_name(func: Callable[[], Coro[Any]], /) -> str:
350
371
  """Get the name of a coroutine, and then dispose of it gracefully."""
351
372
  coro = func()
@@ -394,6 +415,43 @@ def get_items_nowait[T](queue: Queue[T], /, *, max_size: int | None = None) -> l
394
415
  ##
395
416
 
396
417
 
418
+ async def one_async[T](*iterables: Iterable[T] | AsyncIterable[T]) -> T:
419
+ """Asynchronous version of `one`."""
420
+ result: T | Sentinel = sentinel
421
+ async for item in chain_async(*iterables):
422
+ if not isinstance(result, Sentinel):
423
+ raise OneAsyncNonUniqueError(iterables=iterables, first=result, second=item)
424
+ result = item
425
+ if isinstance(result, Sentinel):
426
+ raise OneAsyncEmptyError(iterables=iterables)
427
+ return result
428
+
429
+
430
+ @dataclass(kw_only=True, slots=True)
431
+ class OneAsyncError[T](Exception):
432
+ iterables: tuple[Iterable[T] | AsyncIterable[T], ...]
433
+
434
+
435
+ @dataclass(kw_only=True, slots=True)
436
+ class OneAsyncEmptyError[T](OneAsyncError[T]):
437
+ @override
438
+ def __str__(self) -> str:
439
+ return f"Iterable(s) {get_repr(self.iterables)} must not be empty"
440
+
441
+
442
+ @dataclass(kw_only=True, slots=True)
443
+ class OneAsyncNonUniqueError[T](OneAsyncError):
444
+ first: T
445
+ second: T
446
+
447
+ @override
448
+ def __str__(self) -> str:
449
+ return f"Iterable(s) {get_repr(self.iterables)} must contain exactly one item; got {self.first}, {self.second} and perhaps more"
450
+
451
+
452
+ ##
453
+
454
+
397
455
  async def put_items[T](items: Iterable[T], queue: Queue[T], /) -> None:
398
456
  """Put items into a queue; if full then wait."""
399
457
  for item in items:
@@ -542,10 +600,15 @@ async def yield_locked_shelf(
542
600
  __all__ = [
543
601
  "AsyncDict",
544
602
  "EnhancedTaskGroup",
603
+ "OneAsyncEmptyError",
604
+ "OneAsyncError",
605
+ "OneAsyncNonUniqueError",
545
606
  "StreamCommandOutput",
607
+ "chain_async",
546
608
  "get_coroutine_name",
547
609
  "get_items",
548
610
  "get_items_nowait",
611
+ "one_async",
549
612
  "put_items",
550
613
  "put_items_nowait",
551
614
  "sleep_max",
utilities/iterables.py CHANGED
@@ -933,7 +933,7 @@ class MergeStrMappingsError(Exception):
933
933
 
934
934
  def one[T](*iterables: Iterable[T]) -> T:
935
935
  """Return the unique value in a set of iterables."""
936
- it = iter(chain(*iterables))
936
+ it = chain(*iterables)
937
937
  try:
938
938
  first = next(it)
939
939
  except StopIteration: