omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev493__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 omlish might be problematic. Click here for more details.

Files changed (265) hide show
  1. omlish/.omlish-manifests.json +12 -0
  2. omlish/README.md +199 -0
  3. omlish/__about__.py +21 -16
  4. omlish/argparse/all.py +17 -9
  5. omlish/argparse/cli.py +16 -3
  6. omlish/argparse/utils.py +21 -0
  7. omlish/asyncs/asyncio/rlock.py +110 -0
  8. omlish/asyncs/asyncio/sync.py +43 -0
  9. omlish/asyncs/asyncio/utils.py +2 -0
  10. omlish/asyncs/sync.py +25 -0
  11. omlish/bootstrap/_marshal.py +1 -1
  12. omlish/bootstrap/diag.py +12 -21
  13. omlish/bootstrap/main.py +2 -5
  14. omlish/bootstrap/sys.py +27 -28
  15. omlish/cexts/__init__.py +0 -0
  16. omlish/cexts/include/omlish/omlish.hh +1 -0
  17. omlish/collections/__init__.py +13 -1
  18. omlish/collections/attrregistry.py +210 -0
  19. omlish/collections/cache/impl.py +1 -0
  20. omlish/collections/identity.py +1 -0
  21. omlish/collections/mappings.py +28 -0
  22. omlish/collections/trie.py +5 -1
  23. omlish/collections/utils.py +77 -0
  24. omlish/concurrent/all.py +2 -1
  25. omlish/concurrent/futures.py +25 -0
  26. omlish/concurrent/threadlets.py +1 -1
  27. omlish/daemons/reparent.py +2 -3
  28. omlish/daemons/spawning.py +2 -3
  29. omlish/dataclasses/__init__.py +2 -0
  30. omlish/dataclasses/impl/api/classes/decorator.py +3 -0
  31. omlish/dataclasses/impl/api/classes/make.py +3 -0
  32. omlish/dataclasses/impl/concerns/repr.py +15 -2
  33. omlish/dataclasses/impl/configs.py +97 -37
  34. omlish/dataclasses/impl/generation/compilation.py +21 -19
  35. omlish/dataclasses/impl/generation/globals.py +1 -0
  36. omlish/dataclasses/impl/generation/ops.py +1 -0
  37. omlish/dataclasses/impl/generation/plans.py +2 -17
  38. omlish/dataclasses/impl/generation/processor.py +106 -25
  39. omlish/dataclasses/impl/processing/base.py +8 -0
  40. omlish/dataclasses/impl/processing/driving.py +19 -7
  41. omlish/dataclasses/specs.py +1 -0
  42. omlish/dataclasses/tools/modifiers.py +5 -0
  43. omlish/diag/_pycharm/runhack.py +1 -1
  44. omlish/diag/cmds/__init__.py +0 -0
  45. omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
  46. omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
  47. omlish/diag/{ps.py → cmds/ps.py} +6 -6
  48. omlish/diag/pycharm.py +16 -2
  49. omlish/diag/pydevd.py +58 -40
  50. omlish/diag/replserver/console.py +1 -1
  51. omlish/dispatch/__init__.py +18 -12
  52. omlish/dispatch/methods.py +50 -140
  53. omlish/dom/rendering.py +1 -1
  54. omlish/formats/dotenv.py +1 -1
  55. omlish/formats/json/stream/__init__.py +13 -0
  56. omlish/funcs/guard.py +225 -0
  57. omlish/graphs/dot/rendering.py +1 -1
  58. omlish/http/all.py +44 -4
  59. omlish/http/clients/asyncs.py +33 -27
  60. omlish/http/clients/base.py +17 -1
  61. omlish/http/clients/coro/__init__.py +0 -0
  62. omlish/http/clients/coro/sync.py +171 -0
  63. omlish/http/clients/default.py +208 -29
  64. omlish/http/clients/executor.py +56 -0
  65. omlish/http/clients/httpx.py +72 -11
  66. omlish/http/clients/middleware.py +181 -0
  67. omlish/http/clients/sync.py +33 -27
  68. omlish/http/clients/syncasync.py +49 -0
  69. omlish/http/clients/urllib.py +6 -3
  70. omlish/http/coro/client/connection.py +15 -6
  71. omlish/http/coro/io.py +2 -0
  72. omlish/http/flasky/__init__.py +40 -0
  73. omlish/http/flasky/_compat.py +2 -0
  74. omlish/http/flasky/api.py +82 -0
  75. omlish/http/flasky/app.py +203 -0
  76. omlish/http/flasky/cvs.py +59 -0
  77. omlish/http/flasky/requests.py +20 -0
  78. omlish/http/flasky/responses.py +23 -0
  79. omlish/http/flasky/routes.py +23 -0
  80. omlish/http/flasky/types.py +15 -0
  81. omlish/http/urls.py +67 -0
  82. omlish/inject/__init__.py +57 -29
  83. omlish/inject/_dataclasses.py +5148 -0
  84. omlish/inject/binder.py +11 -52
  85. omlish/inject/eagers.py +2 -0
  86. omlish/inject/elements.py +27 -0
  87. omlish/inject/helpers/__init__.py +0 -0
  88. omlish/inject/{utils.py → helpers/constfn.py} +3 -3
  89. omlish/inject/{tags.py → helpers/id.py} +2 -2
  90. omlish/inject/helpers/late.py +76 -0
  91. omlish/inject/{managed.py → helpers/managed.py} +10 -10
  92. omlish/inject/helpers/multis.py +143 -0
  93. omlish/inject/helpers/wrappers.py +54 -0
  94. omlish/inject/impl/elements.py +54 -21
  95. omlish/inject/impl/injector.py +29 -25
  96. omlish/inject/impl/inspect.py +10 -1
  97. omlish/inject/impl/maysync.py +3 -4
  98. omlish/inject/impl/multis.py +3 -0
  99. omlish/inject/impl/sync.py +3 -4
  100. omlish/inject/injector.py +31 -2
  101. omlish/inject/inspect.py +35 -0
  102. omlish/inject/maysync.py +2 -4
  103. omlish/inject/multis.py +8 -0
  104. omlish/inject/overrides.py +3 -3
  105. omlish/inject/privates.py +6 -0
  106. omlish/inject/providers.py +3 -2
  107. omlish/inject/sync.py +5 -4
  108. omlish/io/buffers.py +118 -0
  109. omlish/io/readers.py +29 -0
  110. omlish/iterators/transforms.py +2 -2
  111. omlish/lang/__init__.py +180 -97
  112. omlish/lang/_asyncs.cc +186 -0
  113. omlish/lang/asyncs.py +17 -0
  114. omlish/lang/casing.py +11 -0
  115. omlish/lang/contextmanagers.py +28 -4
  116. omlish/lang/functions.py +31 -22
  117. omlish/lang/imports/_capture.cc +11 -9
  118. omlish/lang/imports/capture.py +363 -170
  119. omlish/lang/imports/proxy.py +455 -152
  120. omlish/lang/lazyglobals.py +22 -9
  121. omlish/lang/params.py +17 -0
  122. omlish/lang/recursion.py +0 -1
  123. omlish/lang/sequences.py +124 -0
  124. omlish/lifecycles/README.md +30 -0
  125. omlish/lifecycles/__init__.py +87 -13
  126. omlish/lifecycles/_dataclasses.py +1388 -0
  127. omlish/lifecycles/base.py +178 -64
  128. omlish/lifecycles/contextmanagers.py +113 -4
  129. omlish/lifecycles/controller.py +150 -87
  130. omlish/lifecycles/injection.py +143 -0
  131. omlish/lifecycles/listeners.py +56 -0
  132. omlish/lifecycles/managed.py +142 -0
  133. omlish/lifecycles/manager.py +218 -93
  134. omlish/lifecycles/states.py +2 -0
  135. omlish/lifecycles/transitions.py +3 -0
  136. omlish/lifecycles/unwrap.py +57 -0
  137. omlish/lite/abstract.py +54 -24
  138. omlish/lite/asyncs.py +2 -2
  139. omlish/lite/attrops.py +2 -0
  140. omlish/lite/contextmanagers.py +4 -4
  141. omlish/lite/dataclasses.py +44 -0
  142. omlish/lite/maybes.py +8 -0
  143. omlish/lite/maysync.py +1 -5
  144. omlish/lite/pycharm.py +1 -1
  145. omlish/lite/typing.py +24 -0
  146. omlish/logs/_amalg.py +1 -1
  147. omlish/logs/all.py +25 -11
  148. omlish/logs/asyncs.py +73 -0
  149. omlish/logs/base.py +101 -12
  150. omlish/logs/contexts.py +4 -1
  151. omlish/logs/lists.py +125 -0
  152. omlish/logs/modules.py +19 -1
  153. omlish/logs/std/loggers.py +6 -1
  154. omlish/logs/std/noisy.py +11 -9
  155. omlish/logs/{standard.py → std/standard.py} +3 -4
  156. omlish/logs/utils.py +17 -2
  157. omlish/manifests/loading.py +2 -1
  158. omlish/marshal/__init__.py +33 -13
  159. omlish/marshal/_dataclasses.py +2774 -0
  160. omlish/marshal/base/configs.py +12 -0
  161. omlish/marshal/base/contexts.py +36 -21
  162. omlish/marshal/base/funcs.py +8 -11
  163. omlish/marshal/base/options.py +8 -0
  164. omlish/marshal/base/registries.py +146 -44
  165. omlish/marshal/base/types.py +40 -16
  166. omlish/marshal/composite/iterables.py +33 -20
  167. omlish/marshal/composite/literals.py +20 -18
  168. omlish/marshal/composite/mappings.py +36 -23
  169. omlish/marshal/composite/maybes.py +29 -19
  170. omlish/marshal/composite/newtypes.py +16 -16
  171. omlish/marshal/composite/optionals.py +14 -14
  172. omlish/marshal/composite/special.py +15 -15
  173. omlish/marshal/composite/unions/__init__.py +0 -0
  174. omlish/marshal/composite/unions/literals.py +93 -0
  175. omlish/marshal/composite/unions/primitives.py +103 -0
  176. omlish/marshal/factories/invalidate.py +18 -68
  177. omlish/marshal/factories/method.py +26 -0
  178. omlish/marshal/factories/moduleimport/factories.py +22 -65
  179. omlish/marshal/factories/multi.py +13 -25
  180. omlish/marshal/factories/recursive.py +42 -56
  181. omlish/marshal/factories/typecache.py +29 -74
  182. omlish/marshal/factories/typemap.py +42 -43
  183. omlish/marshal/objects/dataclasses.py +129 -106
  184. omlish/marshal/objects/marshal.py +18 -14
  185. omlish/marshal/objects/namedtuples.py +48 -42
  186. omlish/marshal/objects/unmarshal.py +19 -15
  187. omlish/marshal/polymorphism/marshal.py +9 -11
  188. omlish/marshal/polymorphism/metadata.py +16 -5
  189. omlish/marshal/polymorphism/standard.py +13 -1
  190. omlish/marshal/polymorphism/unions.py +15 -105
  191. omlish/marshal/polymorphism/unmarshal.py +9 -10
  192. omlish/marshal/singular/enums.py +14 -18
  193. omlish/marshal/standard.py +10 -6
  194. omlish/marshal/trivial/any.py +1 -1
  195. omlish/marshal/trivial/forbidden.py +21 -26
  196. omlish/metadata.py +23 -1
  197. omlish/os/forkhooks.py +4 -0
  198. omlish/os/pidfiles/pinning.py +2 -2
  199. omlish/reflect/__init__.py +43 -26
  200. omlish/reflect/ops.py +10 -1
  201. omlish/reflect/types.py +1 -0
  202. omlish/secrets/marshal.py +1 -1
  203. omlish/specs/jmespath/__init__.py +12 -3
  204. omlish/specs/jmespath/_dataclasses.py +2893 -0
  205. omlish/specs/jmespath/ast.py +1 -1
  206. omlish/specs/jsonrpc/__init__.py +13 -0
  207. omlish/specs/jsonrpc/_marshal.py +32 -23
  208. omlish/specs/jsonrpc/conns.py +10 -7
  209. omlish/specs/jsonschema/_marshal.py +1 -1
  210. omlish/specs/jsonschema/keywords/__init__.py +7 -0
  211. omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
  212. omlish/specs/openapi/_marshal.py +31 -22
  213. omlish/sql/__init__.py +24 -5
  214. omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
  215. omlish/sql/api/dbapi.py +1 -1
  216. omlish/sql/dbapi/__init__.py +15 -0
  217. omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
  218. omlish/sql/queries/__init__.py +3 -0
  219. omlish/sql/queries/_marshal.py +2 -2
  220. omlish/sql/queries/rendering.py +1 -1
  221. omlish/sql/tabledefs/_marshal.py +1 -1
  222. omlish/subprocesses/base.py +4 -0
  223. omlish/subprocesses/editor.py +1 -1
  224. omlish/sync.py +155 -21
  225. omlish/term/alt.py +60 -0
  226. omlish/term/confirm.py +8 -8
  227. omlish/term/pager.py +235 -0
  228. omlish/term/terminfo.py +935 -0
  229. omlish/term/termstate.py +67 -0
  230. omlish/term/vt100/terminal.py +0 -3
  231. omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
  232. omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
  233. omlish/testing/pytest/plugins/skips.py +2 -1
  234. omlish/testing/unittest/main.py +3 -3
  235. omlish/text/docwrap/__init__.py +3 -0
  236. omlish/text/docwrap/__main__.py +11 -0
  237. omlish/text/docwrap/api.py +83 -0
  238. omlish/text/docwrap/cli.py +91 -0
  239. omlish/text/docwrap/groups.py +84 -0
  240. omlish/text/docwrap/lists.py +167 -0
  241. omlish/text/docwrap/parts.py +146 -0
  242. omlish/text/docwrap/reflowing.py +106 -0
  243. omlish/text/docwrap/rendering.py +151 -0
  244. omlish/text/docwrap/utils.py +11 -0
  245. omlish/text/docwrap/wrapping.py +59 -0
  246. omlish/text/filecache.py +2 -2
  247. omlish/text/lorem.py +6 -0
  248. omlish/text/parts.py +2 -2
  249. omlish/text/textwrap.py +51 -0
  250. omlish/typedvalues/marshal.py +85 -59
  251. omlish/typedvalues/values.py +2 -1
  252. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
  253. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
  254. omlish/dataclasses/impl/generation/mangling.py +0 -18
  255. omlish/funcs/match.py +0 -227
  256. omlish/lifecycles/abstract.py +0 -86
  257. omlish/marshal/factories/match.py +0 -34
  258. omlish/marshal/factories/simple.py +0 -28
  259. /omlish/inject/{impl → helpers}/proxy.py +0 -0
  260. /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
  261. /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
  262. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
  263. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  264. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  265. {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
omlish/lifecycles/base.py CHANGED
@@ -1,103 +1,217 @@
1
1
  import typing as ta
2
2
 
3
+ from .. import check
3
4
  from .. import dataclasses as dc
4
5
  from .. import lang
6
+ from .states import LifecycleState
5
7
 
6
8
 
7
- R = ta.TypeVar('R')
9
+ ##
10
+
11
+
12
+ class Lifecycle(lang.Abstract):
13
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
14
+ super().__init_subclass__(**kwargs)
15
+
16
+ try:
17
+ async_lifecycle_cls = AsyncLifecycle
18
+ except NameError:
19
+ pass
20
+ else:
21
+ check.not_issubclass(cls, async_lifecycle_cls)
22
+
23
+ def lifecycle_state(self, state: LifecycleState) -> None:
24
+ """
25
+ This method is not intended for actual work - this is intended solely for safe bookkeeping. Only do real work in
26
+ the explicit per-state methods.
27
+ """
28
+
29
+ def lifecycle_construct(self) -> None:
30
+ pass
31
+
32
+ def lifecycle_start(self) -> None:
33
+ pass
34
+
35
+ def lifecycle_stop(self) -> None:
36
+ pass
37
+
38
+ def lifecycle_destroy(self) -> None:
39
+ pass
40
+
41
+
42
+ class AsyncLifecycle(lang.Abstract):
43
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
44
+ super().__init_subclass__(**kwargs)
45
+
46
+ check.not_issubclass(cls, Lifecycle)
47
+
48
+ async def lifecycle_state(self, state: LifecycleState) -> None:
49
+ """
50
+ This method is not intended for actual work - this is intended solely for safe bookkeeping. Only do real work in
51
+ the explicit per-state methods.
52
+ """
53
+
54
+ async def lifecycle_construct(self) -> None:
55
+ pass
56
+
57
+ async def lifecycle_start(self) -> None:
58
+ pass
59
+
60
+ async def lifecycle_stop(self) -> None:
61
+ pass
8
62
 
9
- AnyLifecycleT = ta.TypeVar('AnyLifecycleT', bound='AnyLifecycle')
10
- AnyLifecycleCallback: ta.TypeAlias = ta.Callable[[AnyLifecycleT], R]
63
+ async def lifecycle_destroy(self) -> None:
64
+ pass
11
65
 
12
- LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
13
- LifecycleCallback: ta.TypeAlias = ta.Callable[[LifecycleT], R]
14
66
 
15
- AsyncLifecycleT = ta.TypeVar('AsyncLifecycleT', bound='AsyncLifecycle')
16
- AsyncLifecycleCallback: ta.TypeAlias = ta.Callable[[AsyncLifecycleT], R]
67
+ AnyLifecycle: ta.TypeAlias = Lifecycle | AsyncLifecycle
68
+
69
+ ANY_LIFECYCLE_TYPES: tuple[type[Lifecycle | AsyncLifecycle], ...] = (Lifecycle, AsyncLifecycle)
17
70
 
18
71
 
19
72
  ##
20
73
 
21
74
 
22
- class AnyLifecycle(lang.Abstract, ta.Generic[R]):
23
- def lifecycle_construct(self) -> R | None:
24
- return None
75
+ @ta.final
76
+ @dc.dataclass(frozen=True)
77
+ class _SyncToAsyncLifecycle(AsyncLifecycle, lang.Final):
78
+ lifecycle: Lifecycle
79
+
80
+ async def lifecycle_state(self, state: LifecycleState) -> None:
81
+ self.lifecycle.lifecycle_state(state)
82
+
83
+ async def lifecycle_construct(self) -> None:
84
+ self.lifecycle.lifecycle_construct()
85
+
86
+ async def lifecycle_start(self) -> None:
87
+ self.lifecycle.lifecycle_start()
88
+
89
+ async def lifecycle_stop(self) -> None:
90
+ self.lifecycle.lifecycle_stop()
91
+
92
+ async def lifecycle_destroy(self) -> None:
93
+ self.lifecycle.lifecycle_destroy()
94
+
95
+
96
+ def sync_to_async_lifecycle(lifecycle: Lifecycle) -> AsyncLifecycle:
97
+ return _SyncToAsyncLifecycle(lifecycle)
98
+
99
+
100
+ def as_async_lifecycle(lifecycle: Lifecycle | AsyncLifecycle) -> AsyncLifecycle:
101
+ if isinstance(lifecycle, Lifecycle):
102
+ return sync_to_async_lifecycle(lifecycle)
103
+
104
+ elif isinstance(lifecycle, AsyncLifecycle):
105
+ return lifecycle
106
+
107
+ else:
108
+ raise TypeError(lifecycle)
109
+
110
+
111
+ @ta.final
112
+ @dc.dataclass(frozen=True)
113
+ class _AsyncToSyncLifecycle(Lifecycle, lang.Final):
114
+ lifecycle: AsyncLifecycle
115
+
116
+ def lifecycle_state(self, state: LifecycleState) -> None:
117
+ lang.sync_await(self.lifecycle.lifecycle_state(state))
118
+
119
+ def lifecycle_construct(self) -> None:
120
+ lang.sync_await(self.lifecycle.lifecycle_construct())
25
121
 
26
- def lifecycle_start(self) -> R | None:
27
- return None
122
+ def lifecycle_start(self) -> None:
123
+ lang.sync_await(self.lifecycle.lifecycle_start())
28
124
 
29
- def lifecycle_stop(self) -> R | None:
30
- return None
125
+ def lifecycle_stop(self) -> None:
126
+ lang.sync_await(self.lifecycle.lifecycle_stop())
31
127
 
32
- def lifecycle_destroy(self) -> R | None:
33
- return None
128
+ def lifecycle_destroy(self) -> None:
129
+ lang.sync_await(self.lifecycle.lifecycle_destroy())
34
130
 
35
131
 
132
+ def async_to_sync_lifecycle(lifecycle: AsyncLifecycle) -> Lifecycle:
133
+ return _AsyncToSyncLifecycle(lifecycle)
134
+
135
+
136
+ def as_sync_lifecycle(lifecycle: Lifecycle | AsyncLifecycle) -> Lifecycle:
137
+ if isinstance(lifecycle, Lifecycle):
138
+ return lifecycle
139
+
140
+ elif isinstance(lifecycle, AsyncLifecycle):
141
+ return async_to_sync_lifecycle(lifecycle)
142
+
143
+ else:
144
+ raise TypeError(lifecycle)
145
+
146
+
147
+ ##
148
+
149
+
150
+ @ta.final
36
151
  @dc.dataclass(frozen=True, kw_only=True)
37
- class AnyCallbackLifecycle(
38
- AnyLifecycle[R],
39
- lang.Abstract,
40
- ta.Generic[AnyLifecycleT, R],
41
- ):
42
- on_construct: AnyLifecycleCallback['AnyCallbackLifecycle[AnyLifecycleT, R]', R] | None = None
43
- on_start: AnyLifecycleCallback['AnyCallbackLifecycle[AnyLifecycleT, R]', R] | None = None
44
- on_stop: AnyLifecycleCallback['AnyCallbackLifecycle[AnyLifecycleT, R]', R] | None = None
45
- on_destroy: AnyLifecycleCallback['AnyCallbackLifecycle[AnyLifecycleT, R]', R] | None = None
152
+ class CallbackLifecycle(Lifecycle, lang.Final):
153
+ on_state: ta.Callable[[LifecycleState], None] | None = None
154
+ on_construct: ta.Callable[[], None] | None = None
155
+ on_start: ta.Callable[[], None] | None = None
156
+ on_stop: ta.Callable[[], None] | None = None
157
+ on_destroy: ta.Callable[[], None] | None = None
158
+
159
+ @ta.override
160
+ def lifecycle_state(self, state: LifecycleState) -> None:
161
+ if self.on_state is not None:
162
+ self.on_state(state)
46
163
 
47
164
  @ta.override
48
- def lifecycle_construct(self) -> R | None:
165
+ def lifecycle_construct(self) -> None:
49
166
  if self.on_construct is not None:
50
- return self.on_construct(self)
51
- else:
52
- return None
167
+ self.on_construct()
53
168
 
54
169
  @ta.override
55
- def lifecycle_start(self) -> R | None:
170
+ def lifecycle_start(self) -> None:
56
171
  if self.on_start is not None:
57
- return self.on_start(self)
58
- else:
59
- return None
172
+ self.on_start()
60
173
 
61
174
  @ta.override
62
- def lifecycle_stop(self) -> R | None:
175
+ def lifecycle_stop(self) -> None:
63
176
  if self.on_stop is not None:
64
- return self.on_stop(self)
65
- else:
66
- return None
177
+ self.on_stop()
67
178
 
68
179
  @ta.override
69
- def lifecycle_destroy(self) -> R | None:
180
+ def lifecycle_destroy(self) -> None:
70
181
  if self.on_destroy is not None:
71
- return self.on_destroy(self)
72
- else:
73
- return None
74
-
75
-
76
- ##
77
-
78
-
79
- class Lifecycle(AnyLifecycle[None]):
80
- pass
182
+ self.on_destroy()
81
183
 
82
184
 
83
- class CallbackLifecycle(
84
- AnyCallbackLifecycle[LifecycleT, None],
85
- lang.Final,
86
- ta.Generic[LifecycleT],
87
- ):
88
- pass
89
-
185
+ @ta.final
186
+ @dc.dataclass(frozen=True, kw_only=True)
187
+ class CallbackAsyncLifecycle(AsyncLifecycle, lang.Final):
188
+ on_state: ta.Callable[[LifecycleState], ta.Awaitable[None]] | None = None
189
+ on_construct: ta.Callable[[], ta.Awaitable[None]] | None = None
190
+ on_start: ta.Callable[[], ta.Awaitable[None]] | None = None
191
+ on_stop: ta.Callable[[], ta.Awaitable[None]] | None = None
192
+ on_destroy: ta.Callable[[], ta.Awaitable[None]] | None = None
90
193
 
91
- ##
194
+ @ta.override
195
+ async def lifecycle_state(self, state: LifecycleState) -> None:
196
+ if self.on_state is not None:
197
+ await self.on_state(state)
92
198
 
199
+ @ta.override
200
+ async def lifecycle_construct(self) -> None:
201
+ if self.on_construct is not None:
202
+ await self.on_construct()
93
203
 
94
- class AsyncLifecycle(AnyLifecycle[ta.Awaitable[None]]):
95
- pass
204
+ @ta.override
205
+ async def lifecycle_start(self) -> None:
206
+ if self.on_start is not None:
207
+ await self.on_start()
96
208
 
209
+ @ta.override
210
+ async def lifecycle_stop(self) -> None:
211
+ if self.on_stop is not None:
212
+ await self.on_stop()
97
213
 
98
- class CallbackAsyncLifecycle(
99
- AnyCallbackLifecycle[LifecycleT, ta.Awaitable[None]],
100
- lang.Final,
101
- ta.Generic[LifecycleT],
102
- ):
103
- pass
214
+ @ta.override
215
+ async def lifecycle_destroy(self) -> None:
216
+ if self.on_destroy is not None:
217
+ await self.on_destroy()
@@ -1,21 +1,33 @@
1
+ import contextlib
1
2
  import types
2
3
  import typing as ta
3
4
 
5
+ from .. import check
4
6
  from .. import dataclasses as dc
5
7
  from .. import lang
8
+ from .base import AsyncLifecycle
6
9
  from .base import Lifecycle
10
+ from .controller import AsyncLifecycleController
7
11
  from .controller import LifecycleController
8
12
  from .states import LifecycleState
9
13
  from .states import LifecycleStates
14
+ from .unwrap import unwrap_async_lifecycle
15
+ from .unwrap import unwrap_lifecycle
10
16
 
11
17
 
12
- LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
18
+ T = ta.TypeVar('T')
19
+
13
20
  ContextManagerT = ta.TypeVar('ContextManagerT', bound=ta.ContextManager)
21
+ AsyncContextManagerT = ta.TypeVar('AsyncContextManagerT', bound=ta.AsyncContextManager)
22
+
23
+ LifecycleT = ta.TypeVar('LifecycleT', bound=Lifecycle)
24
+ AsyncLifecycleT = ta.TypeVar('AsyncLifecycleT', bound=AsyncLifecycle)
14
25
 
15
26
 
16
27
  ##
17
28
 
18
29
 
30
+ @ta.final
19
31
  @dc.dataclass(frozen=True)
20
32
  class ContextManagerLifecycle(Lifecycle, lang.Final, ta.Generic[ContextManagerT]):
21
33
  cm: ContextManagerT
@@ -29,7 +41,25 @@ class ContextManagerLifecycle(Lifecycle, lang.Final, ta.Generic[ContextManagerT]
29
41
  self.cm.__exit__(None, None, None)
30
42
 
31
43
 
32
- class LifecycleContextManager(ta.Generic[LifecycleT]):
44
+ @ta.final
45
+ @dc.dataclass(frozen=True)
46
+ class AsyncContextManagerLifecycle(AsyncLifecycle, lang.Final, ta.Generic[AsyncContextManagerT]):
47
+ cm: AsyncContextManagerT
48
+
49
+ @ta.override
50
+ async def lifecycle_start(self) -> None:
51
+ await self.cm.__aenter__()
52
+
53
+ @ta.override
54
+ async def lifecycle_stop(self) -> None:
55
+ await self.cm.__aexit__(None, None, None)
56
+
57
+
58
+ ##
59
+
60
+
61
+ @ta.final
62
+ class LifecycleContextManager(lang.Final, ta.Generic[LifecycleT]):
33
63
  def __init__(self, lifecycle: LifecycleT) -> None:
34
64
  super().__init__()
35
65
 
@@ -38,6 +68,8 @@ class LifecycleContextManager(ta.Generic[LifecycleT]):
38
68
 
39
69
  __repr__ = lang.attr_ops(lambda o: (o.lifecycle, o.state)).repr
40
70
 
71
+ #
72
+
41
73
  @property
42
74
  def lifecycle(self) -> LifecycleT:
43
75
  return self._lifecycle
@@ -50,14 +82,16 @@ class LifecycleContextManager(ta.Generic[LifecycleT]):
50
82
  def state(self) -> LifecycleState:
51
83
  return self._controller.state
52
84
 
53
- def __enter__(self) -> ta.Self:
85
+ #
86
+
87
+ def __enter__(self) -> LifecycleT:
54
88
  try:
55
89
  self._controller.lifecycle_construct()
56
90
  self._controller.lifecycle_start()
57
91
  except Exception:
58
92
  self._controller.lifecycle_destroy()
59
93
  raise
60
- return self
94
+ return self._lifecycle
61
95
 
62
96
  def __exit__(
63
97
  self,
@@ -74,3 +108,78 @@ class LifecycleContextManager(ta.Generic[LifecycleT]):
74
108
  else:
75
109
  self._controller.lifecycle_destroy()
76
110
  return None
111
+
112
+
113
+ @ta.final
114
+ class AsyncLifecycleContextManager(lang.Final, ta.Generic[AsyncLifecycleT]):
115
+ def __init__(self, lifecycle: AsyncLifecycleT) -> None:
116
+ super().__init__()
117
+
118
+ self._lifecycle = lifecycle
119
+ self._controller = lifecycle if isinstance(lifecycle, AsyncLifecycleController) else AsyncLifecycleController(lifecycle) # noqa
120
+
121
+ __repr__ = lang.attr_ops(lambda o: (o.lifecycle, o.state)).repr
122
+
123
+ #
124
+
125
+ @property
126
+ def lifecycle(self) -> AsyncLifecycleT:
127
+ return self._lifecycle
128
+
129
+ @property
130
+ def controller(self) -> AsyncLifecycleController:
131
+ return self._controller
132
+
133
+ @property
134
+ def state(self) -> LifecycleState:
135
+ return self._controller.state
136
+
137
+ #
138
+
139
+ async def __aenter__(self) -> AsyncLifecycleT:
140
+ try:
141
+ await self._controller.lifecycle_construct()
142
+ await self._controller.lifecycle_start()
143
+ except Exception:
144
+ await self._controller.lifecycle_destroy()
145
+ raise
146
+ return self._lifecycle
147
+
148
+ async def __aexit__(
149
+ self,
150
+ exc_type: type[BaseException] | None,
151
+ exc_val: BaseException | None,
152
+ exc_tb: types.TracebackType | None,
153
+ ) -> bool | None:
154
+ try:
155
+ if self._controller.state is LifecycleStates.STARTED:
156
+ await self._controller.lifecycle_stop()
157
+ except Exception:
158
+ await self._controller.lifecycle_destroy()
159
+ raise
160
+ else:
161
+ await self._controller.lifecycle_destroy()
162
+ return None
163
+
164
+
165
+ ##
166
+
167
+
168
+ def lifecycle_context_manage(obj: T) -> ta.ContextManager[T]:
169
+ @contextlib.contextmanager
170
+ def inner():
171
+ lc = check.not_none(unwrap_lifecycle(obj))
172
+ with LifecycleContextManager(lc):
173
+ yield obj
174
+
175
+ return inner()
176
+
177
+
178
+ def async_lifecycle_context_manage(obj: T) -> ta.AsyncContextManager[T]:
179
+ @contextlib.asynccontextmanager
180
+ async def inner():
181
+ lc = check.not_none(unwrap_async_lifecycle(obj))
182
+ async with AsyncLifecycleContextManager(lc):
183
+ yield obj
184
+
185
+ return inner()