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
omlish/inject/impl/inspect.py
CHANGED
|
@@ -5,24 +5,29 @@ TODO:
|
|
|
5
5
|
- tag decorator - @inj.tag(x='foo')
|
|
6
6
|
- *unpack optional here*
|
|
7
7
|
"""
|
|
8
|
+
import dataclasses as dc
|
|
8
9
|
import inspect
|
|
9
10
|
import types
|
|
10
11
|
import typing as ta
|
|
11
12
|
import weakref
|
|
12
13
|
|
|
13
14
|
from ... import reflect as rfl
|
|
14
|
-
from ..exceptions import
|
|
15
|
+
from ..exceptions import ConflictingKeyError
|
|
15
16
|
from ..inspect import Kwarg
|
|
16
17
|
from ..inspect import KwargsTarget
|
|
17
18
|
from ..keys import Key
|
|
18
19
|
from ..keys import as_key
|
|
19
|
-
from ..
|
|
20
|
+
from ..types import Tag
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
T = ta.TypeVar('T')
|
|
22
24
|
P = ta.ParamSpec('P')
|
|
23
25
|
R = ta.TypeVar('R')
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
##
|
|
29
|
+
|
|
30
|
+
|
|
26
31
|
_signature_cache: ta.MutableMapping[ta.Any, inspect.Signature] = weakref.WeakKeyDictionary()
|
|
27
32
|
|
|
28
33
|
|
|
@@ -38,9 +43,20 @@ def signature(obj: ta.Any) -> inspect.Signature:
|
|
|
38
43
|
return sig
|
|
39
44
|
|
|
40
45
|
|
|
46
|
+
##
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
_tags: ta.MutableMapping[ta.Any, dict[str, ta.Any]] = weakref.WeakKeyDictionary()
|
|
42
50
|
|
|
43
51
|
|
|
52
|
+
def tag(obj: T, **kwargs: ta.Any) -> T:
|
|
53
|
+
for v in kwargs.values():
|
|
54
|
+
if isinstance(v, Tag):
|
|
55
|
+
raise TypeError(v)
|
|
56
|
+
_tags.setdefault(obj, {}).update(**kwargs)
|
|
57
|
+
return obj
|
|
58
|
+
|
|
59
|
+
|
|
44
60
|
def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]]:
|
|
45
61
|
def inner(obj):
|
|
46
62
|
_tags[obj] = kwargs
|
|
@@ -48,6 +64,9 @@ def tags(**kwargs: ta.Any) -> ta.Callable[[ta.Callable[P, R]], ta.Callable[P, R]
|
|
|
48
64
|
return inner
|
|
49
65
|
|
|
50
66
|
|
|
67
|
+
##
|
|
68
|
+
|
|
69
|
+
|
|
51
70
|
def build_kwargs_target(
|
|
52
71
|
obj: ta.Any,
|
|
53
72
|
*,
|
|
@@ -78,12 +97,21 @@ def build_kwargs_target(
|
|
|
78
97
|
):
|
|
79
98
|
[ann] = [a for a in rf.args if a is not types.NoneType]
|
|
80
99
|
|
|
81
|
-
|
|
100
|
+
rty = rfl.type_(ann)
|
|
101
|
+
|
|
102
|
+
tag = None
|
|
103
|
+
if isinstance(rty, rfl.Annotated):
|
|
104
|
+
for e in rty.md:
|
|
105
|
+
if isinstance(e, Tag):
|
|
106
|
+
tag = e.tag
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
k: Key = Key(rfl.strip_annotations(rty), tag=tag)
|
|
82
110
|
if tags is not None and (pt := tags.get(p.name)) is not None:
|
|
83
|
-
k =
|
|
111
|
+
k = dc.replace(k, tag=pt)
|
|
84
112
|
|
|
85
113
|
if k in seen:
|
|
86
|
-
raise
|
|
114
|
+
raise ConflictingKeyError(k)
|
|
87
115
|
seen.add(k)
|
|
88
116
|
|
|
89
117
|
kws.append(Kwarg(
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import types
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from ... import dataclasses as dc
|
|
6
|
+
from ... import lang
|
|
7
|
+
from ..origins import HasOrigins
|
|
8
|
+
from ..origins import Origin
|
|
9
|
+
from ..origins import Origins
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
HasOriginsT = ta.TypeVar('HasOriginsT', bound=HasOrigins)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ORIGIN_NUM_FRAMES = 8
|
|
19
|
+
ORIGIN_BASE_OFS = 2
|
|
20
|
+
|
|
21
|
+
ORIGIN_IGNORED_PACKAGES = frozenset([
|
|
22
|
+
__package__,
|
|
23
|
+
__package__.rpartition('.')[0],
|
|
24
|
+
|
|
25
|
+
lang.__name__,
|
|
26
|
+
lang.functions.__name__,
|
|
27
|
+
|
|
28
|
+
dc.__name__,
|
|
29
|
+
dc.impl.__name__, # noqa
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_origin_frame(f: types.FrameType) -> bool:
|
|
34
|
+
gl = f.f_globals
|
|
35
|
+
try:
|
|
36
|
+
pkg = gl['__package__']
|
|
37
|
+
except KeyError:
|
|
38
|
+
pass
|
|
39
|
+
else:
|
|
40
|
+
if pkg in ORIGIN_IGNORED_PACKAGES:
|
|
41
|
+
return False
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_origin(ofs: int = 0) -> Origin:
|
|
46
|
+
lst = [] # type: ignore
|
|
47
|
+
cur = sys._getframe(ORIGIN_BASE_OFS + ofs) # noqa
|
|
48
|
+
while len(lst) < ORIGIN_NUM_FRAMES and cur is not None:
|
|
49
|
+
if is_origin_frame(cur):
|
|
50
|
+
lst.append(cur)
|
|
51
|
+
cur = cur.f_back # type: ignore
|
|
52
|
+
return Origin(tuple(str(f) for f in lst))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
ORIGINS_ATTR = '__inject_origins__'
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def set_origins(obj: HasOriginsT, origins: Origins) -> HasOriginsT:
|
|
62
|
+
obj.__dict__[ORIGINS_ATTR] = origins
|
|
63
|
+
return obj
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class HasOriginsImpl(HasOrigins):
|
|
67
|
+
@property
|
|
68
|
+
def origins(self) -> Origins:
|
|
69
|
+
return self.__dict__[ORIGINS_ATTR]
|
|
70
|
+
|
|
71
|
+
def __post_init__(self) -> None:
|
|
72
|
+
dc.maybe_post_init(super())
|
|
73
|
+
if ORIGINS_ATTR in self.__dict__:
|
|
74
|
+
raise AttributeError('Origin already set')
|
|
75
|
+
set_origins(self, Origins((build_origin(),)))
|
|
@@ -14,8 +14,8 @@ from ..eagers import Eager
|
|
|
14
14
|
from ..elements import Element
|
|
15
15
|
from ..injector import Injector
|
|
16
16
|
from ..keys import Key
|
|
17
|
-
from ..
|
|
18
|
-
from ..
|
|
17
|
+
from ..privates import Expose
|
|
18
|
+
from ..privates import Private
|
|
19
19
|
from ..providers import Provider
|
|
20
20
|
from ..scopes import Singleton
|
|
21
21
|
from .elements import ElementCollection
|
omlish/inject/impl/scopes.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- ContextVar ('context')
|
|
4
|
+
"""
|
|
1
5
|
import abc
|
|
2
6
|
import contextlib
|
|
3
7
|
import threading
|
|
@@ -13,8 +17,8 @@ from ..exceptions import ScopeAlreadyOpenError
|
|
|
13
17
|
from ..exceptions import ScopeNotOpenError
|
|
14
18
|
from ..injector import Injector
|
|
15
19
|
from ..keys import Key
|
|
20
|
+
from ..providers import FnProvider
|
|
16
21
|
from ..providers import Provider
|
|
17
|
-
from ..providers import fn
|
|
18
22
|
from ..scopes import ScopeSeededProvider
|
|
19
23
|
from ..scopes import SeededScope
|
|
20
24
|
from ..scopes import Singleton
|
|
@@ -162,7 +166,7 @@ class SeededScopeImpl(ScopeImpl):
|
|
|
162
166
|
return as_elements(
|
|
163
167
|
Binding(
|
|
164
168
|
Key(SeededScope.Manager, tag=self._ss),
|
|
165
|
-
|
|
169
|
+
FnProvider(lang.typed_partial(SeededScopeImpl.Manager, ss=self._ss)),
|
|
166
170
|
scope=Singleton(),
|
|
167
171
|
),
|
|
168
172
|
)
|
omlish/inject/injector.py
CHANGED
|
@@ -2,12 +2,16 @@ import abc
|
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
4
|
from .. import lang
|
|
5
|
-
from .elements import
|
|
5
|
+
from .elements import Elemental
|
|
6
|
+
from .elements import as_elements
|
|
6
7
|
from .inspect import KwargsTarget
|
|
7
8
|
from .keys import Key
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
if ta.TYPE_CHECKING:
|
|
12
|
+
from .impl import injector as _injector
|
|
13
|
+
else:
|
|
14
|
+
_injector = lang.proxy_import('.impl.injector', __package__)
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
T = ta.TypeVar('T')
|
|
@@ -37,5 +41,5 @@ class Injector(lang.Abstract):
|
|
|
37
41
|
return self.provide(target)
|
|
38
42
|
|
|
39
43
|
|
|
40
|
-
def create_injector(
|
|
41
|
-
return
|
|
44
|
+
def create_injector(*args: Elemental) -> Injector:
|
|
45
|
+
return _injector.create_injector(as_elements(*args))
|
omlish/inject/inspect.py
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
|
+
from .. import lang
|
|
3
4
|
from .keys import Key
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
if ta.TYPE_CHECKING:
|
|
8
|
+
from .impl import inspect as _inspect
|
|
9
|
+
else:
|
|
10
|
+
_inspect = lang.proxy_import('.impl.inspect', __package__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
T = ta.TypeVar('T')
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
class Kwarg(ta.NamedTuple):
|
|
7
17
|
name: str
|
|
8
18
|
key: Key
|
|
@@ -12,3 +22,11 @@ class Kwarg(ta.NamedTuple):
|
|
|
12
22
|
class KwargsTarget(ta.NamedTuple):
|
|
13
23
|
obj: ta.Any
|
|
14
24
|
kwargs: ta.Sequence[Kwarg]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def tag(obj: T, **kwargs: ta.Any) -> T:
|
|
28
|
+
return _inspect.tag(obj, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_kwargs_target(obj: ta.Any, **kwargs: ta.Any) -> KwargsTarget:
|
|
32
|
+
return _inspect.build_kwargs_target(obj, **kwargs)
|
omlish/inject/keys.py
CHANGED
|
@@ -4,34 +4,28 @@ import typing as ta
|
|
|
4
4
|
from .. import dataclasses as dc
|
|
5
5
|
from .. import lang
|
|
6
6
|
from .. import reflect as rfl
|
|
7
|
+
from .types import Tag
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
T = ta.TypeVar('T')
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
|
|
15
13
|
@dc.dataclass(frozen=True)
|
|
16
14
|
@dc.extra_params(cache_hash=True)
|
|
17
15
|
class Key(lang.Final, ta.Generic[T]):
|
|
18
16
|
ty: rfl.Type = dc.xfield(coerce=rfl.type_)
|
|
19
|
-
tag: ta.Any = dc.field(default=None, kw_only=True)
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
tag: ta.Any = dc.xfield(
|
|
19
|
+
default=None,
|
|
20
|
+
kw_only=True,
|
|
21
|
+
check=lambda o: not isinstance(o, Tag),
|
|
22
|
+
repr_fn=dc.opt_repr,
|
|
23
|
+
)
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def as_key(o: ta.Any) -> Key:
|
|
26
|
-
if o is inspect.Parameter.empty:
|
|
27
|
+
if o is None or o is inspect.Parameter.empty:
|
|
27
28
|
raise TypeError(o)
|
|
28
29
|
if isinstance(o, Key):
|
|
29
30
|
return o
|
|
30
31
|
return Key(rfl.type_(o))
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def tag(o: ta.Any, t: ta.Any) -> Key:
|
|
37
|
-
return dc.replace(as_key(o), tag=t)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import dataclasses as dc
|
|
4
|
+
from .. import lang
|
|
5
|
+
from .bindings import Binding
|
|
6
|
+
from .elements import Element
|
|
7
|
+
from .injector import Injector
|
|
8
|
+
from .keys import Key
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ProvisionListener: ta.TypeAlias = ta.Callable[[
|
|
12
|
+
Injector,
|
|
13
|
+
Key,
|
|
14
|
+
Binding,
|
|
15
|
+
ta.Callable[[], ta.Any],
|
|
16
|
+
], ta.Callable[[], ta.Any]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dc.dataclass(frozen=True)
|
|
20
|
+
@dc.extra_params(cache_hash=True)
|
|
21
|
+
class ProvisionListenerBinding(Element, lang.Final):
|
|
22
|
+
listener: ProvisionListener
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def bind_provision_listener(l: ProvisionListener) -> Element:
|
|
26
|
+
return ProvisionListenerBinding(l)
|
omlish/inject/managed.py
CHANGED
|
@@ -5,20 +5,86 @@ TODO:
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import typing as ta
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from .
|
|
10
|
-
from .elements import
|
|
8
|
+
from .. import lang
|
|
9
|
+
from .binder import bind
|
|
10
|
+
from .elements import Elemental
|
|
11
|
+
from .impl.inspect import build_kwargs_target
|
|
11
12
|
from .injector import Injector
|
|
12
13
|
from .injector import create_injector
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if ta.TYPE_CHECKING:
|
|
17
|
+
from .. import asyncs as _asyncs
|
|
18
|
+
else:
|
|
19
|
+
_asyncs = lang.proxy_import('..asyncs', __package__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
T = ta.TypeVar('T')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
##
|
|
14
26
|
|
|
15
27
|
|
|
16
28
|
@contextlib.contextmanager
|
|
17
|
-
def create_managed_injector(
|
|
18
|
-
i = create_injector(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
))
|
|
29
|
+
def create_managed_injector(*args: Elemental) -> ta.Generator[Injector, None, None]:
|
|
30
|
+
i = create_injector(
|
|
31
|
+
bind(contextlib.ExitStack, singleton=True, eager=True),
|
|
32
|
+
*args,
|
|
33
|
+
)
|
|
23
34
|
with i[contextlib.ExitStack]:
|
|
24
35
|
yield i
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def make_managed_provider(
|
|
39
|
+
fac: ta.Callable[..., T],
|
|
40
|
+
*fns: ta.Callable[[T], ta.ContextManager[T]],
|
|
41
|
+
) -> ta.Callable[..., T]:
|
|
42
|
+
kt = build_kwargs_target(fac)
|
|
43
|
+
|
|
44
|
+
def _provide(
|
|
45
|
+
i: Injector,
|
|
46
|
+
es: contextlib.ExitStack,
|
|
47
|
+
):
|
|
48
|
+
obj = i.inject(kt)
|
|
49
|
+
if not fns:
|
|
50
|
+
obj = es.enter_context(obj)
|
|
51
|
+
else:
|
|
52
|
+
for fn in fns:
|
|
53
|
+
es.enter_context(fn(obj))
|
|
54
|
+
return obj
|
|
55
|
+
|
|
56
|
+
return _provide
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@contextlib.asynccontextmanager
|
|
63
|
+
async def create_async_managed_injector(*args: Elemental) -> ta.AsyncGenerator[Injector, None]:
|
|
64
|
+
i = await _asyncs.s_to_a(create_injector)(
|
|
65
|
+
bind(contextlib.AsyncExitStack, singleton=True, eager=True),
|
|
66
|
+
*args,
|
|
67
|
+
)
|
|
68
|
+
async with i[contextlib.AsyncExitStack]:
|
|
69
|
+
yield i
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def make_async_managed_provider(
|
|
73
|
+
fac: ta.Callable[..., T],
|
|
74
|
+
*fns: ta.Callable[[T], ta.AsyncContextManager[T]],
|
|
75
|
+
) -> ta.Callable[..., T]:
|
|
76
|
+
kt = build_kwargs_target(fac)
|
|
77
|
+
|
|
78
|
+
def _provide(
|
|
79
|
+
i: Injector,
|
|
80
|
+
aes: contextlib.AsyncExitStack,
|
|
81
|
+
):
|
|
82
|
+
obj = i.inject(kt)
|
|
83
|
+
if not fns:
|
|
84
|
+
obj = _asyncs.a_to_s(aes.enter_async_context)(obj)
|
|
85
|
+
else:
|
|
86
|
+
for fn in fns:
|
|
87
|
+
_asyncs.a_to_s(aes.enter_async_context)(fn(obj))
|
|
88
|
+
return obj
|
|
89
|
+
|
|
90
|
+
return _provide
|
omlish/inject/multis.py
CHANGED
|
@@ -5,16 +5,23 @@ TODO:
|
|
|
5
5
|
import collections.abc
|
|
6
6
|
import typing as ta
|
|
7
7
|
|
|
8
|
+
from .. import check
|
|
8
9
|
from .. import dataclasses as dc
|
|
9
10
|
from .. import lang
|
|
10
11
|
from .. import reflect as rfl
|
|
11
12
|
from .bindings import Binding
|
|
12
13
|
from .elements import Element
|
|
14
|
+
from .elements import ElementGenerator
|
|
13
15
|
from .keys import Key
|
|
14
16
|
from .keys import as_key
|
|
15
17
|
from .providers import Provider
|
|
16
18
|
|
|
17
19
|
|
|
20
|
+
T = ta.TypeVar('T')
|
|
21
|
+
K = ta.TypeVar('K')
|
|
22
|
+
V = ta.TypeVar('V')
|
|
23
|
+
|
|
24
|
+
|
|
18
25
|
##
|
|
19
26
|
|
|
20
27
|
|
|
@@ -26,21 +33,14 @@ def _check_set_multi_key(mk: Key) -> bool:
|
|
|
26
33
|
@dc.extra_params(cache_hash=True)
|
|
27
34
|
class SetBinding(Element, lang.Final):
|
|
28
35
|
multi_key: Key = dc.xfield(check=_check_set_multi_key)
|
|
29
|
-
dst: Key = dc.xfield()
|
|
36
|
+
dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
|
30
37
|
|
|
31
38
|
|
|
32
|
-
@dc.dataclass(frozen=True
|
|
39
|
+
@dc.dataclass(frozen=True)
|
|
40
|
+
@dc.extra_params(cache_hash=True)
|
|
33
41
|
class SetProvider(Provider):
|
|
34
42
|
multi_key: Key = dc.xfield(check=_check_set_multi_key)
|
|
35
43
|
|
|
36
|
-
def provided_ty(self) -> rfl.Type | None:
|
|
37
|
-
return self.multi_key.ty
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def bind_set_provider(multi_key: ta.Any) -> Element:
|
|
41
|
-
multi_key = as_key(multi_key)
|
|
42
|
-
return Binding(multi_key, SetProvider(multi_key))
|
|
43
|
-
|
|
44
44
|
|
|
45
45
|
##
|
|
46
46
|
|
|
@@ -53,18 +53,68 @@ def _check_map_multi_key(mk: Key) -> bool:
|
|
|
53
53
|
@dc.extra_params(cache_hash=True)
|
|
54
54
|
class MapBinding(Element, lang.Final):
|
|
55
55
|
multi_key: Key = dc.xfield(check=_check_map_multi_key)
|
|
56
|
-
map_key: ta.Any = dc.xfield(
|
|
57
|
-
dst: Key = dc.xfield(())
|
|
56
|
+
map_key: ta.Any = dc.xfield()
|
|
57
|
+
dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
@dc.dataclass(frozen=True
|
|
60
|
+
@dc.dataclass(frozen=True)
|
|
61
|
+
@dc.extra_params(cache_hash=True)
|
|
61
62
|
class MapProvider(Provider):
|
|
62
63
|
multi_key: Key = dc.xfield(check=_check_map_multi_key)
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
|
|
66
|
+
##
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
class SetBinder(ElementGenerator, ta.Generic[T]):
|
|
70
|
+
def __init__(self, *, tag: ta.Any = None) -> None:
|
|
71
|
+
super().__init__()
|
|
72
|
+
self._tag: ta.Any = tag
|
|
73
|
+
self._sbs: list[SetBinding] = []
|
|
74
|
+
|
|
75
|
+
@lang.cached_property
|
|
76
|
+
def _multi_key(self) -> Key:
|
|
77
|
+
oty = rfl.type_(rfl.get_orig_class(self))
|
|
78
|
+
ety = check.single(check.isinstance(oty, rfl.Generic).args)
|
|
79
|
+
return Key(ta.AbstractSet[ety], tag=self._tag) # type: ignore
|
|
80
|
+
|
|
81
|
+
@lang.cached_property
|
|
82
|
+
def _set_provider_binding(self) -> Element:
|
|
83
|
+
return Binding(self._multi_key, SetProvider(self._multi_key))
|
|
84
|
+
|
|
85
|
+
def bind(self, *keys: ta.Any) -> ta.Self:
|
|
86
|
+
if not isinstance(self, SetBinder):
|
|
87
|
+
raise TypeError
|
|
88
|
+
self._sbs.extend(SetBinding(self._multi_key, as_key(k)) for k in keys)
|
|
89
|
+
return self # type: ignore
|
|
90
|
+
|
|
91
|
+
def __iter__(self) -> ta.Iterator[Element]:
|
|
92
|
+
yield self._set_provider_binding
|
|
93
|
+
yield from self._sbs
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MapBinder(ElementGenerator, ta.Generic[K, V]):
|
|
97
|
+
def __init__(self, *, tag: ta.Any = None) -> None:
|
|
98
|
+
super().__init__()
|
|
99
|
+
self._tag: ta.Any = tag
|
|
100
|
+
self._mbs: list[MapBinding] = []
|
|
101
|
+
|
|
102
|
+
@lang.cached_property
|
|
103
|
+
def _multi_key(self) -> Key:
|
|
104
|
+
oty = rfl.type_(rfl.get_orig_class(self))
|
|
105
|
+
kty, vty = check.isinstance(oty, rfl.Generic).args
|
|
106
|
+
return Key(ta.Mapping[kty, vty], tag=self._tag) # type: ignore
|
|
107
|
+
|
|
108
|
+
@lang.cached_property
|
|
109
|
+
def _map_provider_binding(self) -> Element:
|
|
110
|
+
return Binding(self._multi_key, MapProvider(self._multi_key))
|
|
111
|
+
|
|
112
|
+
def bind(self, map_key: K, map_value_key: ta.Any) -> ta.Self:
|
|
113
|
+
if not isinstance(self, MapBinder):
|
|
114
|
+
raise TypeError
|
|
115
|
+
self._mbs.append(MapBinding(self._multi_key, map_key, as_key(map_value_key)))
|
|
116
|
+
return self # type: ignore
|
|
117
|
+
|
|
118
|
+
def __iter__(self) -> ta.Iterator[Element]:
|
|
119
|
+
yield self._map_provider_binding
|
|
120
|
+
yield from self._mbs
|
omlish/inject/origins.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .. import dataclasses as dc
|
|
5
|
+
from .. import lang
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dc.dataclass(frozen=True)
|
|
9
|
+
@dc.extra_params(cache_hash=True)
|
|
10
|
+
class Origin:
|
|
11
|
+
lst: ta.Sequence[str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dc.dataclass(frozen=True)
|
|
15
|
+
@dc.extra_params(cache_hash=True)
|
|
16
|
+
class Origins:
|
|
17
|
+
lst: ta.Sequence[Origin]
|
|
18
|
+
|
|
19
|
+
def __iter__(self) -> ta.Iterator[Origin]:
|
|
20
|
+
yield from self.lst
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HasOrigins(lang.Abstract):
|
|
24
|
+
@property
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def origins(self) -> Origins:
|
|
27
|
+
raise NotImplementedError
|
omlish/inject/overrides.py
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
|
+
from .. import check
|
|
3
4
|
from .. import dataclasses as dc
|
|
4
5
|
from .. import lang
|
|
5
|
-
from .binder import bind
|
|
6
6
|
from .elements import Element
|
|
7
7
|
from .elements import Elements
|
|
8
|
+
from .elements import as_elements
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@dc.dataclass(frozen=True)
|
|
11
12
|
@dc.extra_params(cache_hash=True)
|
|
12
13
|
class Overrides(Element, lang.Final):
|
|
13
|
-
ovr: Elements
|
|
14
|
-
src: Elements
|
|
14
|
+
ovr: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
|
15
|
+
src: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def override(ovr: ta.Any, *a: ta.Any) -> Element:
|
|
18
|
-
return Overrides(
|
|
19
|
+
return Overrides(as_elements(ovr), as_elements(*a))
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import typing as ta
|
|
2
|
-
|
|
3
1
|
from .. import check
|
|
4
2
|
from .. import dataclasses as dc
|
|
5
3
|
from .. import lang
|
|
6
4
|
from .elements import Element
|
|
5
|
+
from .elements import Elemental
|
|
7
6
|
from .elements import Elements
|
|
7
|
+
from .elements import as_elements
|
|
8
8
|
from .keys import Key
|
|
9
9
|
from .keys import as_key
|
|
10
10
|
|
|
@@ -12,18 +12,14 @@ from .keys import as_key
|
|
|
12
12
|
@dc.dataclass(frozen=True)
|
|
13
13
|
@dc.extra_params(cache_hash=True)
|
|
14
14
|
class Expose(Element, lang.Final):
|
|
15
|
-
key: Key
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def expose(k: ta.Any) -> Element:
|
|
19
|
-
return Expose(as_key(k))
|
|
15
|
+
key: Key = dc.xfield(coerce=as_key)
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
@dc.dataclass(frozen=True)
|
|
23
19
|
@dc.extra_params(cache_hash=True)
|
|
24
20
|
class Private(Element, lang.Final):
|
|
25
|
-
elements: Elements
|
|
21
|
+
elements: Elements = dc.xfield(coerce=check.of_isinstance(Elements))
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
def private(
|
|
29
|
-
return Private(
|
|
24
|
+
def private(*args: Elemental) -> Private:
|
|
25
|
+
return Private(as_elements(*args))
|