omlish 0.0.0.dev4__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 (143) hide show
  1. omlish/__about__.py +1 -1
  2. omlish/__init__.py +1 -1
  3. omlish/asyncs/__init__.py +10 -4
  4. omlish/asyncs/anyio.py +142 -12
  5. omlish/asyncs/asyncio.py +23 -0
  6. omlish/asyncs/asyncs.py +9 -6
  7. omlish/asyncs/bridge.py +316 -0
  8. omlish/asyncs/flavors.py +27 -1
  9. omlish/asyncs/trio_asyncio.py +28 -18
  10. omlish/c3.py +1 -1
  11. omlish/cached.py +1 -2
  12. omlish/collections/__init__.py +5 -1
  13. omlish/collections/cache/impl.py +1 -1
  14. omlish/collections/identity.py +7 -0
  15. omlish/collections/indexed.py +1 -1
  16. omlish/collections/utils.py +38 -6
  17. omlish/configs/__init__.py +5 -0
  18. omlish/configs/classes.py +53 -0
  19. omlish/configs/strings.py +94 -0
  20. omlish/dataclasses/__init__.py +9 -0
  21. omlish/dataclasses/impl/api.py +1 -1
  22. omlish/dataclasses/impl/as_.py +1 -1
  23. omlish/dataclasses/impl/copy.py +30 -0
  24. omlish/dataclasses/impl/exceptions.py +6 -0
  25. omlish/dataclasses/impl/fields.py +25 -25
  26. omlish/dataclasses/impl/init.py +5 -3
  27. omlish/dataclasses/impl/main.py +3 -0
  28. omlish/dataclasses/impl/metaclass.py +6 -1
  29. omlish/dataclasses/impl/order.py +1 -1
  30. omlish/dataclasses/impl/reflect.py +15 -2
  31. omlish/dataclasses/utils.py +44 -0
  32. omlish/defs.py +1 -1
  33. omlish/diag/__init__.py +4 -0
  34. omlish/diag/procfs.py +31 -3
  35. omlish/diag/procstats.py +32 -0
  36. omlish/{testing → diag}/pydevd.py +35 -0
  37. omlish/diag/replserver/console.py +3 -3
  38. omlish/diag/replserver/server.py +6 -5
  39. omlish/diag/threads.py +86 -0
  40. omlish/dispatch/_dispatch2.py +65 -0
  41. omlish/dispatch/_dispatch3.py +104 -0
  42. omlish/docker.py +20 -1
  43. omlish/fnpairs.py +37 -18
  44. omlish/graphs/dags.py +113 -0
  45. omlish/graphs/domination.py +268 -0
  46. omlish/graphs/trees.py +2 -2
  47. omlish/http/__init__.py +25 -0
  48. omlish/http/asgi.py +132 -0
  49. omlish/http/collections.py +15 -0
  50. omlish/http/consts.py +47 -5
  51. omlish/http/cookies.py +194 -0
  52. omlish/http/dates.py +70 -0
  53. omlish/http/encodings.py +6 -0
  54. omlish/http/json.py +273 -0
  55. omlish/http/sessions.py +204 -0
  56. omlish/inject/__init__.py +51 -17
  57. omlish/inject/binder.py +185 -5
  58. omlish/inject/bindings.py +3 -36
  59. omlish/inject/eagers.py +2 -8
  60. omlish/inject/elements.py +30 -9
  61. omlish/inject/exceptions.py +3 -3
  62. omlish/inject/impl/elements.py +65 -31
  63. omlish/inject/impl/injector.py +20 -2
  64. omlish/inject/impl/inspect.py +33 -5
  65. omlish/inject/impl/multis.py +74 -0
  66. omlish/inject/impl/origins.py +75 -0
  67. omlish/inject/impl/{private.py → privates.py} +2 -2
  68. omlish/inject/impl/providers.py +19 -39
  69. omlish/inject/{proxy.py → impl/proxy.py} +2 -2
  70. omlish/inject/impl/scopes.py +7 -2
  71. omlish/inject/injector.py +9 -4
  72. omlish/inject/inspect.py +18 -0
  73. omlish/inject/keys.py +11 -23
  74. omlish/inject/listeners.py +26 -0
  75. omlish/inject/managed.py +76 -10
  76. omlish/inject/multis.py +120 -0
  77. omlish/inject/origins.py +27 -0
  78. omlish/inject/overrides.py +5 -4
  79. omlish/inject/{private.py → privates.py} +6 -10
  80. omlish/inject/providers.py +12 -85
  81. omlish/inject/scopes.py +20 -9
  82. omlish/inject/types.py +2 -8
  83. omlish/iterators.py +13 -0
  84. omlish/lang/__init__.py +12 -2
  85. omlish/lang/cached.py +2 -2
  86. omlish/lang/classes/restrict.py +3 -2
  87. omlish/lang/classes/simple.py +18 -8
  88. omlish/lang/classes/virtual.py +2 -2
  89. omlish/lang/contextmanagers.py +75 -2
  90. omlish/lang/datetimes.py +6 -5
  91. omlish/lang/descriptors.py +131 -0
  92. omlish/lang/functions.py +18 -28
  93. omlish/lang/imports.py +11 -2
  94. omlish/lang/iterables.py +20 -1
  95. omlish/lang/typing.py +6 -0
  96. omlish/lifecycles/__init__.py +34 -0
  97. omlish/lifecycles/abstract.py +43 -0
  98. omlish/lifecycles/base.py +51 -0
  99. omlish/lifecycles/contextmanagers.py +74 -0
  100. omlish/lifecycles/controller.py +116 -0
  101. omlish/lifecycles/manager.py +161 -0
  102. omlish/lifecycles/states.py +43 -0
  103. omlish/lifecycles/transitions.py +64 -0
  104. omlish/logs/formatters.py +1 -1
  105. omlish/logs/utils.py +1 -1
  106. omlish/marshal/__init__.py +4 -0
  107. omlish/marshal/datetimes.py +1 -1
  108. omlish/marshal/naming.py +4 -0
  109. omlish/marshal/objects.py +1 -0
  110. omlish/marshal/polymorphism.py +4 -4
  111. omlish/reflect.py +139 -18
  112. omlish/secrets/__init__.py +7 -0
  113. omlish/secrets/marshal.py +41 -0
  114. omlish/secrets/passwords.py +120 -0
  115. omlish/secrets/secrets.py +47 -0
  116. omlish/serde/__init__.py +0 -0
  117. omlish/serde/dotenv.py +574 -0
  118. omlish/{json.py → serde/json.py} +4 -2
  119. omlish/serde/props.py +604 -0
  120. omlish/serde/yaml.py +223 -0
  121. omlish/sql/dbs.py +1 -1
  122. omlish/sql/duckdb.py +136 -0
  123. omlish/sql/sqlean.py +17 -0
  124. omlish/sync.py +70 -0
  125. omlish/term.py +7 -2
  126. omlish/testing/pytest/__init__.py +8 -2
  127. omlish/testing/pytest/helpers.py +0 -24
  128. omlish/testing/pytest/inject/harness.py +4 -4
  129. omlish/testing/pytest/marks.py +45 -0
  130. omlish/testing/pytest/plugins/__init__.py +3 -0
  131. omlish/testing/pytest/plugins/asyncs.py +136 -0
  132. omlish/testing/pytest/plugins/managermarks.py +60 -0
  133. omlish/testing/pytest/plugins/pydevd.py +1 -1
  134. omlish/testing/testing.py +10 -0
  135. omlish/text/delimit.py +4 -0
  136. omlish/text/glyphsplit.py +92 -0
  137. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/METADATA +1 -1
  138. omlish-0.0.0.dev6.dist-info/RECORD +240 -0
  139. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/WHEEL +1 -1
  140. omlish/configs/props.py +0 -64
  141. omlish-0.0.0.dev4.dist-info/RECORD +0 -195
  142. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/LICENSE +0 -0
  143. {omlish-0.0.0.dev4.dist-info → omlish-0.0.0.dev6.dist-info}/top_level.txt +0 -0
