dycw-utilities 0.133.7__py3-none-any.whl → 0.134.1__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.
utilities/atools.py CHANGED
@@ -1,35 +1,32 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
- from typing import TYPE_CHECKING, ParamSpec, TypeVar
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from atools import memoize
7
7
 
8
- from utilities.types import Coroutine1
8
+ from utilities.types import Coro
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from whenever import TimeDelta
12
12
 
13
13
 
14
- _P = ParamSpec("_P")
15
- _R = TypeVar("_R")
16
- _AsyncFunc = Callable[_P, Coroutine1[_R]]
17
- type _Key = tuple[_AsyncFunc, TimeDelta]
18
- _MEMOIZED_FUNCS: dict[_Key, _AsyncFunc] = {}
14
+ type _Key[**P, T] = tuple[Callable[P, Coro[T]], TimeDelta]
15
+ _MEMOIZED_FUNCS: dict[_Key, Callable[..., Coro[Any]]] = {}
19
16
 
20
17
 
21
- async def call_memoized(
22
- func: _AsyncFunc[_P, _R],
18
+ async def call_memoized[**P, T](
19
+ func: Callable[P, Coro[T]],
23
20
  refresh: TimeDelta | None = None,
24
21
  /,
25
- *args: _P.args,
26
- **kwargs: _P.kwargs,
27
- ) -> _R:
22
+ *args: P.args,
23
+ **kwargs: P.kwargs,
24
+ ) -> T:
28
25
  """Call an asynchronous function, with possible memoization."""
29
26
  if refresh is None:
30
27
  return await func(*args, **kwargs)
31
28
  key: _Key = (func, refresh)
32
- memoized_func: _AsyncFunc[_P, _R]
29
+ memoized_func: Callable[P, Coro[T]]
33
30
  try:
34
31
  memoized_func = _MEMOIZED_FUNCS[key]
35
32
  except KeyError:
utilities/cachetools.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Callable, Iterable, Iterator, MutableSet
3
+ from collections.abc import Callable, Hashable, Iterable, Iterator, MutableSet
4
4
  from math import inf
5
5
  from time import monotonic
6
- from typing import TYPE_CHECKING, Any, TypeVar, override
6
+ from typing import TYPE_CHECKING, Any, override
7
7
 
8
8
  import cachetools
9
9
  from cachetools.func import ttl_cache
@@ -11,14 +11,8 @@ from cachetools.func import ttl_cache
11
11
  if TYPE_CHECKING:
12
12
  from whenever import TimeDelta
13
13
 
14
- from utilities.types import TCallable
15
14
 
16
- _K = TypeVar("_K")
17
- _T = TypeVar("_T")
18
- _V = TypeVar("_V")
19
-
20
-
21
- class TTLCache(cachetools.TTLCache[_K, _V]):
15
+ class TTLCache[K: Hashable, V](cachetools.TTLCache[K, V]):
22
16
  """A TTL-cache."""
23
17
 
