omlish 0.0.0.dev5__py3-none-any.whl → 0.0.0.dev7__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 (163) hide show
  1. omlish/__about__.py +109 -5
  2. omlish/__init__.py +0 -8
  3. omlish/asyncs/__init__.py +9 -9
  4. omlish/asyncs/anyio.py +123 -19
  5. omlish/asyncs/asyncio.py +23 -0
  6. omlish/asyncs/asyncs.py +9 -6
  7. omlish/asyncs/bridge.py +316 -0
  8. omlish/asyncs/trio_asyncio.py +7 -3
  9. omlish/bootstrap.py +737 -0
  10. omlish/check.py +1 -1
  11. omlish/collections/__init__.py +5 -0
  12. omlish/collections/exceptions.py +2 -0
  13. omlish/collections/identity.py +7 -0
  14. omlish/collections/utils.py +38 -9
  15. omlish/configs/strings.py +96 -0
  16. omlish/dataclasses/__init__.py +16 -0
  17. omlish/dataclasses/impl/copy.py +30 -0
  18. omlish/dataclasses/impl/descriptors.py +95 -0
  19. omlish/dataclasses/impl/exceptions.py +6 -0
  20. omlish/dataclasses/impl/fields.py +24 -25
  21. omlish/dataclasses/impl/init.py +4 -2
  22. omlish/dataclasses/impl/main.py +2 -0
  23. omlish/dataclasses/impl/reflect.py +1 -1
  24. omlish/dataclasses/utils.py +67 -0
  25. omlish/{lang/datetimes.py → datetimes.py} +8 -4
  26. omlish/diag/__init__.py +4 -0
  27. omlish/diag/procfs.py +2 -2
  28. omlish/{testing → diag}/pydevd.py +35 -0
  29. omlish/diag/threads.py +131 -48
  30. omlish/dispatch/_dispatch2.py +65 -0
  31. omlish/dispatch/_dispatch3.py +104 -0
  32. omlish/docker.py +16 -1
  33. omlish/fnpairs.py +11 -4
  34. omlish/formats/__init__.py +0 -0
  35. omlish/{configs → formats}/dotenv.py +15 -24
  36. omlish/{json.py → formats/json.py} +2 -1
  37. omlish/formats/yaml.py +223 -0
  38. omlish/graphs/trees.py +1 -1
  39. omlish/http/asgi.py +2 -1
  40. omlish/http/collections.py +15 -0
  41. omlish/http/consts.py +22 -1
  42. omlish/http/sessions.py +10 -3
  43. omlish/inject/__init__.py +49 -17
  44. omlish/inject/binder.py +185 -5
  45. omlish/inject/bindings.py +3 -36
  46. omlish/inject/eagers.py +2 -8
  47. omlish/inject/elements.py +31 -10
  48. omlish/inject/exceptions.py +1 -1
  49. omlish/inject/impl/elements.py +37 -12
  50. omlish/inject/impl/injector.py +72 -25
  51. omlish/inject/impl/inspect.py +33 -5
  52. omlish/inject/impl/origins.py +77 -0
  53. omlish/inject/impl/{private.py → privates.py} +2 -2
  54. omlish/inject/impl/scopes.py +6 -2
  55. omlish/inject/injector.py +8 -4
  56. omlish/inject/inspect.py +18 -0
  57. omlish/inject/keys.py +8 -14
  58. omlish/inject/listeners.py +26 -0
  59. omlish/inject/managed.py +76 -10
  60. omlish/inject/multis.py +68 -18
  61. omlish/inject/origins.py +30 -0
  62. omlish/inject/overrides.py +5 -4
  63. omlish/inject/{private.py → privates.py} +6 -10
  64. omlish/inject/providers.py +12 -85
  65. omlish/inject/scopes.py +13 -6
  66. omlish/inject/types.py +3 -1
  67. omlish/inject/utils.py +18 -0
  68. omlish/iterators.py +69 -2
  69. omlish/lang/__init__.py +24 -9
  70. omlish/lang/cached.py +2 -2
  71. omlish/lang/classes/restrict.py +12 -1
  72. omlish/lang/classes/simple.py +18 -8
  73. omlish/lang/contextmanagers.py +13 -4
  74. omlish/lang/descriptors.py +132 -1
  75. omlish/lang/functions.py +8 -28
  76. omlish/lang/imports.py +67 -0
  77. omlish/lang/iterables.py +60 -1
  78. omlish/lang/maybes.py +3 -0
  79. omlish/lang/objects.py +38 -0
  80. omlish/lang/strings.py +25 -0
  81. omlish/lang/sys.py +9 -0
  82. omlish/lang/typing.py +42 -0
  83. omlish/lifecycles/__init__.py +34 -0
  84. omlish/lifecycles/abstract.py +43 -0
  85. omlish/lifecycles/base.py +51 -0
  86. omlish/lifecycles/contextmanagers.py +74 -0
  87. omlish/lifecycles/controller.py +116 -0
  88. omlish/lifecycles/manager.py +161 -0
  89. omlish/lifecycles/states.py +43 -0
  90. omlish/lifecycles/transitions.py +64 -0
  91. omlish/lite/__init__.py +1 -0
  92. omlish/lite/cached.py +18 -0
  93. omlish/lite/check.py +29 -0
  94. omlish/lite/contextmanagers.py +18 -0
  95. omlish/lite/json.py +30 -0
  96. omlish/lite/logs.py +52 -0
  97. omlish/lite/marshal.py +316 -0
  98. omlish/lite/reflect.py +49 -0
  99. omlish/lite/runtime.py +18 -0
  100. omlish/lite/secrets.py +19 -0
  101. omlish/lite/strings.py +25 -0
  102. omlish/lite/subprocesses.py +112 -0
  103. omlish/logs/configs.py +15 -2
  104. omlish/logs/formatters.py +7 -2
  105. omlish/marshal/__init__.py +32 -0
  106. omlish/marshal/any.py +5 -5
  107. omlish/marshal/base.py +27 -11
  108. omlish/marshal/base64.py +24 -9
  109. omlish/marshal/dataclasses.py +34 -28
  110. omlish/marshal/datetimes.py +74 -18
  111. omlish/marshal/enums.py +14 -8
  112. omlish/marshal/exceptions.py +11 -1
  113. omlish/marshal/factories.py +59 -74
  114. omlish/marshal/forbidden.py +35 -0
  115. omlish/marshal/global_.py +11 -4
  116. omlish/marshal/iterables.py +21 -24
  117. omlish/marshal/mappings.py +23 -26
  118. omlish/marshal/naming.py +4 -0
  119. omlish/marshal/numbers.py +51 -0
  120. omlish/marshal/objects.py +1 -0
  121. omlish/marshal/optionals.py +11 -12
  122. omlish/marshal/polymorphism.py +86 -21
  123. omlish/marshal/primitives.py +4 -5
  124. omlish/marshal/standard.py +13 -8
  125. omlish/marshal/uuids.py +4 -5
  126. omlish/matchfns.py +218 -0
  127. omlish/os.py +64 -0
  128. omlish/reflect/__init__.py +39 -0
  129. omlish/reflect/isinstance.py +38 -0
  130. omlish/reflect/ops.py +84 -0
  131. omlish/reflect/subst.py +110 -0
  132. omlish/reflect/types.py +275 -0
  133. omlish/secrets/__init__.py +23 -0
  134. omlish/secrets/crypto.py +132 -0
  135. omlish/secrets/marshal.py +70 -0
  136. omlish/secrets/openssl.py +207 -0
  137. omlish/secrets/passwords.py +120 -0
  138. omlish/secrets/secrets.py +299 -0
  139. omlish/secrets/subprocesses.py +42 -0
  140. omlish/sql/dbs.py +7 -6
  141. omlish/sql/duckdb.py +136 -0
  142. omlish/sql/exprs.py +12 -0
  143. omlish/sql/secrets.py +10 -0
  144. omlish/sql/sqlean.py +17 -0
  145. omlish/term.py +2 -2
  146. omlish/testing/pytest/__init__.py +3 -2
  147. omlish/testing/pytest/inject/harness.py +3 -3
  148. omlish/testing/pytest/marks.py +4 -7
  149. omlish/testing/pytest/plugins/__init__.py +1 -0
  150. omlish/testing/pytest/plugins/asyncs.py +136 -0
  151. omlish/testing/pytest/plugins/pydevd.py +1 -1
  152. omlish/testing/pytest/plugins/switches.py +54 -19
  153. omlish/text/glyphsplit.py +97 -0
  154. omlish-0.0.0.dev7.dist-info/METADATA +50 -0
  155. omlish-0.0.0.dev7.dist-info/RECORD +268 -0
  156. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/WHEEL +1 -1
  157. omlish/reflect.py +0 -355
  158. omlish-0.0.0.dev5.dist-info/METADATA +0 -34
  159. omlish-0.0.0.dev5.dist-info/RECORD +0 -212
  160. /omlish/{asyncs/futures.py → concurrent.py} +0 -0
  161. /omlish/{configs → formats}/props.py +0 -0
  162. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/LICENSE +0 -0
  163. {omlish-0.0.0.dev5.dist-info → omlish-0.0.0.dev7.dist-info}/top_level.txt +0 -0
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,30 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from .. import dataclasses as dc
5
+ from .. import lang
6
+
7
+
8
+ T = ta.TypeVar('T')
9
+
10
+
11
+ @dc.dataclass(frozen=True)
12
+ @dc.extra_params(cache_hash=True)
13
+ class Origin:
14
+ lst: ta.Sequence[str]
15
+
16
+
17
+ @dc.dataclass(frozen=True)
18
+ @dc.extra_params(cache_hash=True)
19
+ class Origins:
20
+ lst: ta.Sequence[Origin]
21
+
22
+ def __iter__(self) -> ta.Iterator[Origin]:
23
+ yield from self.lst
24
+
25
+
26
+ class HasOrigins(lang.Abstract):
27
+ @property
28
+ @abc.abstractmethod
29
+ def origins(self) -> Origins:
30
+ 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))
@@ -1,111 +1,38 @@
1
- import abc
2
1
  import typing as ta
