omlish 0.0.0.dev484__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/README.md +199 -0
- omlish/__about__.py +8 -3
- omlish/dataclasses/impl/generation/plans.py +2 -17
- omlish/dataclasses/impl/generation/processor.py +2 -2
- omlish/dataclasses/impl/processing/driving.py +13 -1
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/inject/__init__.py +19 -11
- omlish/inject/_dataclasses.py +1485 -1323
- omlish/inject/binder.py +7 -4
- omlish/inject/eagers.py +2 -0
- omlish/inject/helpers/late.py +76 -0
- omlish/inject/{managed.py → helpers/managed.py} +10 -10
- omlish/inject/impl/elements.py +7 -4
- omlish/inject/impl/injector.py +10 -7
- omlish/inject/inspect.py +1 -1
- omlish/lang/__init__.py +2 -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/typing.py +18 -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 +16 -1
- omlish/marshal/_dataclasses.py +781 -781
- omlish/reflect/__init__.py +43 -26
- omlish/reflect/ops.py +10 -1
- omlish/specs/jmespath/_dataclasses.py +559 -559
- omlish/specs/jsonschema/keywords/_dataclasses.py +220 -220
- omlish/sql/__init__.py +24 -5
- 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/testing/pytest/plugins/asyncs/plugin.py +2 -0
- omlish/text/docwrap/cli.py +5 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/METADATA +8 -5
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +61 -51
- omlish/lifecycles/abstract.py +0 -86
- /omlish/inject/{impl → helpers}/proxy.py +0 -0
- /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
omlish/inject/binder.py
CHANGED
|
@@ -88,7 +88,7 @@ def bind(
|
|
|
88
88
|
in_: Scope | None = None,
|
|
89
89
|
singleton: bool = False,
|
|
90
90
|
|
|
91
|
-
eager: bool = False,
|
|
91
|
+
eager: bool | int = False,
|
|
92
92
|
expose: bool = False,
|
|
93
93
|
) -> Element | Elements:
|
|
94
94
|
if obj is None or obj is inspect.Parameter.empty:
|
|
@@ -117,7 +117,10 @@ def bind(
|
|
|
117
117
|
elif _is_fn(obj) and not has_to:
|
|
118
118
|
sig = _inspect.signature(obj)
|
|
119
119
|
ty = rfl.type_(sig.return_annotation)
|
|
120
|
-
|
|
120
|
+
if inspect.iscoroutinefunction(obj):
|
|
121
|
+
to_async_fn = obj
|
|
122
|
+
else:
|
|
123
|
+
to_fn = obj
|
|
121
124
|
key = Key(ty)
|
|
122
125
|
else:
|
|
123
126
|
if to_const is not None:
|
|
@@ -180,8 +183,8 @@ def bind(
|
|
|
180
183
|
|
|
181
184
|
elements: list[Element] = [binding]
|
|
182
185
|
|
|
183
|
-
if eager:
|
|
184
|
-
elements.append(Eager(key))
|
|
186
|
+
if eager is not False:
|
|
187
|
+
elements.append(Eager(key, priority=eager if isinstance(eager, int) else 0))
|
|
185
188
|
if expose:
|
|
186
189
|
elements.append(Expose(key))
|
|
187
190
|
|
omlish/inject/eagers.py
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A minor tidiness for hiding refs to [Async]Injector in circ-dep workarounds. Unfortunately unsuitable outside of
|
|
3
|
+
injection modules due to it itself being in here (as user code shouldn't reference injector guts), but at least it's
|
|
4
|
+
something.
|
|
5
|
+
"""
|
|
6
|
+
import abc
|
|
7
|
+
import functools
|
|
8
|
+
import typing as ta
|
|
9
|
+
|
|
10
|
+
from ... import lang
|
|
11
|
+
from ... import reflect as rfl
|
|
12
|
+
from ..binder import bind
|
|
13
|
+
from ..elements import Elements
|
|
14
|
+
from ..elements import as_elements
|
|
15
|
+
from ..injector import AsyncInjector
|
|
16
|
+
from ..inspect import KwargsTarget
|
|
17
|
+
from ..keys import Key
|
|
18
|
+
from ..keys import as_key
|
|
19
|
+
from ..sync import Injector
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
T = ta.TypeVar('T')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Late(lang.Abstract, ta.Generic[T]):
|
|
29
|
+
@abc.abstractmethod
|
|
30
|
+
def __call__(self) -> T:
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AsyncLate(lang.Abstract, ta.Generic[T]):
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def __call__(self) -> ta.Awaitable[T]:
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _bind_late(
|
|
44
|
+
injector_cls: type,
|
|
45
|
+
late_cls: ta.Any,
|
|
46
|
+
inner: ta.Any,
|
|
47
|
+
outer: ta.Any | None = None,
|
|
48
|
+
) -> Elements:
|
|
49
|
+
inner_key = as_key(inner)
|
|
50
|
+
|
|
51
|
+
outer_key: Key
|
|
52
|
+
outer_fac: ta.Callable[[ta.Any], ta.Any]
|
|
53
|
+
if outer is None:
|
|
54
|
+
inner_ann = rfl.to_annotation(inner_key.ty)
|
|
55
|
+
outer_ann = late_cls[inner_ann]
|
|
56
|
+
outer_key = Key(rfl.type_(outer_ann))
|
|
57
|
+
outer_fac = lang.identity
|
|
58
|
+
else:
|
|
59
|
+
outer_key = as_key(outer)
|
|
60
|
+
outer_cls = rfl.get_concrete_type(outer_key.ty)
|
|
61
|
+
outer_fac = outer_cls # type: ignore[assignment]
|
|
62
|
+
|
|
63
|
+
return as_elements(
|
|
64
|
+
bind(outer_key, to_fn=KwargsTarget.of( # noqa
|
|
65
|
+
lambda i: outer_fac(functools.partial(i.provide, inner_key)),
|
|
66
|
+
i=injector_cls,
|
|
67
|
+
)),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def bind_late(inner: ta.Any, outer: ta.Any | None = None) -> Elements:
|
|
72
|
+
return _bind_late(Injector, Late, inner, outer)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def bind_async_late(inner: ta.Any, outer: ta.Any | None = None) -> Elements:
|
|
76
|
+
return _bind_late(AsyncInjector, AsyncLate, inner, outer)
|
|
@@ -5,20 +5,20 @@ TODO:
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import typing as ta
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
11
|
-
from
|
|
8
|
+
from ... import lang
|
|
9
|
+
from ..binder import bind
|
|
10
|
+
from ..elements import Elemental
|
|
11
|
+
from ..impl.inspect import build_kwargs_target
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
if ta.TYPE_CHECKING:
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
15
|
+
from .. import injector as _injector
|
|
16
|
+
from .. import maysync as _maysync
|
|
17
|
+
from .. import sync as _sync
|
|
18
18
|
else:
|
|
19
|
-
_injector = lang.proxy_import('
|
|
20
|
-
_maysync = lang.proxy_import('
|
|
21
|
-
_sync = lang.proxy_import('
|
|
19
|
+
_injector = lang.proxy_import('..injector', __package__)
|
|
20
|
+
_maysync = lang.proxy_import('..maysync', __package__)
|
|
21
|
+
_sync = lang.proxy_import('..sync', __package__)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
T = ta.TypeVar('T')
|
omlish/inject/impl/elements.py
CHANGED
|
@@ -214,13 +214,16 @@ class ElementCollection(CollectedElements, lang.Final):
|
|
|
214
214
|
return [sb.scope for sb in self.elements_of_type(ScopeBinding)]
|
|
215
215
|
|
|
216
216
|
@lang.cached_function
|
|
217
|
-
def
|
|
217
|
+
def sorted_eager_keys_by_scope(self) -> ta.Mapping[Scope, ta.Sequence[Key]]:
|
|
218
218
|
bim = self.binding_impl_map()
|
|
219
|
-
|
|
219
|
+
dct: dict[Scope, list[Eager]] = {}
|
|
220
220
|
for e in self.elements_of_type(Eager):
|
|
221
221
|
bi = bim[e.key]
|
|
222
|
-
|
|
223
|
-
return
|
|
222
|
+
dct.setdefault(bi.scope, []).append(e)
|
|
223
|
+
return {
|
|
224
|
+
sc: tuple(eg.key for eg in sorted(egs, key=lambda eg: eg.priority))
|
|
225
|
+
for sc, egs in dct.items()
|
|
226
|
+
}
|
|
224
227
|
|
|
225
228
|
|
|
226
229
|
##
|
omlish/inject/impl/injector.py
CHANGED
|
@@ -12,6 +12,9 @@ TODO:
|
|
|
12
12
|
- multiple live request scopes on single injector - use private injectors?
|
|
13
13
|
- more listeners - UnboundKeyListener
|
|
14
14
|
- lazy parent listener chain cache thing
|
|
15
|
+
- https://github.com/7mind/izumi-chibi-ts
|
|
16
|
+
- Axis tagging for conditional bindings (e.g., dev vs prod implementations)
|
|
17
|
+
- Fail-fast validation with circular and missing dependency detection
|
|
15
18
|
"""
|
|
16
19
|
import contextlib
|
|
17
20
|
import functools
|
|
@@ -73,14 +76,14 @@ class AsyncInjectorImpl(AsyncInjector, lang.Final):
|
|
|
73
76
|
|
|
74
77
|
self._bim = ec.binding_impl_map()
|
|
75
78
|
|
|
76
|
-
self._ekbs = ec.
|
|
79
|
+
self._ekbs = ec.sorted_eager_keys_by_scope()
|
|
77
80
|
|
|
78
|
-
self._pls: tuple[ProvisionListener, ...] =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
ec.elements_of_type(ProvisionListenerBinding)
|
|
82
|
-
|
|
83
|
-
)
|
|
81
|
+
self._pls: tuple[ProvisionListener, ...] = (
|
|
82
|
+
*(
|
|
83
|
+
b.listener
|
|
84
|
+
for b in ec.elements_of_type(ProvisionListenerBinding)
|
|
85
|
+
),
|
|
86
|
+
*(p._pls if p is not None else []), # noqa
|
|
84
87
|
)
|
|
85
88
|
|
|
86
89
|
self._root: AsyncInjectorImpl = p._root if p is not None else self # noqa
|
omlish/inject/inspect.py
CHANGED
omlish/lang/__init__.py
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Lifecycle management, inspired by [governator](https://github.com/Netflix/governator/tree/master/governator-core/).
|
|
2
|
+
|
|
3
|
+
A central idea is to 'hide the guts' of a given class's lifecycle management from code interacting with it.
|
|
4
|
+
|
|
5
|
+
The main classes are as follows:
|
|
6
|
+
|
|
7
|
+
- Lifecycle / AsyncLifecycle - the code 'managed by' the rest of the lifecycle machinery. In general, user code will
|
|
8
|
+
hide its implementations of these from the code that otherwise interacts with it. Being the lowest level / unit of
|
|
9
|
+
management callbacks, it intentionally provides no additional machinery, existing solely as a skeleton of methods
|
|
10
|
+
which will be called by lifecycle internals - this is to reduce friction with more functional / less OO code. However,
|
|
11
|
+
it still chooses to be a class with multiple nullary methods, rather than a more functional-style single unary free
|
|
12
|
+
function: the operations present in each method are generally intended to have no overlap in practice, and their
|
|
13
|
+
explicit division is a conscious choice.
|
|
14
|
+
|
|
15
|
+
- LifecycleManaged / AsyncLifecycleManaged - a mixin which can be used to add lifecycle management behavior to a class.
|
|
16
|
+
This removes the need for manual subclassing of Lifecycle / AsyncLifecycle, providing private '\_lifecycle_<state>'
|
|
17
|
+
callback methods (with default no-op implementations) which subclasses may override.
|
|
18
|
+
|
|
19
|
+
- LifecycleListener / AsyncLifecycleListener - callback interfaces whose methods will be called when a lifecycle object
|
|
20
|
+
goes through a lifecycle state transition.
|
|
21
|
+
|
|
22
|
+
- LifecycleController / AsyncLifecycleController - these classes run the state machine for any single Lifecycle /
|
|
23
|
+
AsyncLifecycle instance. They are also responsible for maintaining a registry of lifecycle listeners and calling their
|
|
24
|
+
methods as necessary. Unlike user code, these classes are openly subclasses of Lifecycle / AsyncLifecycle, allowing
|
|
25
|
+
them to be called as application state dictates - they will internally ensure correct state transitions.
|
|
26
|
+
|
|
27
|
+
- LifecycleManager / AsyncLifecycleManager - these classes are responsible for construction and operation of (acyclic)
|
|
28
|
+
graphs of lifecycle objects. They will ensure that, as necessary according to registered dependencies, lifecycle
|
|
29
|
+
objects are started in the correct order, and that they are stopped in the correct order. This class is itself a
|
|
30
|
+
LifecycleManaged / AsyncLifecycleManaged. Notably, AsyncLifecycleManager can also manage sync Lifecycles.
|
omlish/lifecycles/__init__.py
CHANGED
|
@@ -1,43 +1,71 @@
|
|
|
1
|
-
from
|
|
2
|
-
AbstractLifecycle,
|
|
1
|
+
from .. import dataclasses as _dc
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
|
|
4
|
+
_dc.init_package(
|
|
5
|
+
globals(),
|
|
6
|
+
codegen=True,
|
|
5
7
|
)
|
|
6
8
|
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
7
13
|
from .base import ( # noqa
|
|
8
|
-
|
|
14
|
+
Lifecycle,
|
|
15
|
+
AsyncLifecycle,
|
|
9
16
|
AnyLifecycle,
|
|
10
|
-
|
|
17
|
+
ANY_LIFECYCLE_TYPES,
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
sync_to_async_lifecycle,
|
|
20
|
+
as_async_lifecycle,
|
|
21
|
+
async_to_sync_lifecycle,
|
|
22
|
+
as_sync_lifecycle,
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
CallbackLifecycle,
|
|
17
25
|
CallbackAsyncLifecycle,
|
|
18
|
-
AsyncLifecycle,
|
|
19
26
|
)
|
|
20
27
|
|
|
21
28
|
from .contextmanagers import ( # noqa
|
|
22
29
|
ContextManagerLifecycle,
|
|
30
|
+
AsyncContextManagerLifecycle,
|
|
31
|
+
|
|
23
32
|
LifecycleContextManager,
|
|
33
|
+
AsyncLifecycleContextManager,
|
|
34
|
+
|
|
35
|
+
lifecycle_context_manage,
|
|
36
|
+
async_lifecycle_context_manage,
|
|
24
37
|
)
|
|
25
38
|
|
|
26
39
|
from .controller import ( # noqa
|
|
27
|
-
|
|
40
|
+
LifecycleController,
|
|
41
|
+
AsyncLifecycleController,
|
|
28
42
|
AnyLifecycleController,
|
|
43
|
+
ANY_LIFECYCLE_CONTROLLER_TYPES,
|
|
44
|
+
)
|
|
29
45
|
|
|
30
|
-
|
|
46
|
+
from .listeners import ( # noqa
|
|
31
47
|
LifecycleListener,
|
|
48
|
+
AsyncLifecycleListener,
|
|
49
|
+
AnyLifecycleListener,
|
|
50
|
+
ANY_LIFECYCLE_LISTENER_TYPES,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
from .managed import ( # noqa
|
|
54
|
+
LifecycleManaged,
|
|
55
|
+
AsyncLifecycleManaged,
|
|
56
|
+
AnyLifecycleManaged,
|
|
57
|
+
ANY_LIFECYCLE_MANAGED_TYPES,
|
|
32
58
|
)
|
|
33
59
|
|
|
34
60
|
from .manager import ( # noqa
|
|
61
|
+
LifecycleManagerEntry,
|
|
35
62
|
LifecycleManager,
|
|
63
|
+
AsyncLifecycleManager,
|
|
36
64
|
)
|
|
37
65
|
|
|
38
66
|
from .states import ( # noqa
|
|
39
|
-
LifecycleState,
|
|
40
67
|
LifecycleStateError,
|
|
68
|
+
LifecycleState,
|
|
41
69
|
LifecycleStates,
|
|
42
70
|
)
|
|
43
71
|
|
|
@@ -45,3 +73,49 @@ from .transitions import ( # noqa
|
|
|
45
73
|
LifecycleTransition,
|
|
46
74
|
LifecycleTransitions,
|
|
47
75
|
)
|
|
76
|
+
|
|
77
|
+
from .unwrap import ( # noqa
|
|
78
|
+
unwrap_lifecycle,
|
|
79
|
+
unwrap_async_lifecycle,
|
|
80
|
+
unwrap_any_lifecycle,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
from .. import lang as _lang # noqa
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
with _lang.auto_proxy_init(globals()):
|
|
91
|
+
##
|
|
92
|
+
|
|
93
|
+
from .injection import ( # noqa
|
|
94
|
+
bind_lifecycle_registrar,
|
|
95
|
+
bind_async_lifecycle_registrar,
|
|
96
|
+
|
|
97
|
+
bind_managed_lifecycle_manager,
|
|
98
|
+
bind_async_managed_lifecycle_manager,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
NEW = LifecycleStates.NEW
|
|
106
|
+
|
|
107
|
+
CONSTRUCTING = LifecycleStates.CONSTRUCTING
|
|
108
|
+
FAILED_CONSTRUCTING = LifecycleStates.FAILED_CONSTRUCTING
|
|
109
|
+
CONSTRUCTED = LifecycleStates.CONSTRUCTED
|
|
110
|
+
|
|
111
|
+
STARTING = LifecycleStates.STARTING
|
|
112
|
+
FAILED_STARTING = LifecycleStates.FAILED_STARTING
|
|
113
|
+
STARTED = LifecycleStates.STARTED
|
|
114
|
+
|
|
115
|
+
STOPPING = LifecycleStates.STOPPING
|
|
116
|
+
FAILED_STOPPING = LifecycleStates.FAILED_STOPPING
|
|
117
|
+
STOPPED = LifecycleStates.STOPPED
|
|
118
|
+
|
|
119
|
+
DESTROYING = LifecycleStates.DESTROYING
|
|
120
|
+
FAILED_DESTROYING = LifecycleStates.FAILED_DESTROYING
|
|
121
|
+
DESTROYED = LifecycleStates.DESTROYED
|