python-injection 0.25.15__py3-none-any.whl → 0.26.0__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.
- injection/__init__.pyi +2 -2
- injection/_core/injectables.py +11 -9
- injection/_core/module.py +2 -3
- injection/_core/scope.py +13 -11
- {python_injection-0.25.15.dist-info → python_injection-0.26.0.dist-info}/METADATA +1 -1
- {python_injection-0.25.15.dist-info → python_injection-0.26.0.dist-info}/RECORD +8 -8
- {python_injection-0.25.15.dist-info → python_injection-0.26.0.dist-info}/WHEEL +1 -1
- {python_injection-0.25.15.dist-info → python_injection-0.26.0.dist-info}/licenses/LICENSE +0 -0
injection/__init__.pyi
CHANGED
|
@@ -68,6 +68,7 @@ def mod(name: str = ..., /) -> Module:
|
|
|
68
68
|
"""
|
|
69
69
|
Short syntax for `Module.from_name`.
|
|
70
70
|
"""
|
|
71
|
+
|
|
71
72
|
@runtime_checkable
|
|
72
73
|
class Injectable[T](Protocol):
|
|
73
74
|
@property
|
|
@@ -234,9 +235,8 @@ class Module:
|
|
|
234
235
|
) -> _Decorator: ...
|
|
235
236
|
def scoped(
|
|
236
237
|
self,
|
|
237
|
-
scope_name: str,
|
|
238
238
|
/,
|
|
239
|
-
|
|
239
|
+
*scope_names: str,
|
|
240
240
|
ignore_type_hint: bool = ...,
|
|
241
241
|
inject: bool = ...,
|
|
242
242
|
on: _TypeInfo[Any] = ...,
|
injection/_core/injectables.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from collections.abc import Awaitable, Callable, MutableMapping
|
|
2
|
+
from collections.abc import Awaitable, Callable, MutableMapping, Sequence
|
|
3
3
|
from contextlib import suppress
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from functools import partial
|
|
@@ -16,7 +16,7 @@ from typing import (
|
|
|
16
16
|
|
|
17
17
|
from injection._core.common.asynchronous import AsyncSemaphore, Caller
|
|
18
18
|
from injection._core.common.type import InputType
|
|
19
|
-
from injection._core.scope import Scope,
|
|
19
|
+
from injection._core.scope import Scope, get_first_scope, in_scope_cache
|
|
20
20
|
from injection._core.slots import SlotKey
|
|
21
21
|
from injection.exceptions import EmptySlotError, InjectionError
|
|
22
22
|
|
|
@@ -129,13 +129,13 @@ class ConstantInjectable[T](Injectable[T]):
|
|
|
129
129
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
130
130
|
class ScopedInjectable[R, T](Injectable[T], ABC):
|
|
131
131
|
factory: Caller[..., R]
|
|
132
|
-
|
|
132
|
+
scope_names: Sequence[str]
|
|
133
133
|
key: SlotKey[T] = field(default_factory=SlotKey)
|
|
134
134
|
logic: CacheLogic[T] = field(default_factory=CacheLogic)
|
|
135
135
|
|
|
136
136
|
@property
|
|
137
137
|
def is_locked(self) -> bool:
|
|
138
|
-
return in_scope_cache(self.key, self.
|
|
138
|
+
return in_scope_cache(self.key, *self.scope_names)
|
|
139
139
|
|
|
140
140
|
@abstractmethod
|
|
141
141
|
async def abuild(self, scope: Scope) -> T:
|
|
@@ -157,14 +157,16 @@ class ScopedInjectable[R, T](Injectable[T], ABC):
|
|
|
157
157
|
|
|
158
158
|
def unlock(self) -> None:
|
|
159
159
|
if self.is_locked:
|
|
160
|
-
raise RuntimeError(
|
|
160
|
+
raise RuntimeError(
|
|
161
|
+
f"To unlock, close all open scopes in [{', '.join(f'`{name}`' for name in self.scope_names)}]."
|
|
162
|
+
)
|
|
161
163
|
|
|
162
164
|
def __get_scope(self) -> Scope:
|
|
163
|
-
return
|
|
165
|
+
return get_first_scope(*self.scope_names)
|
|
164
166
|
|
|
165
167
|
@classmethod
|
|
166
|
-
def
|
|
167
|
-
return partial(cls,
|
|
168
|
+
def bind_scope_names(cls, names: Sequence[str]) -> Callable[[Caller[..., R]], Self]:
|
|
169
|
+
return partial(cls, scope_names=names)
|
|
168
170
|
|
|
169
171
|
|
|
170
172
|
class AsyncCMScopedInjectable[T](ScopedInjectable[AsyncContextManager[T], T]):
|
|
@@ -211,7 +213,7 @@ class ScopedSlotInjectable[T](Injectable[T]):
|
|
|
211
213
|
|
|
212
214
|
def get_instance(self) -> T:
|
|
213
215
|
scope_name = self.scope_name
|
|
214
|
-
scope =
|
|
216
|
+
scope = get_first_scope(scope_name)
|
|
215
217
|
|
|
216
218
|
try:
|
|
217
219
|
return scope.cache[self.key]
|
injection/_core/module.py
CHANGED
|
@@ -250,9 +250,8 @@ class Module(EventListener, InjectionProvider): # type: ignore[misc]
|
|
|
250
250
|
|
|
251
251
|
def scoped[**P, T](
|
|
252
252
|
self,
|
|
253
|
-
scope_name: str,
|
|
254
253
|
/,
|
|
255
|
-
|
|
254
|
+
*scope_names: str,
|
|
256
255
|
ignore_type_hint: bool = False,
|
|
257
256
|
inject: bool = True,
|
|
258
257
|
on: TypeInfo[T] = (),
|
|
@@ -284,7 +283,7 @@ class Module(EventListener, InjectionProvider): # type: ignore[misc]
|
|
|
284
283
|
|
|
285
284
|
self.injectable(
|
|
286
285
|
ctx.wrapper,
|
|
287
|
-
cls=ctx.cls.
|
|
286
|
+
cls=ctx.cls.bind_scope_names(scope_names),
|
|
288
287
|
ignore_type_hint=True,
|
|
289
288
|
inject=inject,
|
|
290
289
|
on=(*ctx.hints, on),
|
injection/_core/scope.py
CHANGED
|
@@ -151,34 +151,36 @@ def define_scope(
|
|
|
151
151
|
if TYPE_CHECKING: # pragma: no cover
|
|
152
152
|
|
|
153
153
|
@overload
|
|
154
|
-
def
|
|
154
|
+
def get_first_scope(*names: str, default: EllipsisType = ...) -> Scope: ...
|
|
155
155
|
|
|
156
156
|
@overload
|
|
157
|
-
def
|
|
157
|
+
def get_first_scope[T](*names: str, default: T) -> Scope | T: ...
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def
|
|
160
|
+
def get_first_scope[T](*names: str, default: T | EllipsisType = ...) -> Scope | T:
|
|
161
161
|
for resolvers in __scope_resolvers.values():
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
for name in names:
|
|
163
|
+
resolver = resolvers.get(name)
|
|
164
|
+
if resolver and (scope := resolver.get_scope()):
|
|
165
|
+
return scope
|
|
165
166
|
|
|
166
167
|
if default is ...:
|
|
167
168
|
raise ScopeUndefinedError(
|
|
168
|
-
f"
|
|
169
|
+
f"No scope in [{', '.join(f'`{name}`' for name in names)}] is defined in the current context."
|
|
169
170
|
)
|
|
170
171
|
|
|
171
172
|
return default
|
|
172
173
|
|
|
173
174
|
|
|
174
|
-
def in_scope_cache(key: SlotKey[Any],
|
|
175
|
-
return any(key in scope.cache for scope in iter_active_scopes(
|
|
175
|
+
def in_scope_cache(key: SlotKey[Any], *scope_names: str) -> bool:
|
|
176
|
+
return any(key in scope.cache for scope in iter_active_scopes(*scope_names))
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
def iter_active_scopes(
|
|
179
|
+
def iter_active_scopes(*names: str) -> Iterator[Scope]:
|
|
179
180
|
active_scopes = (
|
|
180
181
|
resolver.active_scopes
|
|
181
182
|
for resolvers in __scope_resolvers.values()
|
|
183
|
+
for name in names
|
|
182
184
|
if (resolver := resolvers.get(name))
|
|
183
185
|
)
|
|
184
186
|
return itertools.chain.from_iterable(active_scopes)
|
|
@@ -194,7 +196,7 @@ def _bind_scope(
|
|
|
194
196
|
lock = get_lock(threadsafe)
|
|
195
197
|
|
|
196
198
|
with lock:
|
|
197
|
-
if
|
|
199
|
+
if get_first_scope(name, default=None):
|
|
198
200
|
raise ScopeAlreadyDefinedError(
|
|
199
201
|
f"Scope `{name}` is already defined in the current context."
|
|
200
202
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.0
|
|
4
4
|
Summary: Dead-simple dependency injection framework for Python.
|
|
5
5
|
Project-URL: Documentation, https://python-injection.remimd.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/100nm/python-injection
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
injection/__init__.py,sha256=8nYCdToksoBIRTiJFrEbAUa_g8--dFL--4pfSGlGDP4,1350
|
|
2
|
-
injection/__init__.pyi,sha256=
|
|
2
|
+
injection/__init__.pyi,sha256=XzOmrlk_6HNAiQVU4aUDoy3414enMaKTNPpKpXHLYyI,13659
|
|
3
3
|
injection/entrypoint.py,sha256=SjviVj8ajEybGsZ_hmm_NkPIHrIOTmnt3Uhg_GAQJ-s,6709
|
|
4
4
|
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
|
5
5
|
injection/loaders.py,sha256=gBwEfmR1ZHybD2hdtVr-ji6HZS7gCQvgajoiatmZpeM,7394
|
|
@@ -7,10 +7,10 @@ injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
7
7
|
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
injection/_core/asfunction.py,sha256=GY228WHQWvA-9rg3unR8ttyIJlhwxy0bC3LUO5K8Erg,1864
|
|
9
9
|
injection/_core/descriptors.py,sha256=wpW6lFz7ZDDOh1Vc7P86z49UkZ1lajBReWOagSotWDs,3574
|
|
10
|
-
injection/_core/injectables.py,sha256=
|
|
10
|
+
injection/_core/injectables.py,sha256=t_A-tP7kNmNgjXUX_TKwZj3NU65RDpMUTqkVtvXGVNw,6435
|
|
11
11
|
injection/_core/locator.py,sha256=pff_amjMi0XNXdIcPG8fK3vefRPwVLiZVvFg0qwEvDg,8258
|
|
12
|
-
injection/_core/module.py,sha256=
|
|
13
|
-
injection/_core/scope.py,sha256=
|
|
12
|
+
injection/_core/module.py,sha256=AJgAtoYmAzpCXMiazk2mlyW1aAiGuHK5XcvC5Vf-Ibo,28729
|
|
13
|
+
injection/_core/scope.py,sha256=959UhEt1zY2lstP7zJgQJPGMv4s2fdv0gRL4f47GqRM,8517
|
|
14
14
|
injection/_core/slots.py,sha256=g9TG6CbqRzCsjg01iPyfRtTTUCJnnJOwcj9mJabH0dc,37
|
|
15
15
|
injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
injection/_core/common/asynchronous.py,sha256=nKY2_PAMFF2haC75Fwp8O9Zd-SDuenE6n3B-KccwXlw,1772
|
|
@@ -24,7 +24,7 @@ injection/ext/fastapi.py,sha256=wpiNyHKEPwQ7sAJt30XpkgiCi4-gfmyqkQWw9BejXwg,1633
|
|
|
24
24
|
injection/ext/fastapi.pyi,sha256=HLs7mfruIEFRrN_Xf8oCvSa4qwHWfwm6HHU_KMedXkE,185
|
|
25
25
|
injection/testing/__init__.py,sha256=ZWMacZuGhK8Edq1L0Ng0PaRlgKXEunyFZkFvx7-IYIQ,935
|
|
26
26
|
injection/testing/__init__.pyi,sha256=ZFQMUZmvBVkV6Ch7jiYE9zeJhnqHAQnt3vWQ1Th0zDk,646
|
|
27
|
-
python_injection-0.
|
|
28
|
-
python_injection-0.
|
|
29
|
-
python_injection-0.
|
|
30
|
-
python_injection-0.
|
|
27
|
+
python_injection-0.26.0.dist-info/METADATA,sha256=FzRj240iIOWO8HPpTxuAbpEYm-tmBkLHLRbADuoGQJw,3651
|
|
28
|
+
python_injection-0.26.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
29
|
+
python_injection-0.26.0.dist-info/licenses/LICENSE,sha256=oC77BOa9kaaQni5rW-Z-ytz3E5h4EVg248BHg9UFgyg,1063
|
|
30
|
+
python_injection-0.26.0.dist-info/RECORD,,
|
|
File without changes
|