stranded 0.0.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.
Files changed (71) hide show
  1. stranded/__init__.py +51 -0
  2. stranded/abc/__init__.py +21 -0
  3. stranded/abc/composer_.py +191 -0
  4. stranded/argparse/__init__.py +24 -0
  5. stranded/argparse/abc/__init__.py +21 -0
  6. stranded/argparse/abc/argument_parser_.py +539 -0
  7. stranded/argparse/argument_parser_.py +13 -0
  8. stranded/argparse/asyncio/__init__.py +24 -0
  9. stranded/argparse/asyncio/argument_parser_.py +50 -0
  10. stranded/argparse/threading/__init__.py +24 -0
  11. stranded/argparse/threading/argument_parser_.py +52 -0
  12. stranded/asyncio/__init__.py +21 -0
  13. stranded/asyncio/composer_.py +76 -0
  14. stranded/builtins/__init__.py +21 -0
  15. stranded/builtins/exception_.py +6 -0
  16. stranded/composer_.py +27 -0
  17. stranded/execution/__init__.py +30 -0
  18. stranded/execution/abc/__init__.py +21 -0
  19. stranded/execution/abc/scheduler_.py +59 -0
  20. stranded/execution/asyncio/__init__.py +21 -0
  21. stranded/execution/asyncio/scheduler_.py +86 -0
  22. stranded/execution/scheduler_.py +59 -0
  23. stranded/execution/threading/__init__.py +21 -0
  24. stranded/execution/threading/scheduler_.py +116 -0
  25. stranded/functools/__init__.py +51 -0
  26. stranded/functools/abc/__init__.py +39 -0
  27. stranded/functools/abc/herd_.py +96 -0
  28. stranded/functools/abc/lru_cache_.py +192 -0
  29. stranded/functools/abc/retry_.py +60 -0
  30. stranded/functools/abc/throttle_.py +97 -0
  31. stranded/functools/asyncio/__init__.py +51 -0
  32. stranded/functools/asyncio/herd_.py +89 -0
  33. stranded/functools/asyncio/lru_cache_.py +92 -0
  34. stranded/functools/asyncio/retry_.py +43 -0
  35. stranded/functools/asyncio/throttle_.py +74 -0
  36. stranded/functools/herd_.py +18 -0
  37. stranded/functools/lru_cache_.py +18 -0
  38. stranded/functools/retry_.py +13 -0
  39. stranded/functools/threading/__init__.py +51 -0
  40. stranded/functools/threading/herd_.py +88 -0
  41. stranded/functools/threading/lru_cache_.py +90 -0
  42. stranded/functools/threading/retry_.py +42 -0
  43. stranded/functools/threading/throttle_.py +75 -0
  44. stranded/functools/throttle_.py +13 -0
  45. stranded/logging/__init__.py +24 -0
  46. stranded/logging/abc/__init__.py +21 -0
  47. stranded/logging/abc/logger_.py +96 -0
  48. stranded/logging/asyncio/__init__.py +21 -0
  49. stranded/logging/asyncio/logger_.py +41 -0
  50. stranded/logging/logger_.py +13 -0
  51. stranded/logging/threading/__init__.py +21 -0
  52. stranded/logging/threading/logger_.py +41 -0
  53. stranded/py.typed +0 -0
  54. stranded/sqlite3/__init__.py +24 -0
  55. stranded/sqlite3/abc/__init__.py +21 -0
  56. stranded/sqlite3/abc/db_.py +149 -0
  57. stranded/sqlite3/asyncio/__init__.py +24 -0
  58. stranded/sqlite3/asyncio/db_.py +43 -0
  59. stranded/sqlite3/db_.py +13 -0
  60. stranded/sqlite3/threading/__init__.py +24 -0
  61. stranded/sqlite3/threading/db_.py +43 -0
  62. stranded/threading/__init__.py +21 -0
  63. stranded/threading/composer_.py +74 -0
  64. stranded/types/__init__.py +21 -0
  65. stranded/types/constraint.py +7 -0
  66. stranded/types/convert.py +158 -0
  67. stranded-0.0.1.dist-info/METADATA +173 -0
  68. stranded-0.0.1.dist-info/RECORD +71 -0
  69. stranded-0.0.1.dist-info/WHEEL +5 -0
  70. stranded-0.0.1.dist-info/licenses/LICENSE +21 -0
  71. stranded-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,92 @@