24
18
  def __init__(
@@ -40,15 +34,15 @@ class TTLCache(cachetools.TTLCache[_K, _V]):
40
34
  ##
41
35
 
42
36
 
43
- class TTLSet(MutableSet[_T]):
37
+ class TTLSet[T: Hashable](MutableSet[T]):
44
38
  """A TTL-set."""
45
39
 
46
- _cache: TTLCache[_T, None]
40
+ _cache: TTLCache[T, None]
47
41
 
48
42
  @override
49
43
  def __init__(
50
44
  self,
51
- iterable: Iterable[_T] | None = None,
45
+ iterable: Iterable[T] | None = None,
52
46
  /,
53
47
  *,
54
48
  max_size: int | None = None,
@@ -71,7 +65,7 @@ class TTLSet(MutableSet[_T]):
71
65
  return self._cache.__contains__(x)
72
66
 
73
67
  @override
74
- def __iter__(self) -> Iterator[_T]:
68
+ def __iter__(self) -> Iterator[T]:
75
69
  return self._cache.__iter__()
76
70
 
77
71
  @override
@@ -87,24 +81,24 @@ class TTLSet(MutableSet[_T]):
87
81
  return set(self._cache).__str__()
88
82
 
89
83
  @override
90
- def add(self, value: _T) -> None:
84
+ def add(self, value: T) -> None:
91
85
  self._cache[value] = None
92
86
 
93
87
  @override
94
- def discard(self, value: _T) -> None:
88
+ def discard(self, value: T) -> None:
95
89
  del self._cache[value]
96
90
 
97
91
 
98
92
  ##
99
93
 
100
94
 
101
- def cache(
95
+ def cache[F: Callable](
102
96
  *,
103
97
  max_size: int | None = None,
104
98
  max_duration: TimeDelta | None = None,
105
99
  timer: Callable[[], float] = monotonic,
106
100
  typed_: bool = False,
107
- ) -> Callable[[TCallable], TCallable]:
101
+ ) -> Callable[[F], F]:
108
102
  """Decorate a function with `max_size` and/or `ttl` settings."""
109
103
  return ttl_cache(
110
104
  maxsize=inf if max_size is None else max_size,
utilities/click.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import enum
3
4
  import ipaddress
4
5
  import pathlib
5
- from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar, assert_never, override
6
+ from typing import TYPE_CHECKING, TypedDict, assert_never, override
6
7
 
7
8
  import click
8
9
  import whenever
@@ -15,32 +16,27 @@ from utilities.functions import EnsureStrError, ensure_str, get_class_name
15
16
  from utilities.iterables import is_iterable_not_str
16
17
  from utilities.parse import ParseObjectError, parse_object
17
18
  from utilities.text import split_str
18
- from utilities.types import (
19
- DateDeltaLike,
20
- DateLike,
21
- DateTimeDeltaLike,
22
- EnumLike,
23
- IPv4AddressLike,
24
- IPv6AddressLike,
25
- MaybeStr,
26
- PlainDateTimeLike,
27
- TEnum,
28
- TimeDeltaLike,
29
- TimeLike,
30
- ZonedDateTimeLike,
31
- )
32
19
  from utilities.whenever import FreqLike, _FreqParseError, _MonthParseCommonISOError
33
20
 
34
21
  if TYPE_CHECKING:
35
22
  from collections.abc import Iterable, Sequence
36
23
 
24
+ from utilities.types import (
25
+ DateDeltaLike,
26
+ DateLike,
27
+ DateTimeDeltaLike,
28
+ EnumLike,
29
+ IPv4AddressLike,
30
+ IPv6AddressLike,
31
+ MaybeStr,
32
+ PlainDateTimeLike,
33
+ TimeDeltaLike,
34
+ TimeLike,
35
+ ZonedDateTimeLike,
36
+ )
37
37
  from utilities.whenever import MonthLike
38
38
 
39
39
 
40
- _T = TypeVar("_T")
41
- _TParam = TypeVar("_TParam", bound=ParamType)
42
-
43
-
44
40
  FilePath = click.Path(file_okay=True, dir_okay=False, path_type=pathlib.Path)
45
41
  DirPath = click.Path(file_okay=False, dir_okay=True, path_type=pathlib.Path)
46
42
  ExistingFilePath = click.Path(
@@ -145,11 +141,11 @@ class DateTimeDelta(ParamType):
145
141
  assert_never(never)
146
142
 
147
143
 
148
- class Enum(ParamType, Generic[TEnum]):
144
+ class Enum[E: enum.Enum](ParamType):
149
145
  """An enum-valued parameter."""
150
146
 
151
147
  @override
152
- def __init__(self, enum: type[TEnum], /, *, case_sensitive: bool = False) -> None:
148
+ def __init__(self, enum: type[E], /, *, case_sensitive: bool = False) -> None:
153
149
  cls = get_class_name(enum)
154
150
  self.name = f"enum[{cls}]"
155
151
  self._enum = enum
@@ -163,8 +159,8 @@ class Enum(ParamType, Generic[TEnum]):
163
159
 
164
160
  @override
165
161
  def convert(
166
- self, value: EnumLike[TEnum], param: Parameter | None, ctx: Context | None
167
- ) -> TEnum:
162
+ self, value: EnumLike[E], param: Parameter | None, ctx: Context | None
163
+ ) -> E:
168
164
  """Convert a value into the `Enum` type."""
169
165
  try:
170
166
  return ensure_enum(value, self._enum, case_sensitive=self._case_sensitive)
@@ -383,11 +379,11 @@ class ZonedDateTime(ParamType):
383
379
  # parameters - frozenset
384
380
 
385
381
 
386
- class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
382
+ class FrozenSetParameter[P: ParamType, T](ParamType):
387
383
  """A frozenset-valued parameter."""
388
384
 
389
385
  @override
390
- def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
386
+ def __init__(self, param: P, /, *, separator: str = ",") -> None:
391
387
  self.name = f"frozenset[{param.name}]"
392
388
  self._param = param
393
389
  self._separator = separator
@@ -399,11 +395,8 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
399
395
 
400
396
  @override
401
397
  def convert(
402
- self,
403
- value: MaybeStr[Iterable[_T]],
404
- param: Parameter | None,
405
- ctx: Context | None,
406
- ) -> frozenset[_T]:
398
+ self, value: MaybeStr[Iterable[T]], param: Parameter | None, ctx: Context | None
399
+ ) -> frozenset[T]:
407
400
  """Convert a value into the `ListDates` type."""
408
401
  if is_iterable_not_str(value):
409
402
  return frozenset(value)
@@ -442,17 +435,12 @@ class FrozenSetChoices(FrozenSetParameter[Choice, str]):
442
435
  )
443
436
 
444
437
 
445
- class FrozenSetEnums(FrozenSetParameter[Enum[TEnum], TEnum]):
438
+ class FrozenSetEnums[E: enum.Enum](FrozenSetParameter[Enum[E], E]):
446
439
  """A frozenset-of-enums-valued parameter."""
447
440
 
448
441
  @override
449
442
  def __init__(
450
- self,
451
- enum: type[TEnum],
452
- /,
453
- *,
454
- case_sensitive: bool = False,
455
- separator: str = ",",
443
+ self, enum: type[E], /, *, case_sensitive: bool = False, separator: str = ","
456
444
  ) -> None:
457
445
  super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
458
446
 
@@ -468,11 +456,11 @@ class FrozenSetStrs(FrozenSetParameter[StringParamType, str]):
468
456
  # parameters - list
469
457
 
470
458
 
471
- class ListParameter(ParamType, Generic[_TParam, _T]):
459
+ class ListParameter[P: ParamType, T](ParamType):
472
460
  """A list-valued parameter."""
473
461
 
474
462
  @override
475
- def __init__(self, param: _TParam, /, *, separator: str = ",") -> None:
463
+ def __init__(self, param: P, /, *, separator: str = ",") -> None:
476
464
  self.name = f"list[{param.name}]"
477
465
  self._param = param
478
466
  self._separator = separator
@@ -484,11 +472,8 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
484
472
 
485
473
  @override
486
474
  def convert(
487
- self,
488
- value: MaybeStr[Iterable[_T]],
489
- param: Parameter | None,
490
- ctx: Context | None,
491
- ) -> list[_T]:
475
+ self, value: MaybeStr[Iterable[T]], param: Parameter | None, ctx: Context | None
476
+ ) -> list[T]:
492
477
  """Convert a value into the `List` type."""
