python-injection 0.14.1__tar.gz → 0.14.3__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.14.1 → python_injection-0.14.3}/PKG-INFO +1 -1
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/module.py +27 -40
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/exceptions.py +5 -1
- python_injection-0.14.3/injection/py.typed +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/pyproject.toml +1 -1
- python_injection-0.14.1/injection/_core/__init__.py +0 -53
- python_injection-0.14.1/injection/_core/hook.py +0 -106
- {python_injection-0.14.1 → python_injection-0.14.3}/.gitignore +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/README.md +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/__init__.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/__init__.pyi +0 -0
- {python_injection-0.14.1/injection/_core/common → python_injection-0.14.3/injection/_core}/__init__.py +0 -0
- {python_injection-0.14.1/injection/integrations → python_injection-0.14.3/injection/_core/common}/__init__.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/asynchronous.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/event.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/invertible.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/key.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/lazy.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/common/type.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/descriptors.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/injectables.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/_core/scope.py +0 -0
- /python_injection-0.14.1/injection/py.typed → /python_injection-0.14.3/injection/integrations/__init__.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/integrations/fastapi.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/testing/__init__.py +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/testing/__init__.pyi +0 -0
- {python_injection-0.14.1 → python_injection-0.14.3}/injection/utils.py +0 -0
@@ -57,8 +57,8 @@ from injection._core.common.type import (
|
|
57
57
|
TypeInfo,
|
58
58
|
get_return_types,
|
59
59
|
get_yield_hint,
|
60
|
+
standardize_types,
|
60
61
|
)
|
61
|
-
from injection._core.hook import Hook, apply_hooks
|
62
62
|
from injection._core.injectables import (
|
63
63
|
AsyncCMScopedInjectable,
|
64
64
|
CMScopedInjectable,
|
@@ -73,6 +73,7 @@ from injection.exceptions import (
|
|
73
73
|
ModuleLockError,
|
74
74
|
ModuleNotUsedError,
|
75
75
|
NoInjectable,
|
76
|
+
SkipInjectable,
|
76
77
|
)
|
77
78
|
|
78
79
|
"""
|
@@ -222,22 +223,6 @@ class Updater[T]:
|
|
222
223
|
return Record(self.injectable, self.mode)
|
223
224
|
|
224
225
|
|
225
|
-
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
226
|
-
class LocatorHooks[T]:
|
227
|
-
on_conflict: Hook[[Record[T], Record[T], InputType[T]], bool] = field(
|
228
|
-
default_factory=Hook,
|
229
|
-
init=False,
|
230
|
-
)
|
231
|
-
on_input: Hook[[Iterable[InputType[T]]], Iterable[InputType[T]]] = field(
|
232
|
-
default_factory=Hook,
|
233
|
-
init=False,
|
234
|
-
)
|
235
|
-
on_update: Hook[[Updater[T]], Updater[T]] = field(
|
236
|
-
default_factory=Hook,
|
237
|
-
init=False,
|
238
|
-
)
|
239
|
-
|
240
|
-
|
241
226
|
@dataclass(repr=False, frozen=True, slots=True)
|
242
227
|
class Locator(Broker):
|
243
228
|
__records: dict[InputType[Any], Record[Any]] = field(
|
@@ -249,8 +234,6 @@ class Locator(Broker):
|
|
249
234
|
init=False,
|
250
235
|
)
|
251
236
|
|
252
|
-
static_hooks: ClassVar[LocatorHooks[Any]] = LocatorHooks()
|
253
|
-
|
254
237
|
def __getitem__[T](self, cls: InputType[T], /) -> Injectable[T]:
|
255
238
|
for input_class in self.__standardize_inputs((cls,)):
|
256
239
|
try:
|
@@ -325,25 +308,30 @@ class Locator(Broker):
|
|
325
308
|
|
326
309
|
yield cls, record
|
327
310
|
|
311
|
+
@staticmethod
|
328
312
|
def __keep_new_record[T](
|
329
|
-
self,
|
330
313
|
new: Record[T],
|
331
314
|
existing: Record[T],
|
332
315
|
cls: InputType[T],
|
333
316
|
) -> bool:
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
317
|
+
new_mode, existing_mode = new.mode, existing.mode
|
318
|
+
is_override = new_mode == Mode.OVERRIDE
|
319
|
+
|
320
|
+
if new_mode == existing_mode and not is_override:
|
321
|
+
raise RuntimeError(f"An injectable already exists for the class `{cls}`.")
|
322
|
+
|
323
|
+
return is_override or new_mode.rank > existing_mode.rank
|
338
324
|
|
325
|
+
@staticmethod
|
339
326
|
def __standardize_inputs[T](
|
340
|
-
self,
|
341
327
|
classes: Iterable[InputType[T]],
|
342
328
|
) -> Iterable[InputType[T]]:
|
343
|
-
return
|
329
|
+
return tuple(standardize_types(*classes, with_origin=True))
|
344
330
|
|
345
|
-
|
346
|
-
|
331
|
+
@staticmethod
|
332
|
+
def __update_preprocessing[T](updater: Updater[T]) -> Updater[T]:
|
333
|
+
updater.classes = frozenset(standardize_types(*updater.classes))
|
334
|
+
return updater
|
347
335
|
|
348
336
|
|
349
337
|
"""
|
@@ -620,7 +608,7 @@ class Module(Broker, EventListener):
|
|
620
608
|
async def aget_instance(self, cls, default=None): # type: ignore[no-untyped-def]
|
621
609
|
try:
|
622
610
|
return await self.afind_instance(cls)
|
623
|
-
except KeyError:
|
611
|
+
except (KeyError, SkipInjectable):
|
624
612
|
return default
|
625
613
|
|
626
614
|
@overload
|
@@ -640,7 +628,7 @@ class Module(Broker, EventListener):
|
|
640
628
|
def get_instance(self, cls, default=None): # type: ignore[no-untyped-def]
|
641
629
|
try:
|
642
630
|
return self.find_instance(cls)
|
643
|
-
except KeyError:
|
631
|
+
except (KeyError, SkipInjectable):
|
644
632
|
return default
|
645
633
|
|
646
634
|
@overload
|
@@ -866,29 +854,28 @@ class Dependencies:
|
|
866
854
|
lazy_mapping: Lazy[Mapping[str, Injectable[Any]]]
|
867
855
|
|
868
856
|
def __iter__(self) -> Iterator[tuple[str, Any]]:
|
869
|
-
for name, injectable in self.
|
870
|
-
|
871
|
-
|
857
|
+
for name, injectable in self.items():
|
858
|
+
with suppress(SkipInjectable):
|
859
|
+
yield name, injectable.get_instance()
|
872
860
|
|
873
861
|
async def __aiter__(self) -> AsyncIterator[tuple[str, Any]]:
|
874
|
-
for name, injectable in self.
|
875
|
-
|
876
|
-
|
862
|
+
for name, injectable in self.items():
|
863
|
+
with suppress(SkipInjectable):
|
864
|
+
yield name, await injectable.aget_instance()
|
877
865
|
|
878
866
|
@property
|
879
867
|
def are_resolved(self) -> bool:
|
880
868
|
return self.lazy_mapping.is_set
|
881
869
|
|
882
|
-
@property
|
883
|
-
def mapping(self) -> Mapping[str, Injectable[Any]]:
|
884
|
-
return ~self.lazy_mapping
|
885
|
-
|
886
870
|
async def aget_arguments(self) -> dict[str, Any]:
|
887
871
|
return {key: value async for key, value in self}
|
888
872
|
|
889
873
|
def get_arguments(self) -> dict[str, Any]:
|
890
874
|
return dict(self)
|
891
875
|
|
876
|
+
def items(self) -> Iterator[tuple[str, Injectable[Any]]]:
|
877
|
+
return iter((~self.lazy_mapping).items())
|
878
|
+
|
892
879
|
@classmethod
|
893
880
|
def from_iterable(cls, iterable: Iterable[tuple[str, Injectable[Any]]]) -> Self:
|
894
881
|
lazy_mapping = Lazy(lambda: dict(iterable))
|
@@ -10,6 +10,7 @@ __all__ = (
|
|
10
10
|
"ScopeAlreadyDefinedError",
|
11
11
|
"ScopeError",
|
12
12
|
"ScopeUndefinedError",
|
13
|
+
"SkipInjectable",
|
13
14
|
)
|
14
15
|
|
15
16
|
|
@@ -30,6 +31,9 @@ class NoInjectable[T](KeyError, InjectionError):
|
|
30
31
|
return self.__class
|
31
32
|
|
32
33
|
|
34
|
+
class SkipInjectable(InjectionError): ...
|
35
|
+
|
36
|
+
|
33
37
|
class ModuleError(InjectionError): ...
|
34
38
|
|
35
39
|
|
@@ -42,7 +46,7 @@ class ModuleNotUsedError(KeyError, ModuleError): ...
|
|
42
46
|
class ScopeError(InjectionError): ...
|
43
47
|
|
44
48
|
|
45
|
-
class ScopeUndefinedError(LookupError, ScopeError): ...
|
49
|
+
class ScopeUndefinedError(LookupError, SkipInjectable, ScopeError): ...
|
46
50
|
|
47
51
|
|
48
52
|
class ScopeAlreadyDefinedError(ScopeError): ...
|
File without changes
|
@@ -1,53 +0,0 @@
|
|
1
|
-
from collections.abc import Iterable
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from injection._core.common.type import InputType, standardize_types
|
5
|
-
from injection._core.hook import HookGenerator
|
6
|
-
from injection._core.module import Locator, Mode, Record, Updater
|
7
|
-
|
8
|
-
__all__ = ()
|
9
|
-
|
10
|
-
|
11
|
-
@Locator.static_hooks.on_conflict
|
12
|
-
def check_mode[T](
|
13
|
-
new: Record[T],
|
14
|
-
existing: Record[T],
|
15
|
-
cls: InputType[T],
|
16
|
-
*_: Any,
|
17
|
-
**__: Any,
|
18
|
-
) -> HookGenerator[bool]:
|
19
|
-
new_mode = new.mode
|
20
|
-
is_override = new_mode == Mode.OVERRIDE
|
21
|
-
|
22
|
-
if new_mode == existing.mode and not is_override:
|
23
|
-
raise RuntimeError(f"An injectable already exists for the class `{cls}`.")
|
24
|
-
|
25
|
-
value = yield
|
26
|
-
return value or is_override
|
27
|
-
|
28
|
-
|
29
|
-
@Locator.static_hooks.on_conflict
|
30
|
-
def compare_mode_rank[T](
|
31
|
-
new: Record[T],
|
32
|
-
existing: Record[T],
|
33
|
-
*_: Any,
|
34
|
-
**__: Any,
|
35
|
-
) -> HookGenerator[bool]:
|
36
|
-
value = yield
|
37
|
-
return value or new.mode.rank > existing.mode.rank
|
38
|
-
|
39
|
-
|
40
|
-
@Locator.static_hooks.on_input
|
41
|
-
def standardize_input_classes[T](
|
42
|
-
*_: Any,
|
43
|
-
**__: Any,
|
44
|
-
) -> HookGenerator[Iterable[InputType[T]]]:
|
45
|
-
classes = yield
|
46
|
-
return tuple(standardize_types(*classes, with_origin=True))
|
47
|
-
|
48
|
-
|
49
|
-
@Locator.static_hooks.on_update
|
50
|
-
def standardize_classes[T](*_: Any, **__: Any) -> HookGenerator[Updater[T]]:
|
51
|
-
updater = yield
|
52
|
-
updater.classes = frozenset(standardize_types(*updater.classes))
|
53
|
-
return updater
|
@@ -1,106 +0,0 @@
|
|
1
|
-
import itertools
|
2
|
-
from collections.abc import Callable, Generator, Iterator
|
3
|
-
from dataclasses import dataclass, field
|
4
|
-
from inspect import isclass, isgeneratorfunction
|
5
|
-
from typing import Any, Self, TypeGuard
|
6
|
-
|
7
|
-
from injection.exceptions import HookError
|
8
|
-
|
9
|
-
type HookGenerator[T] = Generator[None, T, T]
|
10
|
-
type HookGeneratorFunction[**P, T] = Callable[P, HookGenerator[T]]
|
11
|
-
type HookFunction[**P, T] = Callable[P, T] | HookGeneratorFunction[P, T]
|
12
|
-
|
13
|
-
|
14
|
-
@dataclass(eq=False, frozen=True, slots=True)
|
15
|
-
class Hook[**P, T]:
|
16
|
-
__functions: list[HookFunction[P, T]] = field(
|
17
|
-
default_factory=list,
|
18
|
-
init=False,
|
19
|
-
repr=False,
|
20
|
-
)
|
21
|
-
|
22
|
-
def __call__(
|
23
|
-
self,
|
24
|
-
wrapped: HookFunction[P, T] | type[HookFunction[P, T]] | None = None,
|
25
|
-
/,
|
26
|
-
) -> Any:
|
27
|
-
def decorator(wp: Any) -> Any:
|
28
|
-
self.add(wp() if isclass(wp) else wp)
|
29
|
-
return wp
|
30
|
-
|
31
|
-
return decorator(wrapped) if wrapped else decorator
|
32
|
-
|
33
|
-
@property
|
34
|
-
def __stack(self) -> Iterator[HookFunction[P, T]]:
|
35
|
-
return iter(self.__functions)
|
36
|
-
|
37
|
-
def add(self, *functions: HookFunction[P, T]) -> Self:
|
38
|
-
self.__functions.extend(reversed(functions))
|
39
|
-
return self
|
40
|
-
|
41
|
-
@classmethod
|
42
|
-
def apply_several(cls, handler: Callable[P, T], *hooks: Self) -> Callable[P, T]:
|
43
|
-
stack = itertools.chain.from_iterable((hook.__stack for hook in hooks))
|
44
|
-
return cls.__apply_stack(handler, stack)
|
45
|
-
|
46
|
-
@classmethod
|
47
|
-
def __apply_function(
|
48
|
-
cls,
|
49
|
-
handler: Callable[P, T],
|
50
|
-
function: HookFunction[P, T],
|
51
|
-
) -> Callable[P, T]:
|
52
|
-
if not cls.__is_hook_generator_function(function):
|
53
|
-
return function # type: ignore[return-value]
|
54
|
-
|
55
|
-
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
56
|
-
hook: HookGenerator[T] = function(*args, **kwargs)
|
57
|
-
|
58
|
-
try:
|
59
|
-
next(hook)
|
60
|
-
|
61
|
-
try:
|
62
|
-
value = handler(*args, **kwargs)
|
63
|
-
except BaseException as exc:
|
64
|
-
hook.throw(exc)
|
65
|
-
else:
|
66
|
-
hook.send(value)
|
67
|
-
|
68
|
-
except StopIteration as stop:
|
69
|
-
return stop.value
|
70
|
-
|
71
|
-
finally:
|
72
|
-
hook.close()
|
73
|
-
|
74
|
-
raise HookError("Missing return value.")
|
75
|
-
|
76
|
-
return wrapper
|
77
|
-
|
78
|
-
@classmethod
|
79
|
-
def __apply_stack(
|
80
|
-
cls,
|
81
|
-
handler: Callable[P, T],
|
82
|
-
stack: Iterator[HookFunction[P, T]],
|
83
|
-
) -> Callable[P, T]:
|
84
|
-
for function in stack:
|
85
|
-
new_handler = cls.__apply_function(handler, function)
|
86
|
-
return cls.__apply_stack(new_handler, stack)
|
87
|
-
|
88
|
-
return handler
|
89
|
-
|
90
|
-
@staticmethod
|
91
|
-
def __is_hook_generator_function[**_P, _T](
|
92
|
-
function: HookFunction[_P, _T],
|
93
|
-
) -> TypeGuard[HookGeneratorFunction[_P, _T]]:
|
94
|
-
for fn in function, getattr(function, "__call__", None):
|
95
|
-
if isgeneratorfunction(fn):
|
96
|
-
return True
|
97
|
-
|
98
|
-
return False
|
99
|
-
|
100
|
-
|
101
|
-
def apply_hooks[**P, T](
|
102
|
-
handler: Callable[P, T],
|
103
|
-
hook: Hook[P, T],
|
104
|
-
*hooks: Hook[P, T],
|
105
|
-
) -> Callable[P, T]:
|
106
|
-
return Hook.apply_several(handler, hook, *hooks)
|
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
|