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.

Files changed (100) hide show
  1. omlish/__about__.py +1 -1
  2. omlish/asyncs/__init__.py +9 -0
  3. omlish/asyncs/anyio.py +83 -19
  4. omlish/asyncs/asyncio.py +23 -0
  5. omlish/asyncs/asyncs.py +9 -6
  6. omlish/asyncs/bridge.py +316 -0
  7. omlish/asyncs/trio_asyncio.py +7 -3
  8. omlish/collections/__init__.py +1 -0
  9. omlish/collections/identity.py +7 -0
  10. omlish/configs/strings.py +94 -0
  11. omlish/dataclasses/__init__.py +9 -0
  12. omlish/dataclasses/impl/copy.py +30 -0
  13. omlish/dataclasses/impl/exceptions.py +6 -0
  14. omlish/dataclasses/impl/fields.py +24 -25
  15. omlish/dataclasses/impl/init.py +4 -2
  16. omlish/dataclasses/impl/main.py +2 -0
  17. omlish/dataclasses/utils.py +44 -0
  18. omlish/diag/__init__.py +4 -0
  19. omlish/diag/procfs.py +2 -2
  20. omlish/{testing → diag}/pydevd.py +35 -0
  21. omlish/dispatch/_dispatch2.py +65 -0
  22. omlish/dispatch/_dispatch3.py +104 -0
  23. omlish/docker.py +1 -1
  24. omlish/fnpairs.py +11 -0
  25. omlish/http/asgi.py +2 -1
  26. omlish/http/collections.py +15 -0
  27. omlish/http/consts.py +16 -1
  28. omlish/http/sessions.py +10 -3
  29. omlish/inject/__init__.py +45 -17
  30. omlish/inject/binder.py +185 -5
  31. omlish/inject/bindings.py +3 -36
  32. omlish/inject/eagers.py +2 -8
  33. omlish/inject/elements.py +30 -9
  34. omlish/inject/exceptions.py +1 -1
  35. omlish/inject/impl/elements.py +37 -12
  36. omlish/inject/impl/injector.py +19 -2
  37. omlish/inject/impl/inspect.py +33 -5
  38. omlish/inject/impl/origins.py +75 -0
  39. omlish/inject/impl/{private.py → privates.py} +2 -2
  40. omlish/inject/impl/scopes.py +6 -2
  41. omlish/inject/injector.py +8 -4
  42. omlish/inject/inspect.py +18 -0
  43. omlish/inject/keys.py +8 -14
  44. omlish/inject/listeners.py +26 -0
  45. omlish/inject/managed.py +76 -10
  46. omlish/inject/multis.py +68 -18
  47. omlish/inject/origins.py +27 -0
  48. omlish/inject/overrides.py +5 -4
  49. omlish/inject/{private.py → privates.py} +6 -10
  50. omlish/inject/providers.py +12 -85
  51. omlish/inject/scopes.py +13 -6
  52. omlish/inject/types.py +3 -1
  53. omlish/lang/__init__.py +8 -2
  54. omlish/lang/cached.py +2 -2
  55. omlish/lang/classes/restrict.py +2 -1
  56. omlish/lang/classes/simple.py +18 -8
  57. omlish/lang/contextmanagers.py +12 -3
  58. omlish/lang/descriptors.py +131 -0
  59. omlish/lang/functions.py +8 -28
  60. omlish/lang/iterables.py +20 -1
  61. omlish/lang/typing.py +5 -0
  62. omlish/lifecycles/__init__.py +34 -0
  63. omlish/lifecycles/abstract.py +43 -0
  64. omlish/lifecycles/base.py +51 -0
  65. omlish/lifecycles/contextmanagers.py +74 -0
  66. omlish/lifecycles/controller.py +116 -0
  67. omlish/lifecycles/manager.py +161 -0
  68. omlish/lifecycles/states.py +43 -0
  69. omlish/lifecycles/transitions.py +64 -0
  70. omlish/logs/formatters.py +1 -1
  71. omlish/marshal/__init__.py +4 -0
  72. omlish/marshal/naming.py +4 -0
  73. omlish/marshal/objects.py +1 -0
  74. omlish/marshal/polymorphism.py +4 -4
  75. omlish/reflect.py +134 -19
  76. omlish/secrets/__init__.py +7 -0
  77. omlish/secrets/marshal.py +41 -0
  78. omlish/secrets/passwords.py +120 -0
  79. omlish/secrets/secrets.py +47 -0
  80. omlish/serde/__init__.py +0 -0
  81. omlish/{configs → serde}/dotenv.py +12 -24
  82. omlish/{json.py → serde/json.py} +2 -1
  83. omlish/serde/yaml.py +223 -0
  84. omlish/sql/dbs.py +1 -1
  85. omlish/sql/duckdb.py +136 -0
  86. omlish/sql/sqlean.py +17 -0
  87. omlish/term.py +1 -1
  88. omlish/testing/pytest/__init__.py +3 -2
  89. omlish/testing/pytest/inject/harness.py +3 -3
  90. omlish/testing/pytest/marks.py +4 -7
  91. omlish/testing/pytest/plugins/__init__.py +1 -0
  92. omlish/testing/pytest/plugins/asyncs.py +136 -0
  93. omlish/testing/pytest/plugins/pydevd.py +1 -1
  94. omlish/text/glyphsplit.py +92 -0
  95. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
  96. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/RECORD +100 -72
  97. /omlish/{configs → serde}/props.py +0 -0
  98. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
  99. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +0 -0
  100. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
@@ -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 DuplicateKeyError
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 ..keys import tag
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
- k = as_key(ann)
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 = tag(k, pt)
111
+ k = dc.replace(k, tag=pt)
84
112
 
85
113
  if k in seen:
86
- raise DuplicateKeyError(k)
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 ..private import Expose
18
- from ..private import Private
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
@@ -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
- fn(lang.typed_partial(SeededScopeImpl.Manager, ss=self._ss)),
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 Elements
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
- _impl = lang.proxy_import('.impl.injector', __package__)
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(es: Elements) -> Injector:
41
- return _impl.create_injector(es)
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 .eagers import eager
9
- from .elements import Elements
10
- from .elements import as_elements
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
- from .scopes import singleton
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(es: Elements) -> ta.Generator[Injector, None, None]:
18
- i = create_injector(as_elements(
19
- es,
20
- singleton(contextlib.ExitStack),
21
- eager(contextlib.ExitStack),
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, eq=False)
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, eq=False)
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
- def provided_ty(self) -> rfl.Type | None:
65
- return self.multi_key.ty
65
+
66
+ ##
66
67
 
67
68
 
68
- def bind_map_provider(multi_key: ta.Any) -> Element:
69
- multi_key = as_key(multi_key)
70
- return Binding(multi_key, MapProvider(multi_key))
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
@@ -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
@@ -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(bind(ovr), bind(*a))
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(es: Elements) -> Element:
29
- return Private(check.isinstance(es, Elements))
24
+ def private(*args: Elemental) -> Private:
25
+ return Private(as_elements(*args))