omlish/inject/keys.py CHANGED
@@ -3,41 +3,29 @@ import typing as ta
3
3
 
4
4
  from .. import dataclasses as dc
5
5
  from .. import lang
6
+ from .. import reflect as rfl
7
+ from .types import Tag
6
8
 
7
9
 
8
10
  T = ta.TypeVar('T')
9
11
 
10
12
 
11
- ##
12
-
13
-
14
13
  @dc.dataclass(frozen=True)
15
14
  @dc.extra_params(cache_hash=True)
16
15
  class Key(lang.Final, ta.Generic[T]):
17
- cls: type[T] | ta.NewType
18
- tag: ta.Any = dc.field(default=None, kw_only=True)
19
- multi: bool = dc.field(default=False, kw_only=True)
20
-
16
+ ty: rfl.Type = dc.xfield(coerce=rfl.type_)
21
17
 
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
- if isinstance(o, (type, ta.NewType)): # noqa
31
- return Key(o)
32
- raise TypeError(o)
33
-
34
-
35
- ##
36
-
37
-
38
- def multi(o: ta.Any) -> Key:
39
- return dc.replace(as_key(o), multi=True)
40
-
41
-
42
- def tag(o: ta.Any, t: ta.Any) -> Key:
43
- return dc.replace(as_key(o), tag=t)
31
+ return Key(rfl.type_(o))
@@ -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
@@ -0,0 +1,120 @@
1
+ """
2
+ TODO:
3
+ - scopes
4
+ """
5
+ import collections.abc
6
+ import typing as ta
7
+
8
+ from .. import check
9
+ from .. import dataclasses as dc
10
+ from .. import lang
11
+ from .. import reflect as rfl
12
+ from .bindings import Binding
13
+ from .elements import Element
14
+ from .elements import ElementGenerator
15
+ from .keys import Key
16
+ from .keys import as_key
17
+ from .providers import Provider
18
+
19
+
20
+ T = ta.TypeVar('T')
21
+ K = ta.TypeVar('K')
22
+ V = ta.TypeVar('V')
23
+
24
+
25
+ ##
26
+
27
+
28
+ def _check_set_multi_key(mk: Key) -> bool:
29
+ return rfl.get_concrete_type(mk.ty) is collections.abc.Set
30
+
31
+
32
+ @dc.dataclass(frozen=True)
33
+ @dc.extra_params(cache_hash=True)
34
+ class SetBinding(Element, lang.Final):
35
+ multi_key: Key = dc.xfield(check=_check_set_multi_key)
36
+ dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
37
+
38
+
39
+ @dc.dataclass(frozen=True)
40
+ @dc.extra_params(cache_hash=True)
41
+ class SetProvider(Provider):
42
+ multi_key: Key = dc.xfield(check=_check_set_multi_key)
43
+
44
+
45
+ ##
46
+
47
+
48
+ def _check_map_multi_key(mk: Key) -> bool:
49
+ return rfl.get_concrete_type(mk.ty) is collections.abc.Mapping
50
+
51
+
52
+ @dc.dataclass(frozen=True)
53
+ @dc.extra_params(cache_hash=True)
54
+ class MapBinding(Element, lang.Final):
55
+ multi_key: Key = dc.xfield(check=_check_map_multi_key)
56
+ map_key: ta.Any = dc.xfield()
57
+ dst: Key = dc.xfield(coerce=check.of_isinstance(Key))
58
+
59
+
60
+ @dc.dataclass(frozen=True)
61
+ @dc.extra_params(cache_hash=True)
62
+ class MapProvider(Provider):
63
+ multi_key: Key = dc.xfield(check=_check_map_multi_key)
64
+
65
+
66
+ ##
67
+
68
+
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))
@@ -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 .elements import Element
8
- from .elements import Elements
9
- from .impl.inspect import signature
10
6
  from .keys import Key