3
2
 
4
3
  from .. import check
5
4
  from .. import dataclasses as dc
6
5
  from .. import lang
7
- from .. import reflect as rfl
8
- from .elements import Element
9
- from .elements import Elements
10
- from .impl.inspect import signature
11
6
  from .keys import Key
12
- from .keys import as_key
13
7
 
14
8
 
15
9
  class _Missing(lang.NotInstantiable):
16
10
  pass
17
11
 
18
12
 
19
- ##
20
-
21
-
22
13
  class Provider(lang.Abstract):
23
- @abc.abstractmethod
24
- def provided_ty(self) -> rfl.Type | None:
25
- raise NotImplementedError
26
-
27
-
28
- ##
29
-
30
-
31
- def as_provider(o: ta.Any) -> Provider:
32
- check.not_isinstance(o, (Element, Elements))
33
- if isinstance(o, Provider):
34
- return o
35
- if isinstance(o, Key):
36
- return link(o)
37
- if isinstance(o, type):
38
- return ctor(o)
39
- if callable(o):
40
- return fn(o)
41
- return const(o)
42
-
43
-
44
- ##
14
+ pass
45
15
 
46
16
 
47
- @dc.dataclass(frozen=True, eq=False)
17
+ @dc.dataclass(frozen=True)
18
+ @dc.extra_params(cache_hash=True)
48
19
  class FnProvider(Provider):
