python-injection 0.18.8__tar.gz → 0.18.10__tar.gz
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.
- {python_injection-0.18.8 → python_injection-0.18.10}/PKG-INFO +1 -1
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/__init__.pyi +2 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/module.py +1 -1
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/scope.py +45 -22
- {python_injection-0.18.8 → python_injection-0.18.10}/pyproject.toml +1 -1
- {python_injection-0.18.8 → python_injection-0.18.10}/.gitignore +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/LICENSE +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/README.md +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/__init__.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/__init__.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/__init__.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/asynchronous.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/event.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/invertible.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/key.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/lazy.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/common/type.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/descriptors.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/injectables.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/_core/slots.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/entrypoint.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/exceptions.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/ext/__init__.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/ext/fastapi.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/ext/fastapi.pyi +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/loaders.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/py.typed +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/testing/__init__.py +0 -0
- {python_injection-0.18.8 → python_injection-0.18.10}/injection/testing/__init__.pyi +0 -0
@@ -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
|
"""
|
@@ -996,7 +996,7 @@ class InjectMetadata[**P, T](Caller[P, T], EventListener):
|
|
996
996
|
|
997
997
|
def __init__(self, wrapped: Callable[P, T], /, threadsafe: bool) -> None:
|
998
998
|
self.__dependencies = Dependencies.empty()
|
999
|
-
self.__lock = threading.
|
999
|
+
self.__lock = threading.RLock() if threadsafe else nullcontext()
|
1000
1000
|
self.__owner = None
|
1001
1001
|
self.__tasks = deque()
|
1002
1002
|
self.__wrapped = wrapped
|
@@ -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,26 +200,37 @@ 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
|
-
|
213
|
-
|
225
|
+
stack = ExitStack()
|
226
|
+
stack.enter_context(states[name].bind(scope))
|
227
|
+
|
228
|
+
try:
|
229
|
+
yield _UserScope(scope, lock)
|
230
|
+
|
231
|
+
finally:
|
232
|
+
with lock:
|
233
|
+
stack.close()
|
214
234
|
|
215
235
|
|
216
236
|
@runtime_checkable
|
@@ -304,6 +324,7 @@ class ScopeFacade(Protocol):
|
|
304
324
|
@dataclass(repr=False, frozen=True, slots=True)
|
305
325
|
class _UserScope(ScopeFacade):
|
306
326
|
scope: Scope
|
327
|
+
lock: ContextManager[Any]
|
307
328
|
|
308
329
|
def set_slot[T](self, key: SlotKey[T], value: T) -> Self:
|
309
330
|
return self.slot_map({key: value})
|
@@ -311,9 +332,11 @@ class _UserScope(ScopeFacade):
|
|
311
332
|
def slot_map(self, mapping: Mapping[SlotKey[Any], Any], /) -> Self:
|
312
333
|
cache = self.scope.cache
|
313
334
|
|
314
|
-
|
315
|
-
|
316
|
-
|
335
|
+
with self.lock:
|
336
|
+
for slot_key in mapping:
|
337
|
+
if slot_key in cache:
|
338
|
+
raise InjectionError("Slot already set.")
|
339
|
+
|
340
|
+
cache.update(mapping)
|
317
341
|
|
318
|
-
cache.update(mapping)
|
319
342
|
return self
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|