11
- from .keys import as_key
12
- from .types import Cls
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_cls(self) -> Cls | 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
- cls: Cls | None = None
51
-
52
- def provided_cls(self) -> Cls | None:
53
- return self.cls
54
-
55
-
56
- def fn(fn: ta.Any, cls: Cls | None = _Missing) -> Provider:
57
- check.not_isinstance(fn, type)
58
- check.callable(fn)
59
- if cls is _Missing:
60
- sig = signature(fn)
61
- cls = check.isinstance(sig.return_annotation, type)
62
- return FnProvider(fn, cls)
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
- cls: type
26
+ ty: type = dc.xfield(coerce=check.of_isinstance(type))
71
27
 
72
- def provided_cls(self) -> Cls:
73
- return self.cls
74
28
 
75
-
76
- def ctor(cls: type) -> Provider:
77
- check.isinstance(cls, type)
78
- return CtorProvider(cls)
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
- cls: Cls | None = None
88
-
89
- def provided_cls(self) -> Cls | None:
90
- return self.cls
91
-
92
33
 
93
- def const(v: ta.Any, cls: Cls | None = _Missing) -> Provider:
94
- if cls is _Missing:
95
- cls = type(v)
96
- return ConstProvider(v, cls)
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_cls(self) -> Cls | 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
@@ -5,21 +5,27 @@ import typing as ta
5
5
  from .. import check