49
- fn: ta.Any
50
- ty: rfl.Type | None = None
51
-
52
- def provided_ty(self) -> rfl.Type | None:
53
- return self.ty
54
-
55
-
56
- def fn(fn: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
57
- check.not_isinstance(fn, type)
58
- check.callable(fn)
59
- if ty is _Missing:
60
- sig = signature(fn)
61
- ty = check.isinstance(sig.return_annotation, type)
62
- return FnProvider(fn, ty)
63
-
64
-
65
- ##
20
+ fn: ta.Any = dc.xfield(check=callable)
66
21
 
67
22
 
68
- @dc.dataclass(frozen=True, eq=False)
23
+ @dc.dataclass(frozen=True)
24
+ @dc.extra_params(cache_hash=True)
69
25
  class CtorProvider(Provider):
70
- ty: type
26
+ ty: type = dc.xfield(coerce=check.of_isinstance(type))
71
27
 
72
- def provided_ty(self) -> type:
73
- return self.ty
74
28
 
75
-
76
- def ctor(ty: type) -> Provider:
77
- check.isinstance(ty, type)
78
- return CtorProvider(ty)
79
-
80
-
81
- ##
82
-
83
-
84
- @dc.dataclass(frozen=True, eq=False)
29
+ @dc.dataclass(frozen=True)
30
+ @dc.extra_params(cache_hash=True)
85
31
  class ConstProvider(Provider):
86
32
  v: ta.Any
87
- ty: rfl.Type | None = None
88
-
89
- def provided_ty(self) -> rfl.Type | None:
90
- return self.ty
91
-
92
33
 
93
- def const(v: ta.Any, ty: rfl.Type | None = _Missing) -> Provider:
94
- if ty is _Missing:
95
- ty = type(v)
96
- return ConstProvider(v, ty)
97
34
 
98
-
99
- ##
100
-
101
-
102
- @dc.dataclass(frozen=True, eq=False)
35
+ @dc.dataclass(frozen=True)
36
+ @dc.extra_params(cache_hash=True)
103
37
  class LinkProvider(Provider):
104
- k: Key
105
-
106
- def provided_ty(self) -> rfl.Type | None:
107
- return None
108
-
109
-
110
- def link(k: ta.Any) -> Provider:
111
- return LinkProvider(as_key(k))
38
+ k: Key = dc.xfield(coerce=check.of_isinstance(Key))
omlish/inject/scopes.py CHANGED
@@ -7,7 +7,6 @@ from .. import dataclasses as dc
7
7
  from .. import lang
8
8
  from .. import reflect as rfl
9
9
  from .bindings import Binding
10
- from .bindings import as_binding
11
10
  from .elements import Element
12
11
  from .keys import Key
13
12
  from .keys import as_key
@@ -24,6 +23,9 @@ else:
24
23
  ##
25
24
 
26
25
 
26
+ SCOPE_ALIASES: dict[str, Scope] = {}
27
+
28
+
27
29
  @dc.dataclass(frozen=True)
28
30
  @dc.extra_params(cache_hash=True)
29
31
  class ScopeBinding(Element, lang.Final):
@@ -34,22 +36,26 @@ def bind_scope(sc: Scope) -> Element:
34
36
  return ScopeBinding(sc)
35
37
 
36
38
 
37
- def in_(b: ta.Any, sc: Scope) -> 'Binding':
38
- return dc.replace(as_binding(b), scope=check.isinstance(sc, Scope))
39
+ ##
39
40
 
40
41
 
41
42
  class Singleton(Scope, lang.Singleton, lang.Final):
42
43
  pass
43
44
 
44
45
 
45
- def singleton(b: ta.Any) -> 'Binding':
46
- return in_(b, Singleton())
46
+ SCOPE_ALIASES['singleton'] = Singleton()
47
+
48
+
49
+ ##
47
50
 
48
51
 
49
52
  class Thread(Scope, lang.Singleton, lang.Final):
50
53
  pass
51
54
 
52
55
 
56
+ SCOPE_ALIASES['thread'] = Thread()
57
+
58
+
53
59
  ##
54
60
 
55
61
 
@@ -64,7 +70,8 @@ class SeededScope(Scope, lang.Final):
64
70
  raise NotImplementedError
65
71
 
66
72
 
67
- @dc.dataclass(frozen=True, eq=False)
73
+ @dc.dataclass(frozen=True)
74
+ @dc.extra_params(cache_hash=True)
68
75
  class ScopeSeededProvider(Provider):
69
76
  ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
70
77
  key: Key = dc.xfield(coerce=check.of_isinstance(Key))
omlish/inject/types.py CHANGED
@@ -1,7 +1,9 @@
1
+ import collections
2
+
1
3
  from .. import lang
2
4
 
3
5
 
4
- ##
6
+ Tag = collections.namedtuple('Tag', 'tag') # noqa
5
7
 
6
8
 
7
9
  class Scope(lang.Abstract):
omlish/inject/utils.py ADDED
@@ -0,0 +1,18 @@
1
+ import typing as ta
2
+
3
+ from .. import dataclasses as dc
4
+ from .. import lang
5
+ from .impl.origins import HasOriginsImpl
6
+
7
+
8
+ T = ta.TypeVar('T')
9
+
10
+
11
+ @dc.dataclass(frozen=True, eq=False)
12
+ class ConstFn(HasOriginsImpl, lang.Final, ta.Generic[T]):
13
+ """An origin tracking provider function for a constant value. Equivalent to `lambda: v` but transparent."""
14
+
15
+ v: T
16
+
17
+ def __call__(self) -> T:
18
+ return self.v