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.
- omlish/.omlish-manifests.json +12 -0
- omlish/README.md +199 -0
- omlish/__about__.py +21 -16
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/cexts/__init__.py +0 -0
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +1 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/all.py +2 -1
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +2 -0
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +3 -0
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -37
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/plans.py +2 -17
- omlish/dataclasses/impl/generation/processor.py +106 -25
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +19 -7
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/diag/cmds/__init__.py +0 -0
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +1 -1
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +1 -1
- omlish/formats/json/stream/__init__.py +13 -0
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +44 -4
- omlish/http/clients/asyncs.py +33 -27
- omlish/http/clients/base.py +17 -1
- omlish/http/clients/coro/__init__.py +0 -0
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +208 -29
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +72 -11
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +33 -27
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +6 -3
- omlish/http/coro/client/connection.py +15 -6
- omlish/http/coro/io.py +2 -0
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +57 -29
- omlish/inject/_dataclasses.py +5148 -0
- omlish/inject/binder.py +11 -52
- omlish/inject/eagers.py +2 -0
- omlish/inject/elements.py +27 -0
- omlish/inject/helpers/__init__.py +0 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/late.py +76 -0
- omlish/inject/{managed.py → helpers/managed.py} +10 -10
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +54 -21
- omlish/inject/impl/injector.py +29 -25
- omlish/inject/impl/inspect.py +10 -1
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/multis.py +3 -0
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/inspect.py +35 -0
- omlish/inject/maysync.py +2 -4
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +3 -2
- omlish/inject/sync.py +5 -4
- omlish/io/buffers.py +118 -0
- omlish/io/readers.py +29 -0
- omlish/iterators/transforms.py +2 -2
- omlish/lang/__init__.py +180 -97
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +17 -0
- omlish/lang/casing.py +11 -0
- omlish/lang/contextmanagers.py +28 -4
- omlish/lang/functions.py +31 -22
- omlish/lang/imports/_capture.cc +11 -9
- omlish/lang/imports/capture.py +363 -170
- omlish/lang/imports/proxy.py +455 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/sequences.py +124 -0
- omlish/lifecycles/README.md +30 -0
- omlish/lifecycles/__init__.py +87 -13
- omlish/lifecycles/_dataclasses.py +1388 -0
- omlish/lifecycles/base.py +178 -64
- omlish/lifecycles/contextmanagers.py +113 -4
- omlish/lifecycles/controller.py +150 -87
- omlish/lifecycles/injection.py +143 -0
- omlish/lifecycles/listeners.py +56 -0
- omlish/lifecycles/managed.py +142 -0
- omlish/lifecycles/manager.py +218 -93
- omlish/lifecycles/states.py +2 -0
- omlish/lifecycles/transitions.py +3 -0
- omlish/lifecycles/unwrap.py +57 -0
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +2 -2
- omlish/lite/attrops.py +2 -0
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +44 -0
- omlish/lite/maybes.py +8 -0
- omlish/lite/maysync.py +1 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/typing.py +24 -0
- omlish/logs/_amalg.py +1 -1
- omlish/logs/all.py +25 -11
- omlish/logs/asyncs.py +73 -0
- omlish/logs/base.py +101 -12
- omlish/logs/contexts.py +4 -1
- omlish/logs/lists.py +125 -0
- omlish/logs/modules.py +19 -1
- omlish/logs/std/loggers.py +6 -1
- omlish/logs/std/noisy.py +11 -9
- omlish/logs/{standard.py → std/standard.py} +3 -4
- omlish/logs/utils.py +17 -2
- omlish/manifests/loading.py +2 -1
- omlish/marshal/__init__.py +33 -13
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/metadata.py +23 -1
- omlish/os/forkhooks.py +4 -0
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/__init__.py +43 -26
- omlish/reflect/ops.py +10 -1
- omlish/reflect/types.py +1 -0
- omlish/secrets/marshal.py +1 -1
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/__init__.py +24 -5
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/api/dbapi.py +1 -1
- omlish/sql/dbapi/__init__.py +15 -0
- omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
- omlish/sql/queries/__init__.py +3 -0
- omlish/sql/queries/_marshal.py +2 -2
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/base.py +4 -0
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +155 -21
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
- omlish/testing/pytest/plugins/skips.py +2 -1
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +91 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +36 -32
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/lifecycles/abstract.py +0 -86
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/{impl → helpers}/proxy.py +0 -0
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
omlish/lifecycles/manager.py
CHANGED
|
@@ -4,160 +4,285 @@ from .. import check
|
|
|
4
4
|
from .. import collections as col
|
|
5
5
|
from .. import dataclasses as dc
|
|
6
6
|
from .. import lang
|
|
7
|
-
from .
|
|
7
|
+
from .base import AnyLifecycle
|
|
8
|
+
from .base import AsyncLifecycle
|
|
8
9
|
from .base import Lifecycle
|
|
10
|
+
from .base import as_async_lifecycle
|
|
11
|
+
from .controller import AnyLifecycleController
|
|
12
|
+
from .controller import AsyncLifecycleController
|
|
9
13
|
from .controller import LifecycleController
|
|
14
|
+
from .managed import AsyncLifecycleManaged
|
|
15
|
+
from .managed import LifecycleManaged
|
|
10
16
|
from .states import LifecycleState
|
|
11
17
|
from .states import LifecycleStateError
|
|
12
18
|
from .states import LifecycleStates
|
|
13
19
|
|
|
14
20
|
|
|
21
|
+
LifecycleControllerT = ta.TypeVar('LifecycleControllerT', bound=AnyLifecycleController)
|
|
22
|
+
|
|
23
|
+
|
|
15
24
|
##
|
|
16
25
|
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
28
|
+
class LifecycleManagerEntry(lang.Final, ta.Generic[LifecycleControllerT]):
|
|
29
|
+
controller: LifecycleControllerT
|
|
30
|
+
|
|
31
|
+
dependencies: ta.MutableSet['LifecycleManagerEntry'] = dc.field(default_factory=set)
|
|
32
|
+
dependents: ta.MutableSet['LifecycleManagerEntry'] = dc.field(default_factory=set)
|
|
33
|
+
|
|
24
34
|
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@ta.final
|
|
39
|
+
class _InnerLifecycleManager(AsyncLifecycleManaged, lang.Final):
|
|
25
40
|
def __init__(
|
|
26
41
|
self,
|
|
27
|
-
|
|
28
|
-
lock: lang.DefaultLockable = None,
|
|
42
|
+
get_state: ta.Callable[[], LifecycleState],
|
|
29
43
|
) -> None:
|
|
30
44
|
super().__init__()
|
|
31
45
|
|
|
32
|
-
self.
|
|
33
|
-
|
|
34
|
-
self._entries_by_lifecycle: ta.MutableMapping[Lifecycle, LifecycleManager.Entry] = col.IdentityKeyDict()
|
|
35
|
-
|
|
36
|
-
self._controller = LifecycleController(self._lifecycle, lock=self._lock)
|
|
46
|
+
self._get_state = get_state
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
def controller(self) -> LifecycleController:
|
|
40
|
-
return self._controller
|
|
48
|
+
self._entries_by_lifecycle: ta.MutableMapping[AnyLifecycle, LifecycleManagerEntry] = col.IdentityKeyDict()
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
def state(self) -> LifecycleState:
|
|
44
|
-
return self._controller.state
|
|
50
|
+
#
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if isinstance(lifecycle, LifecycleController):
|
|
52
|
+
def _get_or_make_controller(self, lifecycle: AnyLifecycle) -> AnyLifecycleController:
|
|
53
|
+
if isinstance(lifecycle, (LifecycleController, AsyncLifecycleController)):
|
|
49
54
|
return lifecycle
|
|
50
|
-
|
|
51
|
-
# return lifecycle.lifecycle_controller
|
|
55
|
+
|
|
52
56
|
elif isinstance(lifecycle, Lifecycle):
|
|
53
57
|
return LifecycleController(lifecycle)
|
|
58
|
+
|
|
59
|
+
elif isinstance(lifecycle, AsyncLifecycle):
|
|
60
|
+
return AsyncLifecycleController(lifecycle)
|
|
61
|
+
|
|
54
62
|
else:
|
|
55
63
|
raise TypeError(lifecycle)
|
|
56
64
|
|
|
57
|
-
def _add_internal(self, lifecycle:
|
|
58
|
-
check.state(self.
|
|
65
|
+
def _add_internal(self, lifecycle: AnyLifecycle, dependencies: ta.Iterable[AnyLifecycle]) -> LifecycleManagerEntry:
|
|
66
|
+
check.state(self._get_state() < LifecycleStates.STOPPING and not self._get_state().is_failed)
|
|
59
67
|
|
|
60
|
-
check.isinstance(lifecycle, Lifecycle)
|
|
68
|
+
check.isinstance(lifecycle, (Lifecycle, AsyncLifecycle))
|
|
61
69
|
try:
|
|
62
70
|
entry = self._entries_by_lifecycle[lifecycle]
|
|
63
71
|
except KeyError:
|
|
64
|
-
controller = self.
|
|
65
|
-
entry = self._entries_by_lifecycle[lifecycle] =
|
|
72
|
+
controller = self._get_or_make_controller(lifecycle)
|
|
73
|
+
entry = self._entries_by_lifecycle[lifecycle] = LifecycleManagerEntry(controller)
|
|
66
74
|
|
|
67
75
|
for dep in dependencies:
|
|
68
|
-
check.isinstance(dep, Lifecycle)
|
|
76
|
+
check.isinstance(dep, (Lifecycle, AsyncLifecycle))
|
|
69
77
|
dep_entry = self._add_internal(dep, [])
|
|
70
78
|
entry.dependencies.add(dep_entry)
|
|
71
79
|
dep_entry.dependents.add(entry)
|
|
72
80
|
|
|
73
81
|
return entry
|
|
74
82
|
|
|
75
|
-
def add(
|
|
83
|
+
async def add(
|
|
76
84
|
self,
|
|
77
|
-
lifecycle:
|
|
78
|
-
dependencies: ta.Iterable[
|
|
79
|
-
) ->
|
|
80
|
-
check.state(self.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
85
|
+
lifecycle: AnyLifecycle,
|
|
86
|
+
dependencies: ta.Iterable[AnyLifecycle] = (),
|
|
87
|
+
) -> LifecycleManagerEntry:
|
|
88
|
+
check.state(self._get_state() < LifecycleStates.STOPPING and not self._get_state().is_failed)
|
|
89
|
+
|
|
90
|
+
entry = self._add_internal(lifecycle, dependencies)
|
|
91
|
+
|
|
92
|
+
if self._get_state() >= LifecycleStates.CONSTRUCTING:
|
|
93
|
+
async def rec(e):
|
|
94
|
+
if e.controller.state < LifecycleStates.CONSTRUCTED:
|
|
95
|
+
for dep in e.dependencies:
|
|
96
|
+
await rec(dep)
|
|
97
|
+
|
|
98
|
+
await as_async_lifecycle(e.controller).lifecycle_construct()
|
|
99
|
+
|
|
100
|
+
await rec(entry)
|
|
101
|
+
|
|
102
|
+
if self._get_state() >= LifecycleStates.STARTING:
|
|
103
|
+
async def rec(e):
|
|
104
|
+
if e.controller.state < LifecycleStates.STARTED:
|
|
105
|
+
for dep in e.dependencies:
|
|
106
|
+
await rec(dep)
|
|
107
|
+
|
|
108
|
+
await as_async_lifecycle(e.controller).lifecycle_start()
|
|
109
|
+
|
|
110
|
+
await rec(entry)
|
|
111
|
+
|
|
112
|
+
return entry
|
|
102
113
|
|
|
103
114
|
##
|
|
104
115
|
|
|
105
116
|
@ta.override
|
|
106
|
-
def _lifecycle_construct(self) -> None:
|
|
107
|
-
def rec(
|
|
108
|
-
for dep in
|
|
109
|
-
rec(dep)
|
|
117
|
+
async def _lifecycle_construct(self) -> None:
|
|
118
|
+
async def rec(e: LifecycleManagerEntry) -> None:
|
|
119
|
+
for dep in e.dependencies:
|
|
120
|
+
await rec(dep)
|
|
110
121
|
|
|
111
|
-
if
|
|
112
|
-
raise LifecycleStateError(
|
|
122
|
+
if e.controller.state.is_failed:
|
|
123
|
+
raise LifecycleStateError(e.controller)
|
|
113
124
|
|
|
114
|
-
if
|
|
115
|
-
|
|
125
|
+
if e.controller.state < LifecycleStates.CONSTRUCTED:
|
|
126
|
+
await as_async_lifecycle(e.controller).lifecycle_construct()
|
|
116
127
|
|
|
117
128
|
for entry in self._entries_by_lifecycle.values():
|
|
118
|
-
rec(entry)
|
|
129
|
+
await rec(entry)
|
|
119
130
|
|
|
120
131
|
@ta.override
|
|
121
|
-
def _lifecycle_start(self) -> None:
|
|
122
|
-
def rec(
|
|
123
|
-
for dep in
|
|
124
|
-
rec(dep)
|
|
132
|
+
async def _lifecycle_start(self) -> None:
|
|
133
|
+
async def rec(e: LifecycleManagerEntry) -> None:
|
|
134
|
+
for dep in e.dependencies:
|
|
135
|
+
await rec(dep)
|
|
125
136
|
|
|
126
|
-
if
|
|
127
|
-
raise LifecycleStateError(
|
|
137
|
+
if e.controller.state.is_failed:
|
|
138
|
+
raise LifecycleStateError(e.controller)
|
|
128
139
|
|
|
129
|
-
if
|
|
130
|
-
|
|
140
|
+
if e.controller.state < LifecycleStates.CONSTRUCTED:
|
|
141
|
+
await as_async_lifecycle(e.controller).lifecycle_construct()
|
|
131
142
|
|
|
132
|
-
if
|
|
133
|
-
|
|
143
|
+
if e.controller.state < LifecycleStates.STARTED:
|
|
144
|
+
await as_async_lifecycle(e.controller).lifecycle_start()
|
|
134
145
|
|
|
135
146
|
for entry in self._entries_by_lifecycle.values():
|
|
136
|
-
rec(entry)
|
|
147
|
+
await rec(entry)
|
|
137
148
|
|
|
138
149
|
@ta.override
|
|
139
|
-
def _lifecycle_stop(self) -> None:
|
|
140
|
-
def rec(
|
|
141
|
-
for dep in
|
|
142
|
-
rec(dep)
|
|
150
|
+
async def _lifecycle_stop(self) -> None:
|
|
151
|
+
async def rec(e: LifecycleManagerEntry) -> None:
|
|
152
|
+
for dep in e.dependents:
|
|
153
|
+
await rec(dep)
|
|
143
154
|
|
|
144
|
-
if
|
|
145
|
-
raise LifecycleStateError(
|
|
155
|
+
if e.controller.state.is_failed:
|
|
156
|
+
raise LifecycleStateError(e.controller)
|
|
146
157
|
|
|
147
|
-
if
|
|
148
|
-
|
|
158
|
+
if e.controller.state is LifecycleStates.STARTED:
|
|
159
|
+
await as_async_lifecycle(e.controller).lifecycle_stop()
|
|
149
160
|
|
|
150
161
|
for entry in self._entries_by_lifecycle.values():
|
|
151
|
-
rec(entry)
|
|
162
|
+
await rec(entry)
|
|
152
163
|
|
|
153
164
|
@ta.override
|
|
154
|
-
def _lifecycle_destroy(self) -> None:
|
|
155
|
-
def rec(
|
|
156
|
-
for dep in
|
|
157
|
-
rec(dep)
|
|
165
|
+
async def _lifecycle_destroy(self) -> None:
|
|
166
|
+
async def rec(e: LifecycleManagerEntry) -> None:
|
|
167
|
+
for dep in e.dependents:
|
|
168
|
+
await rec(dep)
|
|
158
169
|
|
|
159
|
-
if
|
|
160
|
-
|
|
170
|
+
if e.controller.state < LifecycleStates.DESTROYED:
|
|
171
|
+
await as_async_lifecycle(e.controller).lifecycle_destroy()
|
|
161
172
|
|
|
162
173
|
for entry in self._entries_by_lifecycle.values():
|
|
163
|
-
rec(entry)
|
|
174
|
+
await rec(entry)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
##
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class LifecycleManager(LifecycleManaged, lang.Final):
|
|
181
|
+
def __init__(
|
|
182
|
+
self,
|
|
183
|
+
*,
|
|
184
|
+
lock: lang.DefaultLockable = None,
|
|
185
|
+
) -> None:
|
|
186
|
+
super().__init__()
|
|
187
|
+
|
|
188
|
+
self._lock = lang.default_lock(lock, None)
|
|
189
|
+
|
|
190
|
+
self._inner = _InnerLifecycleManager(lambda: self._inner_controller.state)
|
|
191
|
+
|
|
192
|
+
self._inner_controller = AsyncLifecycleController(
|
|
193
|
+
self._inner._lifecycle, # noqa
|
|
194
|
+
lock=lambda: lang.SyncToAsyncContextManager(self._lock()),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
#
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def state(self) -> LifecycleState:
|
|
201
|
+
return self._inner_controller.state
|
|
202
|
+
|
|
203
|
+
#
|
|
204
|
+
|
|
205
|
+
def add(
|
|
206
|
+
self,
|
|
207
|
+
lifecycle: Lifecycle,
|
|
208
|
+
dependencies: ta.Iterable[Lifecycle] = (),
|
|
209
|
+
) -> LifecycleManagerEntry[LifecycleController]:
|
|
210
|
+
return lang.sync_await(self._inner.add(
|
|
211
|
+
check.isinstance(lifecycle, Lifecycle),
|
|
212
|
+
[check.isinstance(d, Lifecycle) for d in dependencies],
|
|
213
|
+
))
|
|
214
|
+
|
|
215
|
+
#
|
|
216
|
+
|
|
217
|
+
@ta.override
|
|
218
|
+
def _lifecycle_construct(self) -> None:
|
|
219
|
+
return lang.sync_await(self._inner_controller.lifecycle_construct())
|
|
220
|
+
|
|
221
|
+
@ta.override
|
|
222
|
+
def _lifecycle_start(self) -> None:
|
|
223
|
+
return lang.sync_await(self._inner_controller.lifecycle_start())
|
|
224
|
+
|
|
225
|
+
@ta.override
|
|
226
|
+
def _lifecycle_stop(self) -> None:
|
|
227
|
+
return lang.sync_await(self._inner_controller.lifecycle_stop())
|
|
228
|
+
|
|
229
|
+
@ta.override
|
|
230
|
+
def _lifecycle_destroy(self) -> None:
|
|
231
|
+
return lang.sync_await(self._inner_controller.lifecycle_destroy())
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
#
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class AsyncLifecycleManager(AsyncLifecycleManaged, lang.Final):
|
|
238
|
+
def __init__(
|
|
239
|
+
self,
|
|
240
|
+
*,
|
|
241
|
+
lock: lang.DefaultAsyncLockable = None,
|
|
242
|
+
) -> None:
|
|
243
|
+
super().__init__()
|
|
244
|
+
|
|
245
|
+
self._lock = lang.default_async_lock(lock, None)
|
|
246
|
+
|
|
247
|
+
self._inner = _InnerLifecycleManager(lambda: self._inner_controller.state)
|
|
248
|
+
|
|
249
|
+
self._inner_controller = AsyncLifecycleController(
|
|
250
|
+
self._inner._lifecycle, # noqa
|
|
251
|
+
lock=self._lock,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
#
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def state(self) -> LifecycleState:
|
|
258
|
+
return self._inner_controller.state
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
|
|
262
|
+
async def add(
|
|
263
|
+
self,
|
|
264
|
+
lifecycle: AnyLifecycle,
|
|
265
|
+
dependencies: ta.Iterable[AnyLifecycle] = (),
|
|
266
|
+
) -> LifecycleManagerEntry[AnyLifecycleController]:
|
|
267
|
+
return await self._inner.add(
|
|
268
|
+
lifecycle,
|
|
269
|
+
dependencies,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
#
|
|
273
|
+
|
|
274
|
+
@ta.override
|
|
275
|
+
async def _lifecycle_construct(self) -> None:
|
|
276
|
+
return await self._inner_controller.lifecycle_construct()
|
|
277
|
+
|
|
278
|
+
@ta.override
|
|
279
|
+
async def _lifecycle_start(self) -> None:
|
|
280
|
+
return await self._inner_controller.lifecycle_start()
|
|
281
|
+
|
|
282
|
+
@ta.override
|
|
283
|
+
async def _lifecycle_stop(self) -> None:
|
|
284
|
+
return await self._inner_controller.lifecycle_stop()
|
|
285
|
+
|
|
286
|
+
@ta.override
|
|
287
|
+
async def _lifecycle_destroy(self) -> None:
|
|
288
|
+
return await self._inner_controller.lifecycle_destroy()
|
omlish/lifecycles/states.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import typing as ta
|
|
2
3
|
|
|
3
4
|
from .. import check
|
|
4
5
|
from .. import dataclasses as dc
|
|
@@ -12,6 +13,7 @@ class LifecycleStateError(Exception):
|
|
|
12
13
|
pass
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
@ta.final
|
|
15
17
|
@dc.dataclass(frozen=True, eq=False)
|
|
16
18
|
@functools.total_ordering
|
|
17
19
|
class LifecycleState(lang.Final):
|
omlish/lifecycles/transitions.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
1
3
|
from .. import check
|
|
2
4
|
from .. import dataclasses as dc
|
|
3
5
|
from .. import lang
|
|
@@ -8,6 +10,7 @@ from .states import LifecycleStates
|
|
|
8
10
|
##
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
@ta.final
|
|
11
14
|
@dc.dataclass(frozen=True)
|
|
12
15
|
class LifecycleTransition(lang.Final):
|
|
13
16
|
old: frozenset[LifecycleState]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .base import AsyncLifecycle
|
|
5
|
+
from .base import Lifecycle
|
|
6
|
+
from .managed import AsyncLifecycleManaged
|
|
7
|
+
from .managed import LifecycleManaged
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@functools.singledispatch
|
|
14
|
+
def unwrap_lifecycle(obj: ta.Any) -> Lifecycle | None:
|
|
15
|
+
return None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@unwrap_lifecycle.register
|
|
19
|
+
def _(obj: Lifecycle) -> Lifecycle:
|
|
20
|
+
return obj
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@unwrap_lifecycle.register
|
|
24
|
+
def _(obj: LifecycleManaged) -> Lifecycle:
|
|
25
|
+
return obj._lifecycle # noqa
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@functools.singledispatch
|
|
32
|
+
def unwrap_async_lifecycle(obj: ta.Any) -> AsyncLifecycle | None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@unwrap_async_lifecycle.register
|
|
37
|
+
def _(obj: AsyncLifecycle) -> AsyncLifecycle:
|
|
38
|
+
return obj
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@unwrap_async_lifecycle.register
|
|
42
|
+
def _(obj: AsyncLifecycleManaged) -> AsyncLifecycle:
|
|
43
|
+
return obj._lifecycle # noqa
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def unwrap_any_lifecycle(obj: ta.Any) -> Lifecycle | AsyncLifecycle | None:
|
|
50
|
+
if (lc := unwrap_lifecycle(obj)) is not None:
|
|
51
|
+
return lc
|
|
52
|
+
|
|
53
|
+
elif (alc := unwrap_async_lifecycle(obj)) is not None:
|
|
54
|
+
return alc
|
|
55
|
+
|
|
56
|
+
else:
|
|
57
|
+
return None
|
omlish/lite/abstract.py
CHANGED
|
@@ -3,6 +3,9 @@ import abc
|
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
T = ta.TypeVar('T')
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
##
|
|
7
10
|
|
|
8
11
|
|
|
@@ -14,25 +17,49 @@ def is_abstract_method(obj: ta.Any) -> bool:
|
|
|
14
17
|
return bool(getattr(obj, _IS_ABSTRACT_METHOD_ATTR, False))
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def
|
|
20
|
+
def compute_abstract_methods(cls: type) -> ta.FrozenSet[str]:
|
|
21
|
+
# ~> https://github.com/python/cpython/blob/f3476c6507381ca860eec0989f53647b13517423/Modules/_abc.c#L358
|
|
22
|
+
|
|
23
|
+
# Stage 1: direct abstract methods
|
|
24
|
+
|
|
25
|
+
abstracts = {
|
|
26
|
+
a
|
|
27
|
+
# Get items as a list to avoid mutation issues during iteration
|
|
28
|
+
for a, v in list(cls.__dict__.items())
|
|
29
|
+
if is_abstract_method(v)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Stage 2: inherited abstract methods
|
|
33
|
+
|
|
34
|
+
for base in cls.__bases__:
|
|
35
|
+
# Get __abstractmethods__ from base if it exists
|
|
36
|
+
if (base_abstracts := getattr(base, _ABSTRACT_METHODS_ATTR, None)) is None:
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
# Iterate over abstract methods in base
|
|
40
|
+
for key in base_abstracts:
|
|
41
|
+
# Check if this class has an attribute with this name
|
|
42
|
+
try:
|
|
43
|
+
value = getattr(cls, key)
|
|
44
|
+
except AttributeError:
|
|
45
|
+
# Attribute not found in this class, skip
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Check if it's still abstract
|
|
49
|
+
if is_abstract_method(value):
|
|
50
|
+
abstracts.add(key)
|
|
51
|
+
|
|
52
|
+
return frozenset(abstracts)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def update_abstracts(cls: ta.Type[T], *, force: bool = False) -> ta.Type[T]:
|
|
18
56
|
if not force and not hasattr(cls, _ABSTRACT_METHODS_ATTR):
|
|
19
57
|
# Per stdlib: We check for __abstractmethods__ here because cls might by a C implementation or a python
|
|
20
58
|
# implementation (especially during testing), and we want to handle both cases.
|
|
21
59
|
return cls
|
|
22
60
|
|
|
23
|
-
abstracts
|
|
24
|
-
|
|
25
|
-
for scls in cls.__bases__:
|
|
26
|
-
for name in getattr(scls, _ABSTRACT_METHODS_ATTR, ()):
|
|
27
|
-
value = getattr(cls, name, None)
|
|
28
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
29
|
-
abstracts.add(name)
|
|
30
|
-
|
|
31
|
-
for name, value in cls.__dict__.items():
|
|
32
|
-
if getattr(value, _IS_ABSTRACT_METHOD_ATTR, False):
|
|
33
|
-
abstracts.add(name)
|
|
34
|
-
|
|
35
|
-
setattr(cls, _ABSTRACT_METHODS_ATTR, frozenset(abstracts))
|
|
61
|
+
abstracts = compute_abstract_methods(cls)
|
|
62
|
+
setattr(cls, _ABSTRACT_METHODS_ATTR, abstracts)
|
|
36
63
|
return cls
|
|
37
64
|
|
|
38
65
|
|
|
@@ -86,23 +113,26 @@ class Abstract:
|
|
|
86
113
|
super().__init_subclass__(**kwargs)
|
|
87
114
|
|
|
88
115
|
if not (Abstract in cls.__bases__ or abc.ABC in cls.__bases__):
|
|
89
|
-
ams
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if ams := compute_abstract_methods(cls):
|
|
117
|
+
amd = {
|
|
118
|
+
a: mcls
|
|
119
|
+
for mcls in cls.__mro__[::-1]
|
|
120
|
+
for a in ams
|
|
121
|
+
if a in mcls.__dict__
|
|
122
|
+
}
|
|
95
123
|
|
|
96
|
-
if ams:
|
|
97
124
|
raise AbstractTypeError(
|
|
98
125
|
f'Cannot subclass abstract class {cls.__name__} with abstract methods: ' +
|
|
99
126
|
', '.join(sorted([
|
|
100
127
|
'.'.join([
|
|
101
|
-
*([
|
|
102
|
-
|
|
128
|
+
*([
|
|
129
|
+
*([m] if (m := getattr(c, '__module__')) else []),
|
|
130
|
+
getattr(c, '__qualname__', getattr(c, '__name__')),
|
|
131
|
+
] if c is not None else '?'),
|
|
103
132
|
a,
|
|
104
133
|
])
|
|
105
|
-
for a
|
|
134
|
+
for a in ams
|
|
135
|
+
for c in [amd.get(a)]
|
|
106
136
|
])),
|
|
107
137
|
)
|
|
108
138
|
|
omlish/lite/asyncs.py
CHANGED
omlish/lite/attrops.py
CHANGED
|
@@ -6,6 +6,8 @@ TODO:
|
|
|
6
6
|
- per-attr repr transform / filter
|
|
7
7
|
- __ne__ ? cases where it still matters
|
|
8
8
|
- ordering ?
|
|
9
|
+
- repr_filter: ta.Union[ta.Callable[[ta.Any], ta.Optional[str]], ta.Literal['not_none', 'truthy']]] ?
|
|
10
|
+
- unify repr/repr_fn/repr_filter
|
|
9
11
|
"""
|
|
10
12
|
import types # noqa
|
|
11
13
|
import typing as ta
|
omlish/lite/contextmanagers.py
CHANGED
|
@@ -54,7 +54,7 @@ class ExitStacked:
|
|
|
54
54
|
es.__enter__()
|
|
55
55
|
try:
|
|
56
56
|
self._enter_contexts()
|
|
57
|
-
except
|
|
57
|
+
except BaseException: # noqa
|
|
58
58
|
es.__exit__(*sys.exc_info())
|
|
59
59
|
raise
|
|
60
60
|
return self
|
|
@@ -65,7 +65,7 @@ class ExitStacked:
|
|
|
65
65
|
return None
|
|
66
66
|
try:
|
|
67
67
|
self._exit_contexts()
|
|
68
|
-
except
|
|
68
|
+
except BaseException: # noqa
|
|
69
69
|
es.__exit__(*sys.exc_info())
|
|
70
70
|
raise
|
|
71
71
|
return es.__exit__(exc_type, exc_val, exc_tb)
|
|
@@ -113,7 +113,7 @@ class AsyncExitStacked:
|
|
|
113
113
|
await es.__aenter__()
|
|
114
114
|
try:
|
|
115
115
|
await self._async_enter_contexts()
|
|
116
|
-
except
|
|
116
|
+
except BaseException: # noqa
|
|
117
117
|
await es.__aexit__(*sys.exc_info())
|
|
118
118
|
raise
|
|
119
119
|
return self
|
|
@@ -124,7 +124,7 @@ class AsyncExitStacked:
|
|
|
124
124
|
return None
|
|
125
125
|
try:
|
|
126
126
|
await self._async_exit_contexts()
|
|
127
|
-
except
|
|
127
|
+
except BaseException: # noqa
|
|
128
128
|
await es.__aexit__(*sys.exc_info())
|
|
129
129
|
raise
|
|
130
130
|
return await es.__aexit__(exc_type, exc_val, exc_tb)
|