6
6
  from .. import dataclasses as dc
7
7
  from .. import lang
8
+ from .. import reflect as rfl
8
9
  from .bindings import Binding
9
- from .bindings import as_binding
10
10
  from .elements import Element
11
11
  from .keys import Key
12
12
  from .keys import as_key
13
13
  from .providers import Provider
14
- from .types import Cls
15
14
  from .types import Scope
16
15
 
16
+
17
17
  if ta.TYPE_CHECKING:
18
18
  from . import injector as injector_
19
19
  else:
20
20
  injector_ = lang.proxy_import('.injector', __package__)
21
21
 
22
22
 
23
+ ##
24
+
25
+
26
+ SCOPE_ALIASES: dict[str, Scope] = {}
27
+
28
+
23
29
  @dc.dataclass(frozen=True)
24
30
  @dc.extra_params(cache_hash=True)
25
31
  class ScopeBinding(Element, lang.Final):
@@ -30,22 +36,26 @@ def bind_scope(sc: Scope) -> Element:
30
36
  return ScopeBinding(sc)
31
37
 
32
38
 
33
- def in_(b: ta.Any, sc: Scope) -> 'Binding':
34
- return dc.replace(as_binding(b), scope=check.isinstance(sc, Scope))
39
+ ##
35
40
 
36
41
 
37
42
  class Singleton(Scope, lang.Singleton, lang.Final):
38
43
  pass
39
44
 
40
45
 
41
- def singleton(b: ta.Any) -> 'Binding':
42
- return in_(b, Singleton())
46
+ SCOPE_ALIASES['singleton'] = Singleton()
47
+
48
+
49
+ ##
43
50
 
44
51
 
45
52
  class Thread(Scope, lang.Singleton, lang.Final):
46
53
  pass
47
54
 
48
55
 
56
+ SCOPE_ALIASES['thread'] = Thread()
57
+
58
+
49
59
  ##
50
60
 
51
61
 
@@ -60,13 +70,14 @@ class SeededScope(Scope, lang.Final):
60
70
  raise NotImplementedError
61
71
 
62
72
 
63
- @dc.dataclass(frozen=True, eq=False)
73
+ @dc.dataclass(frozen=True)
74
+ @dc.extra_params(cache_hash=True)
64
75
  class ScopeSeededProvider(Provider):
65
76
  ss: SeededScope = dc.xfield(coerce=check.of_isinstance(SeededScope))
66
77
  key: Key = dc.xfield(coerce=check.of_isinstance(Key))
67
78
 