493
478
  if is_iterable_not_str(value):
494
479
  return list(value)
@@ -527,17 +512,12 @@ class ListChoices(ListParameter[Choice, str]):
527
512
  )
528
513
 
529
514
 
530
- class ListEnums(ListParameter[Enum[TEnum], TEnum]):
515
+ class ListEnums[E: enum.Enum](ListParameter[Enum[E], E]):
531
516
  """A list-of-enums-valued parameter."""
532
517
 
533
518
  @override
534
519
  def __init__(
535
- self,
536
- enum: type[TEnum],
537
- /,
538
- *,
539
- case_sensitive: bool = False,
540
- separator: str = ",",
520
+ self, enum: type[E], /, *, case_sensitive: bool = False, separator: str = ","
541
521
  ) -> None:
542
522
  super().__init__(Enum(enum, case_sensitive=case_sensitive), separator=separator)
543
523
 
utilities/concurrent.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
4
4
  from functools import partial
5
- from typing import TYPE_CHECKING, Any, TypeVar, assert_never
5
+ from typing import TYPE_CHECKING, Any, assert_never
6
6
 
7
7
  from utilities.iterables import apply_to_tuple
8
8
  from utilities.os import get_cpu_use
@@ -15,11 +15,8 @@ if TYPE_CHECKING:
15
15
  from utilities.os import IntOrAll
16
16
 
17
17
 
18
- _T = TypeVar("_T")
19
-
20
-
21
- def concurrent_map(
22
- func: Callable[..., _T],
18
+ def concurrent_map[T](
19
+ func: Callable[..., T],
23
20
  /,
24
21
  *iterables: Iterable[Any],
25
22
  parallelism: Parallelism = "processes",
@@ -31,7 +28,7 @@ def concurrent_map(
31
28
  thread_name_prefix: str = "",
32
29
  timeout: float | None = None,
33
30
  chunksize: int = 1,
34
- ) -> list[_T]:
31
+ ) -> list[T]:
35
32
  """Concurrent map."""
36
33
  return concurrent_starmap(
37
34
  func,
@@ -51,8 +48,8 @@ def concurrent_map(
51
48
  ##
52
49
 
53
50
 
54
- def concurrent_starmap(
55
- func: Callable[..., _T],
51
+ def concurrent_starmap[T](
52
+ func: Callable[..., T],
56
53
  iterable: Iterable[tuple[Any, ...]],
57
54
  /,
58
55
  *,
@@ -65,7 +62,7 @@ def concurrent_starmap(
65
62
  thread_name_prefix: str = "",
66
63
  timeout: float | None = None,
67
64
  chunksize: int = 1,
68
- ) -> list[_T]:
65
+ ) -> list[T]:
69
66
  """Concurrent map."""
70
67
  max_workers_use = get_cpu_use(n=max_workers)
71
68
  apply = partial(apply_to_tuple, func)