omlish 0.0.0.dev5__py3-none-any.whl → 0.0.0.dev6__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/__about__.py +1 -1
- omlish/asyncs/__init__.py +9 -0
- omlish/asyncs/anyio.py +83 -19
- omlish/asyncs/asyncio.py +23 -0
- omlish/asyncs/asyncs.py +9 -6
- omlish/asyncs/bridge.py +316 -0
- omlish/asyncs/trio_asyncio.py +7 -3
- omlish/collections/__init__.py +1 -0
- omlish/collections/identity.py +7 -0
- omlish/configs/strings.py +94 -0
- omlish/dataclasses/__init__.py +9 -0
- omlish/dataclasses/impl/copy.py +30 -0
- omlish/dataclasses/impl/exceptions.py +6 -0
- omlish/dataclasses/impl/fields.py +24 -25
- omlish/dataclasses/impl/init.py +4 -2
- omlish/dataclasses/impl/main.py +2 -0
- omlish/dataclasses/utils.py +44 -0
- omlish/diag/__init__.py +4 -0
- omlish/diag/procfs.py +2 -2
- omlish/{testing → diag}/pydevd.py +35 -0
- omlish/dispatch/_dispatch2.py +65 -0
- omlish/dispatch/_dispatch3.py +104 -0
- omlish/docker.py +1 -1
- omlish/fnpairs.py +11 -0
- omlish/http/asgi.py +2 -1
- omlish/http/collections.py +15 -0
- omlish/http/consts.py +16 -1
- omlish/http/sessions.py +10 -3
- omlish/inject/__init__.py +45 -17
- omlish/inject/binder.py +185 -5
- omlish/inject/bindings.py +3 -36
- omlish/inject/eagers.py +2 -8
- omlish/inject/elements.py +30 -9
- omlish/inject/exceptions.py +1 -1
- omlish/inject/impl/elements.py +37 -12
- omlish/inject/impl/injector.py +19 -2
- omlish/inject/impl/inspect.py +33 -5
- omlish/inject/impl/origins.py +75 -0
- omlish/inject/impl/{private.py → privates.py} +2 -2
- omlish/inject/impl/scopes.py +6 -2
- omlish/inject/injector.py +8 -4
- omlish/inject/inspect.py +18 -0
- omlish/inject/keys.py +8 -14
- omlish/inject/listeners.py +26 -0
- omlish/inject/managed.py +76 -10
- omlish/inject/multis.py +68 -18
- omlish/inject/origins.py +27 -0
- omlish/inject/overrides.py +5 -4
- omlish/inject/{private.py → privates.py} +6 -10
- omlish/inject/providers.py +12 -85
- omlish/inject/scopes.py +13 -6
- omlish/inject/types.py +3 -1
- omlish/lang/__init__.py +8 -2
- omlish/lang/cached.py +2 -2
- omlish/lang/classes/restrict.py +2 -1
- omlish/lang/classes/simple.py +18 -8
- omlish/lang/contextmanagers.py +12 -3
- omlish/lang/descriptors.py +131 -0
- omlish/lang/functions.py +8 -28
- omlish/lang/iterables.py +20 -1
- omlish/lang/typing.py +5 -0
- omlish/lifecycles/__init__.py +34 -0
- omlish/lifecycles/abstract.py +43 -0
- omlish/lifecycles/base.py +51 -0
- omlish/lifecycles/contextmanagers.py +74 -0
- omlish/lifecycles/controller.py +116 -0
- omlish/lifecycles/manager.py +161 -0
- omlish/lifecycles/states.py +43 -0
- omlish/lifecycles/transitions.py +64 -0
- omlish/logs/formatters.py +1 -1
- omlish/marshal/__init__.py +4 -0
- omlish/marshal/naming.py +4 -0
- omlish/marshal/objects.py +1 -0
- omlish/marshal/polymorphism.py +4 -4
- omlish/reflect.py +134 -19
- omlish/secrets/__init__.py +7 -0
- omlish/secrets/marshal.py +41 -0
- omlish/secrets/passwords.py +120 -0
- omlish/secrets/secrets.py +47 -0
- omlish/serde/__init__.py +0 -0
- omlish/{configs → serde}/dotenv.py +12 -24
- omlish/{json.py → serde/json.py} +2 -1
- omlish/serde/yaml.py +223 -0
- omlish/sql/dbs.py +1 -1
- omlish/sql/duckdb.py +136 -0
- omlish/sql/sqlean.py +17 -0
- omlish/term.py +1 -1
- omlish/testing/pytest/__init__.py +3 -2
- omlish/testing/pytest/inject/harness.py +3 -3
- omlish/testing/pytest/marks.py +4 -7
- omlish/testing/pytest/plugins/__init__.py +1 -0
- omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish/testing/pytest/plugins/pydevd.py +1 -1
- omlish/text/glyphsplit.py +92 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/RECORD +100 -72
- /omlish/{configs → serde}/props.py +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import dataclasses as dc
|
|
4
|
+
from .. import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
|
|
8
|
+
LifecycleCallback: ta.TypeAlias = ta.Callable[[LifecycleT], None]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Lifecycle:
|
|
12
|
+
|
|
13
|
+
def lifecycle_construct(self) -> None:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def lifecycle_start(self) -> None:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def lifecycle_stop(self) -> None:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def lifecycle_destroy(self) -> None:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
27
|
+
class CallbackLifecycle(Lifecycle, lang.Final, ta.Generic[LifecycleT]):
|
|
28
|
+
on_construct: LifecycleCallback['CallbackLifecycle[LifecycleT]'] | None = None
|
|
29
|
+
on_start: LifecycleCallback['CallbackLifecycle[LifecycleT]'] | None = None
|
|
30
|
+
on_stop: LifecycleCallback['CallbackLifecycle[LifecycleT]'] | None = None
|
|
31
|
+
on_destroy: LifecycleCallback['CallbackLifecycle[LifecycleT]'] | None = None
|
|
32
|
+
|
|
33
|
+
@ta.override
|
|
34
|
+
def lifecycle_construct(self) -> None:
|
|
35
|
+
if self.on_construct is not None:
|
|
36
|
+
self.on_construct(self)
|
|
37
|
+
|
|
38
|
+
@ta.override
|
|
39
|
+
def lifecycle_start(self) -> None:
|
|
40
|
+
if self.on_start is not None:
|
|
41
|
+
self.on_start(self)
|
|
42
|
+
|
|
43
|
+
@ta.override
|
|
44
|
+
def lifecycle_stop(self) -> None:
|
|
45
|
+
if self.on_stop is not None:
|
|
46
|
+
self.on_stop(self)
|
|
47
|
+
|
|
48
|
+
@ta.override
|
|
49
|
+
def lifecycle_destroy(self) -> None:
|
|
50
|
+
if self.on_destroy is not None:
|
|
51
|
+
self.on_destroy(self)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import types
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import defs
|
|
6
|
+
from .. import lang
|
|
7
|
+
from .base import Lifecycle
|
|
8
|
+
from .controller import LifecycleController
|
|
9
|
+
from .states import LifecycleState
|
|
10
|
+
from .states import LifecycleStates
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
|
|
14
|
+
ContextManagerT = ta.TypeVar('ContextManagerT', bound=ta.ContextManager)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dc.dataclass(frozen=True)
|
|
18
|
+
class ContextManagerLifecycle(Lifecycle, lang.Final, ta.Generic[ContextManagerT]):
|
|
19
|
+
cm: ContextManagerT
|
|
20
|
+
|
|
21
|
+
@ta.override
|
|
22
|
+
def lifecycle_start(self) -> None:
|
|
23
|
+
self.cm.__enter__()
|
|
24
|
+
|
|
25
|
+
@ta.override
|
|
26
|
+
def lifecycle_stop(self) -> None:
|
|
27
|
+
self.cm.__exit__(None, None, None)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LifecycleContextManager(ta.Generic[LifecycleT]):
|
|
31
|
+
|
|
32
|
+
def __init__(self, lifecycle: LifecycleT) -> None:
|
|
33
|
+
super().__init__()
|
|
34
|
+
self._lifecycle = lifecycle
|
|
35
|
+
self._controller = lifecycle if isinstance(lifecycle, LifecycleController) else LifecycleController(lifecycle)
|
|
36
|
+
|
|
37
|
+
defs.repr('lifecycle', 'state')
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def lifecycle(self) -> LifecycleT:
|
|
41
|
+
return self._lifecycle
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def controller(self) -> LifecycleController:
|
|
45
|
+
return self._controller
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def state(self) -> LifecycleState:
|
|
49
|
+
return self._controller.state
|
|
50
|
+
|
|
51
|
+
def __enter__(self) -> ta.Self:
|
|
52
|
+
try:
|
|
53
|
+
self._controller.lifecycle_construct()
|
|
54
|
+
self._controller.lifecycle_start()
|
|
55
|
+
except Exception:
|
|
56
|
+
self._controller.lifecycle_destroy()
|
|
57
|
+
raise
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
def __exit__(
|
|
61
|
+
self,
|
|
62
|
+
exc_type: type[BaseException] | None,
|
|
63
|
+
exc_val: BaseException | None,
|
|
64
|
+
exc_tb: types.TracebackType | None,
|
|
65
|
+
) -> bool | None:
|
|
66
|
+
try:
|
|
67
|
+
if self._controller.state is LifecycleStates.STARTED:
|
|
68
|
+
self._controller.lifecycle_stop()
|
|
69
|
+
except Exception:
|
|
70
|
+
self._controller.lifecycle_destroy()
|
|
71
|
+
raise
|
|
72
|
+
else:
|
|
73
|
+
self._controller.lifecycle_destroy()
|
|
74
|
+
return None
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import check
|
|
4
|
+
from .. import defs
|
|
5
|
+
from .. import lang
|
|
6
|
+
from .base import Lifecycle
|
|
7
|
+
from .states import LifecycleState
|
|
8
|
+
from .states import LifecycleStates
|
|
9
|
+
from .transitions import LifecycleTransition
|
|
10
|
+
from .transitions import LifecycleTransitions
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
LifecycleT = ta.TypeVar('LifecycleT', bound='Lifecycle')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LifecycleListener(ta.Generic[LifecycleT]):
|
|
17
|
+
|
|
18
|
+
def on_starting(self, obj: LifecycleT) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def on_started(self, obj: LifecycleT) -> None:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def on_stopping(self, obj: LifecycleT) -> None:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def on_stopped(self, obj: LifecycleT) -> None:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LifecycleController(Lifecycle, ta.Generic[LifecycleT]):
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
lifecycle: LifecycleT,
|
|
36
|
+
*,
|
|
37
|
+
lock: lang.DefaultLockable = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
super().__init__()
|
|
40
|
+
|
|
41
|
+
self._lifecycle: LifecycleT = check.isinstance(lifecycle, Lifecycle) # type: ignore
|
|
42
|
+
self._lock = lang.default_lock(lock, False)
|
|
43
|
+
|
|
44
|
+
self._state = LifecycleStates.NEW
|
|
45
|
+
self._listeners: list[LifecycleListener[LifecycleT]] = []
|
|
46
|
+
|
|
47
|
+
defs.repr('lifecycle', 'state')
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def lifecycle(self) -> LifecycleT:
|
|
51
|
+
return self._lifecycle
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def state(self) -> LifecycleState:
|
|
55
|
+
return self._state
|
|
56
|
+
|
|
57
|
+
def add_listener(self, listener: LifecycleListener[LifecycleT]) -> 'LifecycleController':
|
|
58
|
+
self._listeners.append(check.isinstance(listener, LifecycleListener)) # type: ignore
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def _advance(
|
|
62
|
+
self,
|
|
63
|
+
transition: LifecycleTransition,
|
|
64
|
+
lifecycle_fn: ta.Callable[[], None],
|
|
65
|
+
pre_listener_fn: ta.Callable[[LifecycleListener[LifecycleT]], ta.Callable[[LifecycleT], None]] | None = None, # noqa
|
|
66
|
+
post_listener_fn: ta.Callable[[LifecycleListener[LifecycleT]], ta.Callable[[LifecycleT], None]] | None = None, # noqa
|
|
67
|
+
) -> None:
|
|
68
|
+
with self._lock():
|
|
69
|
+
if pre_listener_fn is not None:
|
|
70
|
+
for listener in self._listeners:
|
|
71
|
+
pre_listener_fn(listener)(self._lifecycle)
|
|
72
|
+
check.state(self._state in transition.old)
|
|
73
|
+
self._state = transition.new_intermediate
|
|
74
|
+
try:
|
|
75
|
+
lifecycle_fn()
|
|
76
|
+
except Exception:
|
|
77
|
+
self._state = transition.new_failed
|
|
78
|
+
raise
|
|
79
|
+
self._state = transition.new_succeeded
|
|
80
|
+
if post_listener_fn is not None:
|
|
81
|
+
for listener in self._listeners:
|
|
82
|
+
post_listener_fn(listener)(self._lifecycle)
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
|
|
86
|
+
@ta.override
|
|
87
|
+
def lifecycle_construct(self) -> None:
|
|
88
|
+
self._advance(
|
|
89
|
+
LifecycleTransitions.CONSTRUCT,
|
|
90
|
+
self._lifecycle.lifecycle_construct,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@ta.override
|
|
94
|
+
def lifecycle_start(self) -> None:
|
|
95
|
+
self._advance(
|
|
96
|
+
LifecycleTransitions.START,
|
|
97
|
+
self._lifecycle.lifecycle_start,
|
|
98
|
+
lambda l: l.on_starting,
|
|
99
|
+
lambda l: l.on_started,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@ta.override
|
|
103
|
+
def lifecycle_stop(self) -> None:
|
|
104
|
+
self._advance(
|
|
105
|
+
LifecycleTransitions.STOP,
|
|
106
|
+
self._lifecycle.lifecycle_stop,
|
|
107
|
+
lambda l: l.on_stopping,
|
|
108
|
+
lambda l: l.on_stopped,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@ta.override
|
|
112
|
+
def lifecycle_destroy(self) -> None:
|
|
113
|
+
self._advance(
|
|
114
|
+
LifecycleTransitions.DESTROY,
|
|
115
|
+
self._lifecycle.lifecycle_destroy,
|
|
116
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import check
|
|
4
|
+
from .. import collections as col
|
|
5
|
+
from .. import dataclasses as dc
|
|
6
|
+
from .. import lang
|
|
7
|
+
from .abstract import AbstractLifecycle
|
|
8
|
+
from .base import Lifecycle
|
|
9
|
+
from .controller import LifecycleController
|
|
10
|
+
from .states import LifecycleState
|
|
11
|
+
from .states import LifecycleStateError
|
|
12
|
+
from .states import LifecycleStates
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LifecycleManager(AbstractLifecycle):
|
|
16
|
+
|
|
17
|
+
@dc.dataclass(frozen=True)
|
|
18
|
+
class Entry(lang.Final):
|
|
19
|
+
controller: LifecycleController
|
|
20
|
+
dependencies: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
|
|
21
|
+
dependents: ta.MutableSet['LifecycleManager.Entry'] = dc.field(default_factory=col.IdentitySet)
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
*,
|
|
26
|
+
lock: lang.DefaultLockable = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
super().__init__()
|
|
29
|
+
|
|
30
|
+
self._lock = lang.default_lock(lock, False)
|
|
31
|
+
|
|
32
|
+
self._entries_by_lifecycle: ta.MutableMapping[Lifecycle, LifecycleManager.Entry] = col.IdentityKeyDict()
|
|
33
|
+
|
|
34
|
+
self._controller = LifecycleController(self._lifecycle, lock=self._lock)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def controller(self) -> LifecycleController:
|
|
38
|
+
return self._controller
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def state(self) -> LifecycleState:
|
|
42
|
+
return self._controller.state
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _get_controller(lifecycle: Lifecycle) -> LifecycleController:
|
|
46
|
+
if isinstance(lifecycle, LifecycleController):
|
|
47
|
+
return lifecycle
|
|
48
|
+
# elif isinstance(lifecycle, AbstractLifecycle):
|
|
49
|
+
# return lifecycle.lifecycle_controller
|
|
50
|
+
elif isinstance(lifecycle, Lifecycle):
|
|
51
|
+
return LifecycleController(lifecycle)
|
|
52
|
+
else:
|
|
53
|
+
raise TypeError(lifecycle)
|
|
54
|
+
|
|
55
|
+
def _add_internal(self, lifecycle: Lifecycle, dependencies: ta.Iterable[Lifecycle]) -> Entry:
|
|
56
|
+
check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
|
|
57
|
+
|
|
58
|
+
check.isinstance(lifecycle, Lifecycle)
|
|
59
|
+
try:
|
|
60
|
+
entry = self._entries_by_lifecycle[lifecycle]
|
|
61
|
+
except KeyError:
|
|
62
|
+
controller = self._get_controller(lifecycle)
|
|
63
|
+
entry = self._entries_by_lifecycle[lifecycle] = LifecycleManager.Entry(controller)
|
|
64
|
+
|
|
65
|
+
for dep in dependencies:
|
|
66
|
+
check.isinstance(dep, Lifecycle)
|
|
67
|
+
dep_entry = self._add_internal(dep, [])
|
|
68
|
+
entry.dependencies.add(dep_entry)
|
|
69
|
+
dep_entry.dependents.add(entry)
|
|
70
|
+
|
|
71
|
+
return entry
|
|
72
|
+
|
|
73
|
+
def add(
|
|
74
|
+
self,
|
|
75
|
+
lifecycle: Lifecycle,
|
|
76
|
+
dependencies: ta.Iterable[Lifecycle] = (),
|
|
77
|
+
) -> Entry:
|
|
78
|
+
check.state(self.state < LifecycleStates.STOPPING and not self.state.is_failed)
|
|
79
|
+
|
|
80
|
+
with self._lock():
|
|
81
|
+
entry = self._add_internal(lifecycle, dependencies)
|
|
82
|
+
|
|
83
|
+
if self.state >= LifecycleStates.CONSTRUCTING:
|
|
84
|
+
def rec(e):
|
|
85
|
+
if e.controller.state < LifecycleStates.CONSTRUCTED:
|
|
86
|
+
for dep in e.dependencies:
|
|
87
|
+
rec(dep)
|
|
88
|
+
e.controller.lifecycle_construct()
|
|
89
|
+
rec(entry)
|
|
90
|
+
|
|
91
|
+
if self.state >= LifecycleStates.STARTING:
|
|
92
|
+
def rec(e):
|
|
93
|
+
if e.controller.state < LifecycleStates.STARTED:
|
|
94
|
+
for dep in e.dependencies:
|
|
95
|
+
rec(dep)
|
|
96
|
+
e.controller.lifecycle_start()
|
|
97
|
+
rec(entry)
|
|
98
|
+
|
|
99
|
+
return entry
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
|
|
103
|
+
@ta.override
|
|
104
|
+
def _lifecycle_construct(self) -> None:
|
|
105
|
+
def rec(entry: LifecycleManager.Entry) -> None:
|
|
106
|
+
for dep in entry.dependencies:
|
|
107
|
+
rec(dep)
|
|
108
|
+
|
|
109
|
+
if entry.controller.state.is_failed:
|
|
110
|
+
raise LifecycleStateError(entry.controller)
|
|
111
|
+
|
|
112
|
+
if entry.controller.state < LifecycleStates.CONSTRUCTED:
|
|
113
|
+
entry.controller.lifecycle_construct()
|
|
114
|
+
|
|
115
|
+
for entry in self._entries_by_lifecycle.values():
|
|
116
|
+
rec(entry)
|
|
117
|
+
|
|
118
|
+
@ta.override
|
|
119
|
+
def _lifecycle_start(self) -> None:
|
|
120
|
+
def rec(entry: LifecycleManager.Entry) -> None:
|
|
121
|
+
for dep in entry.dependencies:
|
|
122
|
+
rec(dep)
|
|
123
|
+
|
|
124
|
+
if entry.controller.state.is_failed:
|
|
125
|
+
raise LifecycleStateError(entry.controller)
|
|
126
|
+
|
|
127
|
+
if entry.controller.state < LifecycleStates.CONSTRUCTED:
|
|
128
|
+
entry.controller.lifecycle_construct()
|
|
129
|
+
|
|
130
|
+
if entry.controller.state < LifecycleStates.STARTED:
|
|
131
|
+
entry.controller.lifecycle_start()
|
|
132
|
+
|
|
133
|
+
for entry in self._entries_by_lifecycle.values():
|
|
134
|
+
rec(entry)
|
|
135
|
+
|
|
136
|
+
@ta.override
|
|
137
|
+
def _lifecycle_stop(self) -> None:
|
|
138
|
+
def rec(entry: LifecycleManager.Entry) -> None:
|
|
139
|
+
for dep in entry.dependents:
|
|
140
|
+
rec(dep)
|
|
141
|
+
|
|
142
|
+
if entry.controller.state.is_failed:
|
|
143
|
+
raise LifecycleStateError(entry.controller)
|
|
144
|
+
|
|
145
|
+
if entry.controller.state is LifecycleStates.STARTED:
|
|
146
|
+
entry.controller.lifecycle_stop()
|
|
147
|
+
|
|
148
|
+
for entry in self._entries_by_lifecycle.values():
|
|
149
|
+
rec(entry)
|
|
150
|
+
|
|
151
|
+
@ta.override
|
|
152
|
+
def _lifecycle_destroy(self) -> None:
|
|
153
|
+
def rec(entry: LifecycleManager.Entry) -> None:
|
|
154
|
+
for dep in entry.dependents:
|
|
155
|
+
rec(dep)
|
|
156
|
+
|
|
157
|
+
if entry.controller.state < LifecycleStates.DESTROYED:
|
|
158
|
+
entry.controller.lifecycle_destroy()
|
|
159
|
+
|
|
160
|
+
for entry in self._entries_by_lifecycle.values():
|
|
161
|
+
rec(entry)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
from .. import check
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import lang
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LifecycleStateError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dc.dataclass(frozen=True, eq=False)
|
|
13
|
+
@functools.total_ordering
|
|
14
|
+
class LifecycleState(lang.Final):
|
|
15
|
+
name: str
|
|
16
|
+
phase: int
|
|
17
|
+
is_failed: bool
|
|
18
|
+
|
|
19
|
+
def __lt__(self, other):
|
|
20
|
+
return self.phase < check.isinstance(other, LifecycleState).phase
|
|
21
|
+
|
|
22
|
+
def __le__(self, other):
|
|
23
|
+
return self.phase <= check.isinstance(other, LifecycleState).phase
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LifecycleStates(lang.Namespace):
|
|
27
|
+
NEW = LifecycleState('NEW', 0, False)
|
|
28
|
+
|
|
29
|
+
CONSTRUCTING = LifecycleState('CONSTRUCTING', 1, False)
|
|
30
|
+
FAILED_CONSTRUCTING = LifecycleState('FAILED_CONSTRUCTING', 2, True)
|
|
31
|
+
CONSTRUCTED = LifecycleState('CONSTRUCTED', 3, False)
|
|
32
|
+
|
|
33
|
+
STARTING = LifecycleState('STARTING', 4, False)
|
|
34
|
+
FAILED_STARTING = LifecycleState('FAILED_STARTING', 5, True)
|
|
35
|
+
STARTED = LifecycleState('STARTED', 6, False)
|
|
36
|
+
|
|
37
|
+
STOPPING = LifecycleState('STOPPING', 7, False)
|
|
38
|
+
FAILED_STOPPING = LifecycleState('FAILED_STOPPING', 8, True)
|
|
39
|
+
STOPPED = LifecycleState('STOPPED', 9, False)
|
|
40
|
+
|
|
41
|
+
DESTROYING = LifecycleState('DESTROYING', 10, False)
|
|
42
|
+
FAILED_DESTROYING = LifecycleState('FAILED_DESTROYING', 11, True)
|
|
43
|
+
DESTROYED = LifecycleState('DESTROYED', 12, False)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from .. import check
|
|
2
|
+
from .. import dataclasses as dc
|
|
3
|
+
from .. import lang
|
|
4
|
+
from .states import LifecycleState
|
|
5
|
+
from .states import LifecycleStates
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dc.dataclass(frozen=True)
|
|
9
|
+
class LifecycleTransition(lang.Final):
|
|
10
|
+
old: frozenset[LifecycleState]
|
|
11
|
+
new_intermediate: LifecycleState
|
|
12
|
+
new_failed: LifecycleState
|
|
13
|
+
new_succeeded: LifecycleState
|
|
14
|
+
|
|
15
|
+
def __post_init__(self) -> None:
|
|
16
|
+
dc.maybe_post_init(super())
|
|
17
|
+
check.unique([*self.old, self.new_intermediate, self.new_succeeded, self.new_failed])
|
|
18
|
+
check.arg(all(self.new_intermediate > o for o in self.old))
|
|
19
|
+
check.arg(self.new_failed > self.new_intermediate)
|
|
20
|
+
check.arg(self.new_succeeded > self.new_failed)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LifecycleTransitions(lang.Namespace):
|
|
24
|
+
CONSTRUCT = LifecycleTransition(
|
|
25
|
+
frozenset([LifecycleStates.NEW]),
|
|
26
|
+
LifecycleStates.CONSTRUCTING,
|
|
27
|
+
LifecycleStates.FAILED_CONSTRUCTING,
|
|
28
|
+
LifecycleStates.CONSTRUCTED,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
START = LifecycleTransition(
|
|
32
|
+
frozenset([LifecycleStates.CONSTRUCTED]),
|
|
33
|
+
LifecycleStates.STARTING,
|
|
34
|
+
LifecycleStates.FAILED_STARTING,
|
|
35
|
+
LifecycleStates.STARTED,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
STOP = LifecycleTransition(
|
|
39
|
+
frozenset([LifecycleStates.STARTED]),
|
|
40
|
+
LifecycleStates.STOPPING,
|
|
41
|
+
LifecycleStates.FAILED_STOPPING,
|
|
42
|
+
LifecycleStates.STOPPED,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
DESTROY = LifecycleTransition(
|
|
46
|
+
frozenset([
|
|
47
|
+
LifecycleStates.NEW,
|
|
48
|
+
|
|
49
|
+
LifecycleStates.CONSTRUCTING,
|
|
50
|
+
LifecycleStates.FAILED_CONSTRUCTING,
|
|
51
|
+
LifecycleStates.CONSTRUCTED,
|
|
52
|
+
|
|
53
|
+
LifecycleStates.STARTING,
|
|
54
|
+
LifecycleStates.FAILED_STARTING,
|
|
55
|
+
LifecycleStates.STARTED,
|
|
56
|
+
|
|
57
|
+
LifecycleStates.STOPPING,
|
|
58
|
+
LifecycleStates.FAILED_STOPPING,
|
|
59
|
+
LifecycleStates.STOPPED,
|
|
60
|
+
]),
|
|
61
|
+
LifecycleStates.DESTROYING,
|
|
62
|
+
LifecycleStates.FAILED_DESTROYING,
|
|
63
|
+
LifecycleStates.DESTROYED,
|
|
64
|
+
)
|
omlish/logs/formatters.py
CHANGED
omlish/marshal/__init__.py
CHANGED
omlish/marshal/naming.py
CHANGED
|
@@ -7,6 +7,7 @@ from .base import Option
|
|
|
7
7
|
class Naming(Option, enum.Enum):
|
|
8
8
|
SNAKE = 'snake'
|
|
9
9
|
CAMEL = 'camel'
|
|
10
|
+
LOW_CAMEL = 'low_camel'
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def translate_name(n: str, e: Naming) -> str:
|
|
@@ -14,4 +15,7 @@ def translate_name(n: str, e: Naming) -> str:
|
|
|
14
15
|
return lang.snake_case(n)
|
|
15
16
|
if e is Naming.CAMEL:
|
|
16
17
|
return lang.camel_case(n)
|
|
18
|
+
if e is Naming.LOW_CAMEL:
|
|
19
|
+
r = lang.camel_case(n)
|
|
20
|
+
return (r[0].lower() + r[1:]) if r else r
|
|
17
21
|
raise ValueError(e)
|
omlish/marshal/objects.py
CHANGED
omlish/marshal/polymorphism.py
CHANGED
|
@@ -97,7 +97,7 @@ def polymorphism_from_subclasses(ty: type, *, naming: Naming | None = None) -> P
|
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
@dc.dataclass(frozen=True)
|
|
100
|
-
class
|
|
100
|
+
class DictKeyPolymorphismMarshaler(Marshaler):
|
|
101
101
|
m: ta.Mapping[type, tuple[str, Marshaler]]
|
|
102
102
|
|
|
103
103
|
def marshal(self, ctx: MarshalContext, o: ta.Any | None) -> Value:
|
|
@@ -111,7 +111,7 @@ class PolymorphismMarshalerFactory(MarshalerFactory):
|
|
|
111
111
|
|
|
112
112
|
def __call__(self, ctx: MarshalContext, rty: rfl.Type) -> Marshaler | None:
|
|
113
113
|
if rty is self.p.ty:
|
|
114
|
-
return
|
|
114
|
+
return DictKeyPolymorphismMarshaler({
|
|
115
115
|
i.ty: (i.tag, ctx.make(i.ty))
|
|
116
116
|
for i in self.p.impls
|
|
117
117
|
})
|
|
@@ -122,7 +122,7 @@ class PolymorphismMarshalerFactory(MarshalerFactory):
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
@dc.dataclass(frozen=True)
|
|
125
|
-
class
|
|
125
|
+
class DictKeyPolymorphismUnmarshaler(Unmarshaler):
|
|
126
126
|
m: ta.Mapping[str, Unmarshaler]
|
|
127
127
|
|
|
128
128
|
def unmarshal(self, ctx: UnmarshalContext, v: Value) -> ta.Any | None:
|
|
@@ -138,7 +138,7 @@ class PolymorphismUnmarshalerFactory(UnmarshalerFactory):
|
|
|
138
138
|
|
|
139
139
|
def __call__(self, ctx: UnmarshalContext, rty: rfl.Type) -> Unmarshaler | None:
|
|
140
140
|
if rty is self.p.ty:
|
|
141
|
-
return
|
|
141
|
+
return DictKeyPolymorphismUnmarshaler({
|
|
142
142
|
t: u
|
|
143
143
|
for i in self.p.impls
|
|
144
144
|
for u in [ctx.make(i.ty)]
|