68
- def provided_cls(self) -> Cls | None:
69
- return self.key.cls
79
+ def provided_ty(self) -> rfl.Type | None:
80
+ return self.key.ty
70
81
 
71
82
 
72
83
  def bind_scope_seed(ss: SeededScope, k: ta.Any) -> Element:
omlish/inject/types.py CHANGED
@@ -1,15 +1,9 @@
1
- import typing as ta
1
+ import collections
2
2
 
3
3
  from .. import lang
4
4
 
5
5
 
6
- ##
7
-
8
-
9
- Cls = type | ta.NewType
10
-
11
-
12
- ##
6
+ Tag = collections.namedtuple('Tag', 'tag') # noqa
13
7
 
14
8
 
15
9
  class Scope(lang.Abstract):
omlish/iterators.py CHANGED
@@ -218,3 +218,16 @@ def expand_indexed_pairs(
218
218
  if idx < width_:
219
219
  result[idx] = value
220
220
  return result
221
+
222
+
223
+ ##
224
+ # https://docs.python.org/3/library/itertools.html#itertools-recipes
225
+
226
+
227
+ def sliding_window(it: ta.Iterable[T], n: int) -> ta.Iterator[tuple[T, ...]]:
228
+ # sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
229
+ iterator = iter(it)
230
+ window = collections.deque(itertools.islice(iterator, n - 1), maxlen=n)
231
+ for x in iterator:
232
+ window.append(x)
233
+ yield tuple(window)
omlish/lang/__init__.py CHANGED
@@ -46,7 +46,9 @@ from .cmp import ( # noqa
46
46
  )
47
47
 
48
48
  from .contextmanagers import ( # noqa
49
+ AsyncContextManager,
49
50
  ContextManaged,
51
+ ContextManager,
50
52
  ContextWrapped,
51
53
  DefaultLockable,
52
54
  ExitStacked,
@@ -78,9 +80,13 @@ from .descriptors import ( # noqa
78
80
  access_forbidden,
79
81
  attr_property,
80
82
  classonly,
83
+ decorator,
81
84
  is_method_descriptor,
82
85
  item_property,
86
+ unwrap_func,
87
+ unwrap_func_with_partials,
83
88
  unwrap_method_descriptors,
89
+ update_wrapper_except_dict,
84
90
  )
85
91
 
86
92
  from .exceptions import ( # noqa
@@ -90,24 +96,26 @@ from .exceptions import ( # noqa
90
96
  from .functions import ( # noqa
91
97
  Args,
92
98
  VoidError,
99
+ as_async,
93
100
  constant,
94
101
  finally_,
95
102
  identity,
96
103
  is_lambda,
97
104
  is_none,
98
105
  is_not_none,
106
+ isinstance_of,
107
+ issubclass_of,
99
108
  maybe_call,
100
109
  periodically,
101
110
  raise_,
102
111
  raising,
103
112
  recurse,
104
113
  try_,
105
- unwrap_func,
106
- unwrap_func_with_partials,
107
114
  void,
108
115
  )
109
116
 
110
117
  from .imports import ( # noqa
118
+ can_import,
111
119
  import_all,
112
120
  import_module,
113
121
  import_module_attr,
@@ -123,8 +131,10 @@ from .iterables import ( # noqa
123
131
  asrange,
124
132
  exhaust,
125
133
  ilen,
134
+ itergen,
126
135
  peek,
127
136
  prodrange,
137
+ renumerate,
128
138
  take,
129
139
  )
130
140
 
omlish/lang/cached.py CHANGED
@@ -11,8 +11,8 @@ import typing as ta
11
11
 
12
12
  from .contextmanagers import DefaultLockable
13
13
  from .contextmanagers import default_lock
14
- from .functions import unwrap_func
15
- from .functions import unwrap_func_with_partials
14
+ from .descriptors import unwrap_func
15
+ from .descriptors import unwrap_func_with_partials
16
16
 
17
17
 
18
18
  P = ta.ParamSpec('P')