1
+ import asyncio
2
+ import dataclasses
3
+ import typing
4
+
5
+ from ..abc import lru_cache_
6
+ from ...asyncio import composer_
7
+
8
+
9
+ Raise = composer_.Raise
10
+ Stop = composer_.Stop
11
+ Param = composer_.Param
12
+ Return = composer_.Return
13
+
14
+
15
+ @typing.runtime_checkable
16
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], lru_cache_.Composee[ParamT, RetT], typing.Protocol): ...
17
+
18
+
19
+ @typing.final
20
+ @dataclasses.dataclass(frozen=True, kw_only=True)
21
+ class Future[RetT](lru_cache_.Future[RetT]):
22
+ future: asyncio.Future[RetT] = dataclasses.field(default_factory=asyncio.Future)
23
+
24
+ @typing.override
25
+ def set_value(self, value: Return[RetT] | Raise | Stop) -> None:
26
+ match value:
27
+ case Return(ret=ret): self.future.set_result(ret)
28
+ case Raise() as raise_: self.future.set_exception(raise_.exc_val)
29
+ case Stop() as stop_: self.future.set_exception(stop_)
30
+
31
+ @typing.override
32
+ async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> RetT:
33
+ return await self.future
34
+
35
+
36
+ @typing.final
37
+ @dataclasses.dataclass(frozen=True, kw_only=True)
38
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], lru_cache_.Connect[ParamT, RetT]): ...
39
+
40
+
41
+ @typing.final
42
+ @dataclasses.dataclass(frozen=True, kw_only=True)
43
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], lru_cache_.Exit[ParamT, RetT, Future[RetT]]):
44
+ @typing.override
45
+ async def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT: # type: ignore[override]
46
+ match value:
47
+ case Param(): pass
48
+ case Return() | Raise() | Stop(): self.future.set_value(value)
49
+
50
+ return ()
51
+
52
+
53
+ @typing.final
54
+ @dataclasses.dataclass(frozen=True, kw_only=True)
55
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], lru_cache_.Enter[ParamT, RetT, Future[RetT]]):
56
+ # TODO: Dedup this with the threading version.
57
+ @typing.override
58
+ async def __call__( # type: ignore[override]
59
+ self, value: composer_.ValueT[ParamT, RetT], /,
60
+ ) -> composer_.StackT:
61
+ match value:
62
+ case Param():
63
+ key = self.create_key(*value.args, **value.kwargs)
64
+ if (future := self.get_future(key)) is not None:
65
+ return future,
66
+ future = self.set_future(key)
67
+ return self.composer.Exit(enter=self, future=future, key=key), self.composee # type: ignore[call-arg, return-value]
68
+ case _: return ()
69
+
70
+
71
+ @typing.final
72
+ @dataclasses.dataclass(frozen=True, kw_only=True)
73
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], lru_cache_.Composed[ParamT, RetT]): ...
74
+
75
+
76
+ @typing.final
77
+ @dataclasses.dataclass(frozen=True, kw_only=True)
78
+ class LruCache[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], lru_cache_.Composer[ParamT, RetT]):
79
+ Composee: typing.ClassVar = Composee
80
+ Connect: typing.ClassVar = Connect
81
+ Exit: typing.ClassVar = Exit # type: ignore[assignment]
82
+ Enter: typing.ClassVar = Enter
83
+ Composed: typing.ClassVar = Composed
84
+
85
+ @property
86
+ @typing.override
87
+ def future_t(self) -> type[Future[RetT]]:
88
+ return Future
89
+
90
+
91
+ Composer = LruCache
92
+ lru_cache = LruCache()
@@ -0,0 +1,43 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from ..abc import retry_
5
+ from ...asyncio import composer_
6
+
7
+
8
+ @typing.runtime_checkable
9
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], retry_.Composee[ParamT, RetT], typing.Protocol): ...
10
+
11
+
12
+ @typing.final
13
+ @dataclasses.dataclass(frozen=True, kw_only=True)
14
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], retry_.Connect[ParamT, RetT]): ...
15
+
16
+
17
+ @typing.final
18
+ @dataclasses.dataclass(frozen=True, kw_only=True)
19
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], retry_.Exit[ParamT, RetT]): ... # type: ignore[misc]
20
+
21
+
22
+ @typing.final
23
+ @dataclasses.dataclass(frozen=True, kw_only=True)
24
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], retry_.Enter[ParamT, RetT]): ... # type: ignore[misc]
25
+
26
+
27
+ @typing.final
28
+ @dataclasses.dataclass(frozen=True, kw_only=True)
29
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], retry_.Composed[ParamT, RetT]): ...
30
+
31
+
32
+ @typing.final
33
+ @dataclasses.dataclass(frozen=True, kw_only=True)
34
+ class Retry[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], retry_.Composer[ParamT, RetT]):
35
+ Composee: typing.ClassVar = Composee
36
+ Connect: typing.ClassVar = Connect
37
+ Exit: typing.ClassVar = Exit
38
+ Enter: typing.ClassVar = Enter
39
+ Composed: typing.ClassVar = Composed
40
+
41
+
42
+ Composer = Retry
43
+ retry = Retry()
@@ -0,0 +1,74 @@
1
+ import asyncio
2
+ import dataclasses
3
+ import typing
4
+
5
+ from ..abc import throttle_
6
+ from ...asyncio import composer_
7
+
8
+ type _Condition = asyncio.Condition
9
+
10
+
11
+ @typing.runtime_checkable
12
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], throttle_.Composee[ParamT, RetT], typing.Protocol): ...
13
+
14
+
15
+ Param = composer_.Param
16
+ Raise = composer_.Raise
17
+ Return = composer_.Return
18
+ Stop = composer_.Stop
19
+
20
+
21
+ @typing.final
22
+ @dataclasses.dataclass(frozen=True, kw_only=True)
23
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], throttle_.Connect[ParamT, RetT]): ...
24
+
25
+
26
+ @typing.final
27
+ @dataclasses.dataclass(frozen=True, kw_only=True)
28
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], throttle_.Exit[ParamT, RetT]):
29
+ async def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT: # type: ignore[override]
30
+ async with self.enter.condition: # type: ignore[attr-defined]
31
+ return await super().__call__(value)
32
+
33
+
34
+ @typing.final
35
+ @dataclasses.dataclass(frozen=True, kw_only=True)
36
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], throttle_.Enter[ParamT, RetT]):
37
+ condition: _Condition = dataclasses.field(default_factory=asyncio.Condition)
38
+
39
+ async def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT: # type: ignore[override]
40
+ # TODO: this is mostly duplicate with the threading version. Try to consolidate.
41
+ match value:
42
+ case Param():
43
+ state = self.state
44
+ async with self.condition:
45
+ if state.num_waiting >= self.composer.max_waiting: # type: ignore[attr-defined]
46
+ raise Exception(f'Exceeded {self.composer.max_waiting=}.') # type: ignore[attr-defined]
47
+ elif 0 < state.num_waiting or state.cap_running <= state.num_running:
48
+ state.num_waiting += 1
49
+ await self.condition.wait_for(lambda: state.num_running < state.cap_running)
50
+ state.num_waiting -= 1
51
+ state.num_running += 1
52
+
53
+ return self.composer.Exit(enter=self), self.composee, # type: ignore[return-value]
54
+ case _:
55
+ return ()
56
+
57
+
58
+ @typing.final
59
+ @dataclasses.dataclass(frozen=True, kw_only=True)
60
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], throttle_.Composed[ParamT, RetT]): ...
61
+
62
+
63
+ @typing.final
64
+ @dataclasses.dataclass(frozen=True, kw_only=True)
65
+ class Throttle[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], throttle_.Composer[ParamT, RetT]):
66
+ Composee: typing.ClassVar = Composee
67
+ Connect: typing.ClassVar = Connect
68
+ Exit: typing.ClassVar = Exit
69
+ Enter: typing.ClassVar = Enter
70
+ Composed: typing.ClassVar = Composed
71
+
72
+
73
+ Composer = Throttle
74
+ throttle = Throttle()
@@ -0,0 +1,18 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from .abc import herd_
5
+ from .. import composer_
6
+
7
+
8
+ @dataclasses.dataclass(frozen=True, kw_only=True)
9
+ class Herd(composer_.Composer[..., typing.Any], herd_.Herd[..., typing.Any]):
10
+ @property
11
+ def future_t(self) -> type:
12
+ raise NotImplementedError(
13
+ f'{type(self).__name__} is a dispatcher; future_t is provided by the threading or asyncio variant.'
14
+ )
15
+
16
+
17
+ Composer = Herd
18
+ herd = Herd()
@@ -0,0 +1,18 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from .abc import lru_cache_
5
+ from .. import composer_
6
+
7
+
8
+ @dataclasses.dataclass(frozen=True, kw_only=True)
9
+ class LruCache(composer_.Composer[..., typing.Any], lru_cache_.LruCache[..., typing.Any]):
10
+ @property
11
+ def future_t(self) -> type:
12
+ raise NotImplementedError(
13
+ f'{type(self).__name__} is a dispatcher; future_t is provided by the threading or asyncio variant.'
14
+ )
15
+
16
+
17
+ Composer = LruCache
18
+ lru_cache = LruCache()
@@ -0,0 +1,13 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from .abc import retry_
5
+ from .. import composer_
6
+
7
+
8
+ @dataclasses.dataclass(frozen=True, kw_only=True)
9
+ class Retry(composer_.Composer[..., typing.Any], retry_.Retry[..., typing.Any]): ...
10
+
11
+
12
+ Composer = Retry
13
+ retry = Retry()
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib as _importlib
4
+ import typing as _typing
5
+
6
+ if _typing.TYPE_CHECKING:
7
+ from . import herd_
8
+ from .herd_ import Herd
9
+ from .herd_ import herd
10
+ from . import lru_cache_
11
+ from .lru_cache_ import LruCache
12
+ from .lru_cache_ import lru_cache
13
+ from . import retry_
14
+ from .retry_ import Retry
15
+ from .retry_ import retry
16
+ from . import throttle_
17
+ from .throttle_ import Throttle
18
+ from .throttle_ import throttle
19
+
20
+
21
+ def __getattr__(name: str) -> _typing.Any:
22
+ match name:
23
+ case 'herd_': return _importlib.import_module('.herd_', __name__)
24
+ case 'Herd': return _importlib.import_module('.herd_', __name__).Herd
25
+ case 'herd': return _importlib.import_module('.herd_', __name__).herd
26
+ case 'lru_cache_': return _importlib.import_module('.lru_cache_', __name__)
27
+ case 'LruCache': return _importlib.import_module('.lru_cache_', __name__).LruCache
28
+ case 'lru_cache': return _importlib.import_module('.lru_cache_', __name__).lru_cache
29
+ case 'retry_': return _importlib.import_module('.retry_', __name__)
30
+ case 'Retry': return _importlib.import_module('.retry_', __name__).Retry
31
+ case 'retry': return _importlib.import_module('.retry_', __name__).retry
32
+ case 'throttle_': return _importlib.import_module('.throttle_', __name__)
33
+ case 'Throttle': return _importlib.import_module('.throttle_', __name__).Throttle
34
+ case 'throttle': return _importlib.import_module('.throttle_', __name__).throttle
35
+ case _: raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
36
+
37
+
38
+ __all__ = (
39
+ 'herd_',
40
+ 'Herd',
41
+ 'herd',
42
+ 'lru_cache_',
43
+ 'LruCache',
44
+ 'lru_cache',
45
+ 'retry_',
46
+ 'Retry',
47
+ 'retry',
48
+ 'throttle_',
49
+ 'Throttle',
50
+ 'throttle',
51
+ )
@@ -0,0 +1,88 @@
1
+ import concurrent.futures
2
+ import dataclasses
3
+ import typing
4
+
5
+ from ..abc import herd_
6
+ from ...threading import composer_
7
+
8
+
9
+ Raise = composer_.Raise
10
+ Stop = composer_.Stop
11
+ Param = composer_.Param
12
+ Return = composer_.Return
13
+
14
+
15
+ @typing.runtime_checkable
16
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], herd_.Composee[ParamT, RetT], typing.Protocol): ...
17
+
18
+
19
+ @typing.final
20
+ @dataclasses.dataclass(frozen=True, kw_only=True)
21
+ class Future[RetT](herd_.Future[RetT]):
22
+ future: concurrent.futures.Future[RetT] = dataclasses.field(default_factory=concurrent.futures.Future)
23
+
24
+ @typing.override
25
+ def set_value(self, value: Return[RetT] | Raise | Stop) -> None:
26
+ match value:
27
+ case Return(ret=ret): self.future.set_result(ret)
28
+ case Raise() as raise_: self.future.set_exception(raise_.exc_val)
29
+ case Stop() as stop_: self.future.set_exception(stop_)
30
+
31
+ @typing.override
32
+ def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> RetT:
33
+ return self.future.result()
34
+
35
+
36
+ @typing.final
37
+ @dataclasses.dataclass(frozen=True, kw_only=True)
38
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], herd_.Connect[ParamT, RetT]): ...
39
+
40
+
41
+ @typing.final
42
+ @dataclasses.dataclass(frozen=True, kw_only=True)
43
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], herd_.Exit[ParamT, RetT, Future[RetT]]):
44
+ future: Future[RetT] = dataclasses.field(default_factory=Future)
45
+
46
+ def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT:
47
+ self.enter.future_by_key.pop(self.key, None) # type: ignore[attr-defined]
48
+ match value:
49
+ case Param(): pass
50
+ case Return() | Raise() | Stop(): self.future.set_value(value)
51
+
52
+ return ()
53
+
54
+
55
+ @typing.final
56
+ @dataclasses.dataclass(frozen=True, kw_only=True)
57
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], herd_.Enter[ParamT, RetT, Future[RetT]]):
58
+ @typing.override
59
+ def __call__(
60
+ self, value: composer_.ValueT[ParamT, RetT], /,
61
+ ) -> composer_.StackT:
62
+ match value:
63
+ case Param(): return self._dispatch(*value.args, **value.kwargs) # type: ignore[return-value]
64
+ case _: return ()
65
+
66
+
67
+ @typing.final
68
+ @dataclasses.dataclass(frozen=True, kw_only=True)
69
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], herd_.Composed[ParamT, RetT]): ...
70
+
71
+
72
+ @typing.final
73
+ @dataclasses.dataclass(frozen=True, kw_only=True)
74
+ class Herd[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], herd_.Herd[ParamT, RetT]):
75
+ Composee: typing.ClassVar = Composee
76
+ Connect: typing.ClassVar = Connect
77
+ Exit: typing.ClassVar = Exit # type: ignore[assignment]
78
+ Enter: typing.ClassVar = Enter
79
+ Composed: typing.ClassVar = Composed
80
+
81
+ @property
82
+ @typing.override
83
+ def future_t(self) -> type[Future[RetT]]:
84
+ return Future
85
+
86
+
87
+ Composer = Herd
88
+ herd = Herd()
@@ -0,0 +1,90 @@
1
+ import concurrent.futures
2
+ import dataclasses
3
+ import typing
4
+
5
+ from ..abc import lru_cache_
6
+ from ...threading import composer_
7
+
8
+
9
+ Raise = composer_.Raise
10
+ Stop = composer_.Stop
11
+ Param = composer_.Param
12
+ Return = composer_.Return
13
+
14
+
15
+ @typing.runtime_checkable
16
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], lru_cache_.Composee[ParamT, RetT], typing.Protocol): ...
17
+
18
+
19
+ @typing.final
20
+ @dataclasses.dataclass(frozen=True, kw_only=True)
21
+ class Future[RetT](lru_cache_.Future[RetT]):
22
+ future: concurrent.futures.Future[RetT] = dataclasses.field(default_factory=concurrent.futures.Future)
23
+
24
+ @typing.override
25
+ def set_value(self, value: Return[RetT] | Raise | Stop) -> None:
26
+ match value:
27
+ case Return(ret=ret): self.future.set_result(ret)
28
+ case Raise() as raise_: self.future.set_exception(raise_.exc_val)
29
+ case Stop() as stop_: self.future.set_exception(stop_)
30
+
31
+ @typing.override
32
+ def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> RetT:
33
+ return self.future.result()
34
+
35
+
36
+ @typing.final
37
+ @dataclasses.dataclass(frozen=True, kw_only=True)
38
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], lru_cache_.Connect[ParamT, RetT]): ...
39
+
40
+
41
+ @typing.final
42
+ @dataclasses.dataclass(frozen=True, kw_only=True)
43
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], lru_cache_.Exit[ParamT, RetT, Future[RetT]]):
44
+ def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT:
45
+ match value:
46
+ case Param(): pass
47
+ case Return() | Raise() | Stop(): self.future.set_value(value)
48
+
49
+ return ()
50
+
51
+
52
+ @typing.final
53
+ @dataclasses.dataclass(frozen=True, kw_only=True)
54
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], lru_cache_.Enter[ParamT, RetT, Future[RetT]]):
55
+ # TODO: Dedup this with the asyncio version.
56
+ def __call__(
57
+ self, value: composer_.ValueT[ParamT, RetT], /,
58
+ ) -> composer_.StackT:
59
+ match value:
60
+ case Param():
61
+ key = self.create_key(*value.args, **value.kwargs)
62
+ if (future := self.get_future(key)) is not None:
63
+ return future,
64
+ future = self.set_future(key)
65
+ return self.composer.Exit(enter=self, future=future, key=key), self.composee # type: ignore[call-arg, return-value]
66
+ case _: return ()
67
+
68
+
69
+ @typing.final
70
+ @dataclasses.dataclass(frozen=True, kw_only=True)
71
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], lru_cache_.Composed[ParamT, RetT]): ...
72
+
73
+
74
+ @typing.final
75
+ @dataclasses.dataclass(frozen=True, kw_only=True)
76
+ class LruCache[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], lru_cache_.Composer[ParamT, RetT]):
77
+ Composee: typing.ClassVar = Composee
78
+ Connect: typing.ClassVar = Connect
79
+ Exit: typing.ClassVar = Exit # type: ignore[assignment]
80
+ Enter: typing.ClassVar = Enter
81
+ Composed: typing.ClassVar = Composed
82
+
83
+ @property
84
+ @typing.override
85
+ def future_t(self) -> type[Future[RetT]]:
86
+ return Future
87
+
88
+
89
+ Composer = LruCache
90
+ lru_cache = LruCache()
@@ -0,0 +1,42 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from ..abc import retry_
5
+ from ...threading import composer_
6
+
7
+ @typing.runtime_checkable
8
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], retry_.Composee[ParamT, RetT], typing.Protocol): ...
9
+
10
+
11
+ @typing.final
12
+ @dataclasses.dataclass(frozen=True, kw_only=True)
13
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], retry_.Connect[ParamT, RetT]): ...
14
+
15
+
16
+ @typing.final
17
+ @dataclasses.dataclass(frozen=True, kw_only=True)
18
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], retry_.Exit[ParamT, RetT]): ...
19
+
20
+
21
+ @typing.final
22
+ @dataclasses.dataclass(frozen=True, kw_only=True)
23
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], retry_.Enter[ParamT, RetT]): ...
24
+
25
+
26
+ @typing.final
27
+ @dataclasses.dataclass(frozen=True, kw_only=True)
28
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], retry_.Composed[ParamT, RetT]): ...
29
+
30
+
31
+ @typing.final
32
+ @dataclasses.dataclass(frozen=True, kw_only=True)
33
+ class Retry[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], retry_.Composer[ParamT, RetT]):
34
+ Composee: typing.ClassVar = Composee
35
+ Connect: typing.ClassVar = Connect
36
+ Exit: typing.ClassVar = Exit
37
+ Enter: typing.ClassVar = Enter
38
+ Composed: typing.ClassVar = Composed
39
+
40
+
41
+ Composer = Retry
42
+ retry = Retry()
@@ -0,0 +1,75 @@
1
+ import dataclasses
2
+ import threading
3
+ import typing
4
+
5
+ from ...threading import composer_
6
+ from ..abc import throttle_
7
+
8
+
9
+ type _Condition = threading.Condition
10
+
11
+
12
+ @typing.runtime_checkable
13
+ class Composee[**ParamT, RetT](composer_.Composee[ParamT, RetT], throttle_.Composee[ParamT, RetT], typing.Protocol): ...
14
+
15
+
16
+ Param = composer_.Param
17
+ Raise = composer_.Raise
18
+ Return = composer_.Return
19
+ Stop = composer_.Stop
20
+
21
+
22
+ @typing.final
23
+ @dataclasses.dataclass(frozen=True, kw_only=True)
24
+ class Connect[**ParamT, RetT](composer_.Connect[ParamT, RetT], throttle_.Connect[ParamT, RetT]): ...
25
+
26
+
27
+ @typing.final
28
+ @dataclasses.dataclass(frozen=True, kw_only=True)
29
+ class Exit[**ParamT, RetT](composer_.Exit[ParamT, RetT], throttle_.Exit[ParamT, RetT]):
30
+ def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT:
31
+ with self.enter.condition: # type: ignore[attr-defined]
32
+ return super().__call__(value)
33
+
34
+
35
+ @typing.final
36
+ @dataclasses.dataclass(frozen=True, kw_only=True)
37
+ class Enter[**ParamT, RetT](composer_.Enter[ParamT, RetT], throttle_.Enter[ParamT, RetT]):
38
+ condition: _Condition = dataclasses.field(default_factory=threading.Condition)
39
+
40
+ def __call__(self, value: composer_.ValueT[ParamT, RetT], /) -> composer_.StackT:
41
+ # TODO: this is mostly duplicate with the asyncio version. Try to consolidate.
42
+ match value:
43
+ case Param():
44
+ state = self.state
45
+ with self.condition:
46
+ if state.num_waiting >= self.composer.max_waiting: # type: ignore[attr-defined]
47
+ raise Exception(f'Exceeded {self.composer.max_waiting=}.') # type: ignore[attr-defined]
48
+ elif 0 < state.num_waiting or state.cap_running <= state.num_running:
49
+ state.num_waiting += 1
50
+ self.condition.wait_for(lambda: state.num_running < state.cap_running)
51
+ state.num_waiting -= 1
52
+ state.num_running += 1
53
+
54
+ return self.composer.Exit(enter=self), self.composee, # type: ignore[return-value]
55
+ case _:
56
+ return ()
57
+
58
+
59
+ @typing.final
60
+ @dataclasses.dataclass(frozen=True, kw_only=True)
61
+ class Composed[**ParamT, RetT](composer_.Composed[ParamT, RetT], throttle_.Composed[ParamT, RetT]): ...
62
+
63
+
64
+ @typing.final
65
+ @dataclasses.dataclass(frozen=True, kw_only=True)
66
+ class Throttle[**ParamT = ..., RetT = typing.Any](composer_.Composer[ParamT, RetT], throttle_.Composer[ParamT, RetT]):
67
+ Composee: typing.ClassVar = Composee
68
+ Connect: typing.ClassVar = Connect
69
+ Exit: typing.ClassVar = Exit
70
+ Enter: typing.ClassVar = Enter
71
+ Composed: typing.ClassVar = Composed
72
+
73
+
74
+ Composer = Throttle
75
+ throttle = Throttle()
@@ -0,0 +1,13 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from .abc import throttle_
5
+ from .. import composer_
6
+
7
+
8
+ @dataclasses.dataclass(frozen=True, kw_only=True)
9
+ class Throttle(composer_.Composer[..., typing.Any], throttle_.Throttle[..., typing.Any]): ...
10
+
11
+
12
+ Composer = Throttle
13
+ throttle = Throttle()
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib as _importlib
4
+ import typing as _typing
5
+
6
+ if _typing.TYPE_CHECKING:
7
+ from . import logger_
8
+ from .logger_ import Logger
9
+ from .logger_ import logger
10
+
11
+
12
+ def __getattr__(name: str) -> _typing.Any:
13
+ match name:
14
+ case 'logger_': return _importlib.import_module('.logger_', __name__)
15
+ case 'Logger': return _importlib.import_module('.logger_', __name__).Logger
16
+ case 'logger': return _importlib.import_module('.logger_', __name__).logger
17
+ case _: raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
18
+
19
+
20
+ __all__ = (
21
+ 'logger_',
22
+ 'Logger',
23
+ 'logger',
24
+ )
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib as _importlib
4
+ import typing as _typing
5
+
6
+ if _typing.TYPE_CHECKING:
7
+ from . import logger_
8
+ from .logger_ import Logger
9
+
10
+
11
+ def __getattr__(name: str) -> _typing.Any:
12
+ match name:
13
+ case 'logger_': return _importlib.import_module('.logger_', __name__)
14
+ case 'Logger': return _importlib.import_module('.logger_', __name__).Logger
15
+ case _: raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
16
+
17
+
18
+ __all__ = (
19
+ 'logger_',
20
+ 'Logger',
21
+ )