python-injection 0.18.7__py3-none-any.whl → 0.18.9__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 +3 -0
- injection/_core/module.py +5 -1
- injection/_core/scope.py +38 -17
- injection/entrypoint.py +1 -3
- injection/loaders.py +6 -2
- {python_injection-0.18.7.dist-info → python_injection-0.18.9.dist-info}/METADATA +1 -1
- {python_injection-0.18.7.dist-info → python_injection-0.18.9.dist-info}/RECORD +9 -9
- {python_injection-0.18.7.dist-info → python_injection-0.18.9.dist-info}/WHEEL +0 -0
- {python_injection-0.18.7.dist-info → python_injection-0.18.9.dist-info}/licenses/LICENSE +0 -0
injection/__init__.pyi
CHANGED
@@ -35,12 +35,14 @@ def adefine_scope(
|
|
35
35
|
name: str,
|
36
36
|
/,
|
37
37
|
kind: ScopeKind | ScopeKindStr = ...,
|
38
|
+
threadsafe: bool = ...,
|
38
39
|
) -> AsyncIterator[Scope]: ...
|
39
40
|
@contextmanager
|
40
41
|
def define_scope(
|
41
42
|
name: str,
|
42
43
|
/,
|
43
44
|
kind: ScopeKind | ScopeKindStr = ...,
|
45
|
+
threadsafe: bool = ...,
|
44
46
|
) -> Iterator[Scope]: ...
|
45
47
|
def mod(name: str = ..., /) -> Module:
|
46
48
|
"""
|
@@ -320,6 +322,7 @@ class Module:
|
|
320
322
|
module: Module,
|
321
323
|
*,
|
322
324
|
priority: Priority | PriorityStr = ...,
|
325
|
+
unlock: bool = ...,
|
323
326
|
) -> Iterator[Self]:
|
324
327
|
"""
|
325
328
|
Context manager or decorator for temporary use of a module.
|
injection/_core/module.py
CHANGED
@@ -788,12 +788,16 @@ class Module(Broker, EventListener):
|
|
788
788
|
module: Module,
|
789
789
|
*,
|
790
790
|
priority: Priority | PriorityStr = Priority.get_default(),
|
791
|
+
unlock: bool = False,
|
791
792
|
) -> Iterator[Self]:
|
792
793
|
self.use(module, priority=priority)
|
793
794
|
|
794
795
|
try:
|
795
796
|
yield self
|
796
797
|
finally:
|
798
|
+
if unlock:
|
799
|
+
self.unlock()
|
800
|
+
|
797
801
|
self.stop_using(module)
|
798
802
|
|
799
803
|
def change_priority(self, module: Module, priority: Priority | PriorityStr) -> Self:
|
@@ -992,7 +996,7 @@ class InjectMetadata[**P, T](Caller[P, T], EventListener):
|
|
992
996
|
|
993
997
|
def __init__(self, wrapped: Callable[P, T], /, threadsafe: bool) -> None:
|
994
998
|
self.__dependencies = Dependencies.empty()
|
995
|
-
self.__lock = threading.
|
999
|
+
self.__lock = threading.RLock() if threadsafe else nullcontext()
|
996
1000
|
self.__owner = None
|
997
1001
|
self.__tasks = deque()
|
998
1002
|
self.__wrapped = wrapped
|
injection/_core/scope.py
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import itertools
|
4
|
+
import threading
|
4
5
|
from abc import ABC, abstractmethod
|
5
6
|
from collections import defaultdict
|
6
7
|
from collections.abc import AsyncIterator, Iterator, Mapping, MutableMapping
|
7
|
-
from contextlib import
|
8
|
+
from contextlib import (
|
9
|
+
AsyncExitStack,
|
10
|
+
ExitStack,
|
11
|
+
asynccontextmanager,
|
12
|
+
contextmanager,
|
13
|
+
nullcontext,
|
14
|
+
)
|
8
15
|
from contextvars import ContextVar
|
9
16
|
from dataclasses import dataclass, field
|
10
17
|
from enum import StrEnum
|
@@ -129,9 +136,10 @@ async def adefine_scope(
|
|
129
136
|
name: str,
|
130
137
|
/,
|
131
138
|
kind: ScopeKind | ScopeKindStr = ScopeKind.get_default(),
|
139
|
+
threadsafe: bool = False,
|
132
140
|
) -> AsyncIterator[ScopeFacade]:
|
133
141
|
async with AsyncScope() as scope:
|
134
|
-
with _bind_scope(name, scope, kind) as facade:
|
142
|
+
with _bind_scope(name, scope, kind, threadsafe) as facade:
|
135
143
|
yield facade
|
136
144
|
|
137
145
|
|
@@ -140,9 +148,10 @@ def define_scope(
|
|
140
148
|
name: str,
|
141
149
|
/,
|
142
150
|
kind: ScopeKind | ScopeKindStr = ScopeKind.get_default(),
|
151
|
+
threadsafe: bool = False,
|
143
152
|
) -> Iterator[ScopeFacade]:
|
144
153
|
with SyncScope() as scope:
|
145
|
-
with _bind_scope(name, scope, kind) as facade:
|
154
|
+
with _bind_scope(name, scope, kind, threadsafe) as facade:
|
146
155
|
yield facade
|
147
156
|
|
148
157
|
|
@@ -191,27 +200,39 @@ def _bind_scope(
|
|
191
200
|
name: str,
|
192
201
|
scope: Scope,
|
193
202
|
kind: ScopeKind | ScopeKindStr,
|
203
|
+
threadsafe: bool,
|
194
204
|
) -> Iterator[ScopeFacade]:
|
195
|
-
|
196
|
-
case ScopeKind.CONTEXTUAL:
|
197
|
-
is_already_defined = bool(get_scope(name, default=None))
|
198
|
-
states = __CONTEXTUAL_SCOPES
|
205
|
+
lock = threading.RLock() if threadsafe else nullcontext()
|
199
206
|
|
200
|
-
|
201
|
-
|
202
|
-
|
207
|
+
with lock:
|
208
|
+
match ScopeKind(kind):
|
209
|
+
case ScopeKind.CONTEXTUAL:
|
210
|
+
is_already_defined = bool(get_scope(name, default=None))
|
211
|
+
states = __CONTEXTUAL_SCOPES
|
203
212
|
|
204
|
-
|
205
|
-
|
213
|
+
case ScopeKind.SHARED:
|
214
|
+
is_already_defined = bool(get_active_scopes(name))
|
215
|
+
states = __SHARED_SCOPES
|
206
216
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
217
|
+
case _:
|
218
|
+
raise NotImplementedError
|
219
|
+
|
220
|
+
if is_already_defined:
|
221
|
+
raise ScopeAlreadyDefinedError(
|
222
|
+
f"Scope `{name}` is already defined in the current context."
|
223
|
+
)
|
211
224
|
|
212
|
-
|
225
|
+
stack = ExitStack()
|
226
|
+
binder = states[name].bind(scope)
|
227
|
+
stack.enter_context(binder)
|
228
|
+
|
229
|
+
try:
|
213
230
|
yield _UserScope(scope)
|
214
231
|
|
232
|
+
finally:
|
233
|
+
with lock:
|
234
|
+
stack.close()
|
235
|
+
|
215
236
|
|
216
237
|
@runtime_checkable
|
217
238
|
class Scope(Protocol):
|
injection/entrypoint.py
CHANGED
@@ -153,9 +153,7 @@ class Entrypoint[**P, T]:
|
|
153
153
|
setup_method = profile_loader.module.make_injected_function(setup_method)
|
154
154
|
|
155
155
|
def decorator(function: Callable[P, T]) -> Callable[P, _T]:
|
156
|
-
|
157
|
-
profile_loader.init()
|
158
|
-
|
156
|
+
profile_loader.init()
|
159
157
|
self = cls(function, profile_loader)
|
160
158
|
return MethodType(setup_method, self)().function
|
161
159
|
|
injection/loaders.py
CHANGED
@@ -145,6 +145,10 @@ class ProfileLoader:
|
|
145
145
|
module: Module = field(default_factory=mod, kw_only=True)
|
146
146
|
__initialized_modules: set[str] = field(default_factory=set, init=False)
|
147
147
|
|
148
|
+
@property
|
149
|
+
def __is_empty(self) -> bool:
|
150
|
+
return not self.module_subsets
|
151
|
+
|
148
152
|
def init(self) -> Self:
|
149
153
|
self.__init_subsets_for(self.module)
|
150
154
|
return self
|
@@ -159,12 +163,12 @@ class ProfileLoader:
|
|
159
163
|
self.module.unlock().stop_using(mod(name))
|
160
164
|
|
161
165
|
def __init_subsets_for(self, module: Module) -> Module:
|
162
|
-
if not self.__is_initialized(module):
|
166
|
+
if not self.__is_empty and not self.__is_initialized(module):
|
163
167
|
target_modules = tuple(
|
164
168
|
self.__init_subsets_for(mod(name))
|
165
169
|
for name in self.module_subsets.get(module.name, ())
|
166
170
|
)
|
167
|
-
module.
|
171
|
+
module.init_modules(*target_modules)
|
168
172
|
self.__mark_initialized(module)
|
169
173
|
|
170
174
|
return module
|
@@ -1,14 +1,14 @@
|
|
1
1
|
injection/__init__.py,sha256=7ZRUlO5EEPWO7IlbYHD-8DOX-cg4Np4nYq5fpw-U56o,1259
|
2
|
-
injection/__init__.pyi,sha256=
|
3
|
-
injection/entrypoint.py,sha256=
|
2
|
+
injection/__init__.pyi,sha256=aV1Ebgb7zdCs6b4K-aVzm0JX6W3x0fGp8vtFmWe2U_o,10718
|
3
|
+
injection/entrypoint.py,sha256=12b0_zHAFxHCerAoJTIHkhqi3mLkgheECYAaCUZv_DU,4751
|
4
4
|
injection/exceptions.py,sha256=v57yMujiq6H_zwwn30A8UYEZX9R9k-bY8FnsdaimPM4,1025
|
5
|
-
injection/loaders.py,sha256=
|
5
|
+
injection/loaders.py,sha256=6TjVz9yaQDCVIPIIhjeC6GSXm9rn3oUU2b5KjfOYlsM,6568
|
6
6
|
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
injection/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
injection/_core/descriptors.py,sha256=jH0pyIlPurMmU4yXr-HKS_7BJ-9d0XUvEx4pQre3QeI,704
|
9
9
|
injection/_core/injectables.py,sha256=Rg1nxDkbcpeX4ELohrNVMguPhN36SNQuD0JKfyfL6bI,6192
|
10
|
-
injection/_core/module.py,sha256=
|
11
|
-
injection/_core/scope.py,sha256=
|
10
|
+
injection/_core/module.py,sha256=4WqrDti98A5CrIWaHlld1GYYm4K-8XkKltchJoyXUTU,32016
|
11
|
+
injection/_core/scope.py,sha256=6EKhwbXMwx8ZvqQ5TKzfVtg_JWHhZj_qkHACiDqs6h0,8731
|
12
12
|
injection/_core/slots.py,sha256=g9TG6CbqRzCsjg01iPyfRtTTUCJnnJOwcj9mJabH0dc,37
|
13
13
|
injection/_core/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
injection/_core/common/asynchronous.py,sha256=QeS2Lc4gEBFvTA_snOWfme5mTL4BFZWqZ8EzJwOdVos,1816
|
@@ -22,7 +22,7 @@ injection/ext/fastapi.py,sha256=layUUer5IWiZX6Mmx1_RCYDLNCtEHtpya5ZL6TTBOkY,968
|
|
22
22
|
injection/ext/fastapi.pyi,sha256=8OZEUjHFB9n7QXv_dtXdDuXW-r2huQEFsJ03gJOOvwQ,125
|
23
23
|
injection/testing/__init__.py,sha256=bJ7WXBXrw4rHc91AFVFnOwFLWOlpvX9Oh2SnRQ_NESo,919
|
24
24
|
injection/testing/__init__.pyi,sha256=raGsGlxwbz3jkzJwA_5oCIE1emWINjT2UuwzbnqRb-0,577
|
25
|
-
python_injection-0.18.
|
26
|
-
python_injection-0.18.
|
27
|
-
python_injection-0.18.
|
28
|
-
python_injection-0.18.
|
25
|
+
python_injection-0.18.9.dist-info/METADATA,sha256=ReK9ZuwTqgR5Pv9RI-o2p-j33O2GgGz0Fq_WUpxBkZU,3397
|
26
|
+
python_injection-0.18.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
27
|
+
python_injection-0.18.9.dist-info/licenses/LICENSE,sha256=oC77BOa9kaaQni5rW-Z-ytz3E5h4EVg248BHg9UFgyg,1063
|
28
|
+
python_injection-0.18.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|