python-injection 0.8.4.post2__py3-none-any.whl → 0.9.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.
Potentially problematic release.
This version of python-injection might be problematic. Click here for more details.
- injection/__init__.pyi +44 -38
- injection/common/event.py +3 -3
- injection/common/invertible.py +6 -8
- injection/common/lazy.py +9 -14
- injection/common/tools/threading.py +1 -3
- injection/common/tools/type.py +52 -27
- injection/core/module.py +132 -105
- injection/exceptions.py +4 -6
- injection/integrations/blacksheep.py +2 -4
- {python_injection-0.8.4.post2.dist-info → python_injection-0.9.0.dist-info}/METADATA +2 -4
- python_injection-0.9.0.dist-info/RECORD +19 -0
- injection/common/queue.py +0 -63
- python_injection-0.8.4.post2.dist-info/RECORD +0 -20
- {python_injection-0.8.4.post2.dist-info → python_injection-0.9.0.dist-info}/WHEEL +0 -0
injection/__init__.pyi
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from collections.abc import Callable
|
|
2
|
+
from collections.abc import Callable
|
|
3
3
|
from contextlib import ContextDecorator
|
|
4
|
-
from enum import
|
|
4
|
+
from enum import StrEnum
|
|
5
5
|
from types import UnionType
|
|
6
6
|
from typing import (
|
|
7
7
|
Any,
|
|
8
8
|
ContextManager,
|
|
9
9
|
Final,
|
|
10
|
-
Literal,
|
|
11
10
|
Protocol,
|
|
12
|
-
|
|
11
|
+
Self,
|
|
13
12
|
final,
|
|
14
13
|
runtime_checkable,
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
from .common.invertible import Invertible
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
from .common.tools.type import TypeInfo
|
|
18
|
+
from .core import InjectableFactory
|
|
19
|
+
from .core import ModeStr as InjectableModeStr
|
|
20
|
+
from .core import PriorityStr as ModulePriorityStr
|
|
21
21
|
|
|
22
22
|
default_module: Final[Module] = ...
|
|
23
23
|
|
|
@@ -41,6 +41,8 @@ class Module:
|
|
|
41
41
|
|
|
42
42
|
def __init__(self, name: str = ...): ...
|
|
43
43
|
def __contains__(self, cls: type | UnionType, /) -> bool: ...
|
|
44
|
+
@property
|
|
45
|
+
def is_locked(self) -> bool: ...
|
|
44
46
|
def inject(self, wrapped: Callable[..., Any] = ..., /):
|
|
45
47
|
"""
|
|
46
48
|
Decorator applicable to a class or function. Inject function dependencies using
|
|
@@ -48,15 +50,15 @@ class Module:
|
|
|
48
50
|
will be those of the `__init__` method.
|
|
49
51
|
"""
|
|
50
52
|
|
|
51
|
-
def injectable(
|
|
53
|
+
def injectable[T](
|
|
52
54
|
self,
|
|
53
|
-
wrapped: Callable[...,
|
|
55
|
+
wrapped: Callable[..., T] = ...,
|
|
54
56
|
/,
|
|
55
57
|
*,
|
|
56
|
-
cls:
|
|
58
|
+
cls: InjectableFactory[T] = ...,
|
|
57
59
|
inject: bool = ...,
|
|
58
|
-
on:
|
|
59
|
-
mode: InjectableMode |
|
|
60
|
+
on: TypeInfo[T] = ...,
|
|
61
|
+
mode: InjectableMode | InjectableModeStr = ...,
|
|
60
62
|
):
|
|
61
63
|
"""
|
|
62
64
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -64,14 +66,14 @@ class Module:
|
|
|
64
66
|
injected each time.
|
|
65
67
|
"""
|
|
66
68
|
|
|
67
|
-
def singleton(
|
|
69
|
+
def singleton[T](
|
|
68
70
|
self,
|
|
69
|
-
wrapped: Callable[...,
|
|
71
|
+
wrapped: Callable[..., T] = ...,
|
|
70
72
|
/,
|
|
71
73
|
*,
|
|
72
74
|
inject: bool = ...,
|
|
73
|
-
on:
|
|
74
|
-
mode: InjectableMode |
|
|
75
|
+
on: TypeInfo[T] = ...,
|
|
76
|
+
mode: InjectableMode | InjectableModeStr = ...,
|
|
75
77
|
):
|
|
76
78
|
"""
|
|
77
79
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -86,37 +88,37 @@ class Module:
|
|
|
86
88
|
registered.
|
|
87
89
|
"""
|
|
88
90
|
|
|
89
|
-
def set_constant(
|
|
91
|
+
def set_constant[T](
|
|
90
92
|
self,
|
|
91
|
-
instance:
|
|
92
|
-
on:
|
|
93
|
+
instance: T,
|
|
94
|
+
on: TypeInfo[T] = ...,
|
|
93
95
|
*,
|
|
94
|
-
mode: InjectableMode |
|
|
95
|
-
) ->
|
|
96
|
+
mode: InjectableMode | InjectableModeStr = ...,
|
|
97
|
+
) -> T:
|
|
96
98
|
"""
|
|
97
99
|
Function for registering a specific instance to be injected. This is useful for
|
|
98
100
|
registering global variables. The difference with the singleton decorator is
|
|
99
101
|
that no dependencies are resolved, so the module doesn't need to be locked.
|
|
100
102
|
"""
|
|
101
103
|
|
|
102
|
-
def resolve(self, cls: type[
|
|
104
|
+
def resolve[T](self, cls: type[T]) -> T:
|
|
103
105
|
"""
|
|
104
106
|
Function used to retrieve an instance associated with the type passed in
|
|
105
107
|
parameter or an exception will be raised.
|
|
106
108
|
"""
|
|
107
109
|
|
|
108
|
-
def get_instance(self, cls: type[
|
|
110
|
+
def get_instance[T](self, cls: type[T]) -> T | None:
|
|
109
111
|
"""
|
|
110
112
|
Function used to retrieve an instance associated with the type passed in
|
|
111
113
|
parameter or return `None`.
|
|
112
114
|
"""
|
|
113
115
|
|
|
114
|
-
def get_lazy_instance(
|
|
116
|
+
def get_lazy_instance[T](
|
|
115
117
|
self,
|
|
116
|
-
cls: type[
|
|
118
|
+
cls: type[T],
|
|
117
119
|
*,
|
|
118
120
|
cache: bool = ...,
|
|
119
|
-
) -> Invertible[
|
|
121
|
+
) -> Invertible[T | None]:
|
|
120
122
|
"""
|
|
121
123
|
Function used to retrieve an instance associated with the type passed in
|
|
122
124
|
parameter or `None`. Return a `Invertible` object. To access the instance
|
|
@@ -126,19 +128,24 @@ class Module:
|
|
|
126
128
|
Example: instance = ~lazy_instance
|
|
127
129
|
"""
|
|
128
130
|
|
|
131
|
+
def init_modules(self, *modules: Module) -> Self:
|
|
132
|
+
"""
|
|
133
|
+
Function to clean modules in use and to use those passed as parameters.
|
|
134
|
+
"""
|
|
135
|
+
|
|
129
136
|
def use(
|
|
130
137
|
self,
|
|
131
138
|
module: Module,
|
|
132
139
|
*,
|
|
133
|
-
priority: ModulePriority |
|
|
134
|
-
):
|
|
140
|
+
priority: ModulePriority | ModulePriorityStr = ...,
|
|
141
|
+
) -> Self:
|
|
135
142
|
"""
|
|
136
143
|
Function for using another module. Using another module replaces the module's
|
|
137
144
|
dependencies with those of the module used. If the dependency is not found, it
|
|
138
145
|
will be searched for in the module's dependency container.
|
|
139
146
|
"""
|
|
140
147
|
|
|
141
|
-
def stop_using(self, module: Module):
|
|
148
|
+
def stop_using(self, module: Module) -> Self:
|
|
142
149
|
"""
|
|
143
150
|
Function to remove a module in use.
|
|
144
151
|
"""
|
|
@@ -147,7 +154,7 @@ class Module:
|
|
|
147
154
|
self,
|
|
148
155
|
module: Module,
|
|
149
156
|
*,
|
|
150
|
-
priority: ModulePriority |
|
|
157
|
+
priority: ModulePriority | ModulePriorityStr = ...,
|
|
151
158
|
) -> ContextManager | ContextDecorator:
|
|
152
159
|
"""
|
|
153
160
|
Context manager or decorator for temporary use of a module.
|
|
@@ -156,8 +163,8 @@ class Module:
|
|
|
156
163
|
def change_priority(
|
|
157
164
|
self,
|
|
158
165
|
module: Module,
|
|
159
|
-
priority: ModulePriority |
|
|
160
|
-
):
|
|
166
|
+
priority: ModulePriority | ModulePriorityStr,
|
|
167
|
+
) -> Self:
|
|
161
168
|
"""
|
|
162
169
|
Function for changing the priority of a module in use.
|
|
163
170
|
There are two priority values:
|
|
@@ -166,27 +173,26 @@ class Module:
|
|
|
166
173
|
* **HIGH**: The module concerned becomes the most important of the modules used.
|
|
167
174
|
"""
|
|
168
175
|
|
|
169
|
-
def unlock(self):
|
|
176
|
+
def unlock(self) -> Self:
|
|
170
177
|
"""
|
|
171
178
|
Function to unlock the module by deleting cached instances of singletons.
|
|
172
179
|
"""
|
|
173
180
|
|
|
174
181
|
@final
|
|
175
|
-
class ModulePriority(
|
|
182
|
+
class ModulePriority(StrEnum):
|
|
176
183
|
LOW = ...
|
|
177
184
|
HIGH = ...
|
|
178
185
|
|
|
179
186
|
@runtime_checkable
|
|
180
|
-
class Injectable(Protocol
|
|
181
|
-
def __init__(self, factory: Callable[[], _T_co] = ..., /): ...
|
|
187
|
+
class Injectable[T](Protocol):
|
|
182
188
|
@property
|
|
183
189
|
def is_locked(self) -> bool: ...
|
|
184
190
|
def unlock(self): ...
|
|
185
191
|
@abstractmethod
|
|
186
|
-
def get_instance(self) ->
|
|
192
|
+
def get_instance(self) -> T: ...
|
|
187
193
|
|
|
188
194
|
@final
|
|
189
|
-
class InjectableMode(
|
|
195
|
+
class InjectableMode(StrEnum):
|
|
190
196
|
FALLBACK = ...
|
|
191
197
|
NORMAL = ...
|
|
192
198
|
OVERRIDE = ...
|
injection/common/event.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from contextlib import ExitStack, contextmanager, suppress
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import ContextManager
|
|
4
|
+
from typing import ContextManager, Self
|
|
5
5
|
from weakref import WeakSet
|
|
6
6
|
|
|
7
7
|
__all__ = ("Event", "EventChannel", "EventListener")
|
|
@@ -36,11 +36,11 @@ class EventChannel:
|
|
|
36
36
|
|
|
37
37
|
yield
|
|
38
38
|
|
|
39
|
-
def add_listener(self, listener: EventListener):
|
|
39
|
+
def add_listener(self, listener: EventListener) -> Self:
|
|
40
40
|
self.__listeners.add(listener)
|
|
41
41
|
return self
|
|
42
42
|
|
|
43
|
-
def remove_listener(self, listener: EventListener):
|
|
43
|
+
def remove_listener(self, listener: EventListener) -> Self:
|
|
44
44
|
with suppress(KeyError):
|
|
45
45
|
self.__listeners.remove(listener)
|
|
46
46
|
|
injection/common/invertible.py
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Protocol,
|
|
4
|
+
from typing import Protocol, runtime_checkable
|
|
5
5
|
|
|
6
6
|
__all__ = ("Invertible", "SimpleInvertible")
|
|
7
7
|
|
|
8
|
-
_T_co = TypeVar("_T_co", covariant=True)
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
@runtime_checkable
|
|
12
|
-
class Invertible(Protocol
|
|
10
|
+
class Invertible[T](Protocol):
|
|
13
11
|
@abstractmethod
|
|
14
|
-
def __invert__(self) ->
|
|
12
|
+
def __invert__(self) -> T:
|
|
15
13
|
raise NotImplementedError
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
19
|
-
class SimpleInvertible(Invertible[
|
|
20
|
-
callable: Callable[[],
|
|
17
|
+
class SimpleInvertible[T](Invertible[T]):
|
|
18
|
+
callable: Callable[[], T]
|
|
21
19
|
|
|
22
|
-
def __invert__(self) ->
|
|
20
|
+
def __invert__(self) -> T:
|
|
23
21
|
return self.callable()
|
injection/common/lazy.py
CHANGED
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterator, Mapping
|
|
2
2
|
from types import MappingProxyType
|
|
3
|
-
from typing import TypeVar
|
|
4
3
|
|
|
5
4
|
from injection.common.invertible import Invertible
|
|
6
5
|
|
|
7
6
|
__all__ = ("Lazy", "LazyMapping")
|
|
8
7
|
|
|
9
|
-
_T = TypeVar("_T")
|
|
10
|
-
_K = TypeVar("_K")
|
|
11
|
-
_V = TypeVar("_V")
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
class Lazy(Invertible[_T]):
|
|
9
|
+
class Lazy[T](Invertible[T]):
|
|
15
10
|
__slots__ = ("__cache", "__is_set")
|
|
16
11
|
|
|
17
|
-
def __init__(self, factory: Callable[[],
|
|
12
|
+
def __init__(self, factory: Callable[[], T]):
|
|
18
13
|
self.__setup_cache(factory)
|
|
19
14
|
|
|
20
|
-
def __invert__(self) ->
|
|
15
|
+
def __invert__(self) -> T:
|
|
21
16
|
return next(self.__cache)
|
|
22
17
|
|
|
23
18
|
@property
|
|
24
19
|
def is_set(self) -> bool:
|
|
25
20
|
return self.__is_set
|
|
26
21
|
|
|
27
|
-
def __setup_cache(self, factory: Callable[[],
|
|
28
|
-
def cache_generator() -> Iterator[
|
|
22
|
+
def __setup_cache(self, factory: Callable[[], T]):
|
|
23
|
+
def cache_generator() -> Iterator[T]:
|
|
29
24
|
nonlocal factory
|
|
30
25
|
cached = factory()
|
|
31
26
|
self.__is_set = True
|
|
@@ -38,16 +33,16 @@ class Lazy(Invertible[_T]):
|
|
|
38
33
|
self.__is_set = False
|
|
39
34
|
|
|
40
35
|
|
|
41
|
-
class LazyMapping(Mapping[
|
|
36
|
+
class LazyMapping[K, V](Mapping[K, V]):
|
|
42
37
|
__slots__ = ("__lazy",)
|
|
43
38
|
|
|
44
|
-
def __init__(self, iterator: Iterator[tuple[
|
|
39
|
+
def __init__(self, iterator: Iterator[tuple[K, V]]):
|
|
45
40
|
self.__lazy = Lazy(lambda: MappingProxyType(dict(iterator)))
|
|
46
41
|
|
|
47
|
-
def __getitem__(self, key:
|
|
42
|
+
def __getitem__(self, key: K, /) -> V:
|
|
48
43
|
return (~self.__lazy)[key]
|
|
49
44
|
|
|
50
|
-
def __iter__(self) -> Iterator[
|
|
45
|
+
def __iter__(self) -> Iterator[K]:
|
|
51
46
|
yield from ~self.__lazy
|
|
52
47
|
|
|
53
48
|
def __len__(self) -> int:
|
injection/common/tools/type.py
CHANGED
|
@@ -1,48 +1,73 @@
|
|
|
1
|
-
from collections.abc import Iterable, Iterator
|
|
1
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
2
2
|
from inspect import get_annotations, isfunction
|
|
3
|
-
from types import
|
|
4
|
-
from typing import
|
|
3
|
+
from types import UnionType
|
|
4
|
+
from typing import (
|
|
5
|
+
Annotated,
|
|
6
|
+
Any,
|
|
7
|
+
NamedTuple,
|
|
8
|
+
Self,
|
|
9
|
+
Union,
|
|
10
|
+
get_args,
|
|
11
|
+
get_origin,
|
|
12
|
+
)
|
|
5
13
|
|
|
6
|
-
__all__ = ("
|
|
14
|
+
__all__ = ("TypeInfo", "TypeReport", "analyze_types", "get_return_types")
|
|
7
15
|
|
|
16
|
+
type TypeInfo[T] = type[T] | Callable[..., T] | Iterable[TypeInfo[T]] | UnionType
|
|
8
17
|
|
|
9
|
-
def format_type(cls: type | Any) -> str:
|
|
10
|
-
try:
|
|
11
|
-
return f"{cls.__module__}.{cls.__qualname__}"
|
|
12
|
-
except AttributeError:
|
|
13
|
-
return str(cls)
|
|
14
18
|
|
|
19
|
+
class TypeReport[T](NamedTuple):
|
|
20
|
+
origin: type[T]
|
|
21
|
+
args: tuple[Any, ...]
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
@property
|
|
24
|
+
def cls(self) -> type[T]:
|
|
25
|
+
if self.args:
|
|
26
|
+
return self.origin[*self.args]
|
|
27
|
+
|
|
28
|
+
return self.origin
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def no_args(self) -> Self:
|
|
32
|
+
if self.args:
|
|
33
|
+
return type(self)(self.origin, ())
|
|
34
|
+
|
|
35
|
+
return self
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
|
|
38
|
+
def analyze_types(*types: type | Any) -> Iterator[TypeReport[Any]]:
|
|
39
|
+
for tp in types:
|
|
40
|
+
if tp is None:
|
|
21
41
|
continue
|
|
22
42
|
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
origin = get_origin(tp)
|
|
44
|
+
|
|
45
|
+
if origin is Union or isinstance(tp, UnionType):
|
|
46
|
+
inner_types = get_args(tp)
|
|
25
47
|
|
|
26
|
-
elif origin is Annotated
|
|
27
|
-
|
|
48
|
+
elif origin is Annotated:
|
|
49
|
+
inner_types = get_args(tp)[:1]
|
|
28
50
|
|
|
29
51
|
else:
|
|
30
|
-
yield origin
|
|
52
|
+
yield TypeReport(origin or tp, get_args(tp))
|
|
31
53
|
continue
|
|
32
54
|
|
|
33
|
-
yield from
|
|
55
|
+
yield from analyze_types(*inner_types)
|
|
34
56
|
|
|
35
57
|
|
|
36
|
-
def
|
|
37
|
-
for
|
|
38
|
-
if isinstance(
|
|
39
|
-
|
|
58
|
+
def get_return_types(*args: TypeInfo[Any]) -> Iterator[type | UnionType]:
|
|
59
|
+
for arg in args:
|
|
60
|
+
if isinstance(arg, Iterable) and not isinstance(
|
|
61
|
+
get_origin(arg) or arg,
|
|
62
|
+
type | str,
|
|
63
|
+
):
|
|
64
|
+
inner_args = arg
|
|
40
65
|
|
|
41
|
-
elif isfunction(
|
|
42
|
-
|
|
66
|
+
elif isfunction(arg):
|
|
67
|
+
inner_args = (get_annotations(arg, eval_str=True).get("return"),)
|
|
43
68
|
|
|
44
69
|
else:
|
|
45
|
-
yield
|
|
70
|
+
yield arg
|
|
46
71
|
continue
|
|
47
72
|
|
|
48
|
-
yield from
|
|
73
|
+
yield from get_return_types(*inner_args)
|
injection/core/module.py
CHANGED
|
@@ -14,9 +14,10 @@ from collections.abc import (
|
|
|
14
14
|
)
|
|
15
15
|
from contextlib import contextmanager, suppress
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
|
-
from enum import
|
|
17
|
+
from enum import StrEnum
|
|
18
18
|
from functools import partialmethod, singledispatchmethod, update_wrapper
|
|
19
19
|
from inspect import Signature, isclass
|
|
20
|
+
from queue import Queue
|
|
20
21
|
from types import MethodType, UnionType
|
|
21
22
|
from typing import (
|
|
22
23
|
Any,
|
|
@@ -26,16 +27,20 @@ from typing import (
|
|
|
26
27
|
NamedTuple,
|
|
27
28
|
NoReturn,
|
|
28
29
|
Protocol,
|
|
29
|
-
|
|
30
|
+
Self,
|
|
30
31
|
runtime_checkable,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
from injection.common.event import Event, EventChannel, EventListener
|
|
34
35
|
from injection.common.invertible import Invertible, SimpleInvertible
|
|
35
36
|
from injection.common.lazy import Lazy, LazyMapping
|
|
36
|
-
from injection.common.queue import LimitedQueue
|
|
37
37
|
from injection.common.tools.threading import synchronized
|
|
38
|
-
from injection.common.tools.type import
|
|
38
|
+
from injection.common.tools.type import (
|
|
39
|
+
TypeInfo,
|
|
40
|
+
TypeReport,
|
|
41
|
+
analyze_types,
|
|
42
|
+
get_return_types,
|
|
43
|
+
)
|
|
39
44
|
from injection.exceptions import (
|
|
40
45
|
InjectionError,
|
|
41
46
|
ModuleError,
|
|
@@ -44,14 +49,18 @@ from injection.exceptions import (
|
|
|
44
49
|
NoInjectable,
|
|
45
50
|
)
|
|
46
51
|
|
|
47
|
-
__all__ = (
|
|
52
|
+
__all__ = (
|
|
53
|
+
"Injectable",
|
|
54
|
+
"InjectableFactory",
|
|
55
|
+
"Mode",
|
|
56
|
+
"ModeStr",
|
|
57
|
+
"Module",
|
|
58
|
+
"Priority",
|
|
59
|
+
"PriorityStr",
|
|
60
|
+
)
|
|
48
61
|
|
|
49
62
|
_logger = logging.getLogger(__name__)
|
|
50
63
|
|
|
51
|
-
_T = TypeVar("_T")
|
|
52
|
-
_T_co = TypeVar("_T_co", covariant=True)
|
|
53
|
-
|
|
54
|
-
|
|
55
64
|
"""
|
|
56
65
|
Events
|
|
57
66
|
"""
|
|
@@ -64,12 +73,12 @@ class ContainerEvent(Event, ABC):
|
|
|
64
73
|
|
|
65
74
|
@dataclass(frozen=True, slots=True)
|
|
66
75
|
class ContainerDependenciesUpdated(ContainerEvent):
|
|
67
|
-
|
|
76
|
+
reports: Collection[TypeReport]
|
|
68
77
|
mode: Mode
|
|
69
78
|
|
|
70
79
|
def __str__(self) -> str:
|
|
71
|
-
length = len(self.
|
|
72
|
-
formatted_classes = ", ".join(f"`{
|
|
80
|
+
length = len(self.reports)
|
|
81
|
+
formatted_classes = ", ".join(f"`{report.cls}`" for report in self.reports)
|
|
73
82
|
return (
|
|
74
83
|
f"{length} container dependenc{'ies' if length > 1 else 'y'} have been "
|
|
75
84
|
f"updated{f': {formatted_classes}' if formatted_classes else ''}."
|
|
@@ -135,12 +144,9 @@ Injectables
|
|
|
135
144
|
|
|
136
145
|
|
|
137
146
|
@runtime_checkable
|
|
138
|
-
class Injectable(Protocol
|
|
147
|
+
class Injectable[T](Protocol):
|
|
139
148
|
__slots__ = ()
|
|
140
149
|
|
|
141
|
-
def __init__(self, __factory: Callable[[], _T_co] = None, /):
|
|
142
|
-
pass
|
|
143
|
-
|
|
144
150
|
@property
|
|
145
151
|
def is_locked(self) -> bool:
|
|
146
152
|
return False
|
|
@@ -149,23 +155,23 @@ class Injectable(Protocol[_T_co]):
|
|
|
149
155
|
return
|
|
150
156
|
|
|
151
157
|
@abstractmethod
|
|
152
|
-
def get_instance(self) ->
|
|
158
|
+
def get_instance(self) -> T:
|
|
153
159
|
raise NotImplementedError
|
|
154
160
|
|
|
155
161
|
|
|
156
162
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
157
|
-
class BaseInjectable(Injectable[
|
|
158
|
-
factory: Callable[[],
|
|
163
|
+
class BaseInjectable[T](Injectable[T], ABC):
|
|
164
|
+
factory: Callable[[], T]
|
|
159
165
|
|
|
160
166
|
|
|
161
|
-
class NewInjectable(BaseInjectable[
|
|
167
|
+
class NewInjectable[T](BaseInjectable[T]):
|
|
162
168
|
__slots__ = ()
|
|
163
169
|
|
|
164
|
-
def get_instance(self) ->
|
|
170
|
+
def get_instance(self) -> T:
|
|
165
171
|
return self.factory()
|
|
166
172
|
|
|
167
173
|
|
|
168
|
-
class SingletonInjectable(BaseInjectable[
|
|
174
|
+
class SingletonInjectable[T](BaseInjectable[T]):
|
|
169
175
|
__slots__ = ("__dict__",)
|
|
170
176
|
|
|
171
177
|
__INSTANCE_KEY: ClassVar[str] = "$instance"
|
|
@@ -181,7 +187,7 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
181
187
|
def unlock(self):
|
|
182
188
|
self.cache.clear()
|
|
183
189
|
|
|
184
|
-
def get_instance(self) ->
|
|
190
|
+
def get_instance(self) -> T:
|
|
185
191
|
with suppress(KeyError):
|
|
186
192
|
return self.cache[self.__INSTANCE_KEY]
|
|
187
193
|
|
|
@@ -193,11 +199,11 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
193
199
|
|
|
194
200
|
|
|
195
201
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
196
|
-
class ShouldBeInjectable(Injectable[
|
|
197
|
-
cls: type[
|
|
202
|
+
class ShouldBeInjectable[T](Injectable[T]):
|
|
203
|
+
cls: type[T]
|
|
198
204
|
|
|
199
205
|
def get_instance(self) -> NoReturn:
|
|
200
|
-
raise InjectionError(f"`{
|
|
206
|
+
raise InjectionError(f"`{self.cls}` should be an injectable.")
|
|
201
207
|
|
|
202
208
|
|
|
203
209
|
"""
|
|
@@ -210,7 +216,7 @@ class Broker(Protocol):
|
|
|
210
216
|
__slots__ = ()
|
|
211
217
|
|
|
212
218
|
@abstractmethod
|
|
213
|
-
def __getitem__(self, cls: type[
|
|
219
|
+
def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
|
|
214
220
|
raise NotImplementedError
|
|
215
221
|
|
|
216
222
|
@abstractmethod
|
|
@@ -223,7 +229,7 @@ class Broker(Protocol):
|
|
|
223
229
|
raise NotImplementedError
|
|
224
230
|
|
|
225
231
|
@abstractmethod
|
|
226
|
-
def unlock(self):
|
|
232
|
+
def unlock(self) -> Self:
|
|
227
233
|
raise NotImplementedError
|
|
228
234
|
|
|
229
235
|
|
|
@@ -232,7 +238,7 @@ Container
|
|
|
232
238
|
"""
|
|
233
239
|
|
|
234
240
|
|
|
235
|
-
class Mode(
|
|
241
|
+
class Mode(StrEnum):
|
|
236
242
|
FALLBACK = "fallback"
|
|
237
243
|
NORMAL = "normal"
|
|
238
244
|
OVERRIDE = "override"
|
|
@@ -242,36 +248,37 @@ class Mode(str, Enum):
|
|
|
242
248
|
return tuple(type(self)).index(self)
|
|
243
249
|
|
|
244
250
|
@classmethod
|
|
245
|
-
def get_default(cls):
|
|
251
|
+
def get_default(cls) -> Mode:
|
|
246
252
|
return cls.NORMAL
|
|
247
253
|
|
|
248
254
|
|
|
249
|
-
ModeStr = Literal["fallback", "normal", "override"]
|
|
255
|
+
type ModeStr = Literal["fallback", "normal", "override"]
|
|
250
256
|
|
|
251
257
|
|
|
252
|
-
class Record(NamedTuple):
|
|
253
|
-
injectable: Injectable
|
|
258
|
+
class Record[T](NamedTuple):
|
|
259
|
+
injectable: Injectable[T]
|
|
254
260
|
mode: Mode
|
|
255
261
|
|
|
256
262
|
|
|
257
263
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
258
264
|
class Container(Broker):
|
|
259
|
-
__records: dict[
|
|
265
|
+
__records: dict[TypeReport, Record] = field(default_factory=dict, init=False)
|
|
260
266
|
__channel: EventChannel = field(default_factory=EventChannel, init=False)
|
|
261
267
|
|
|
262
|
-
def __getitem__(self, cls: type[
|
|
263
|
-
for
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
|
|
269
|
+
for report in analyze_types(cls):
|
|
270
|
+
for scoped_report in dict.fromkeys((report, report.no_args)):
|
|
271
|
+
try:
|
|
272
|
+
injectable, _ = self.__records[scoped_report]
|
|
273
|
+
except KeyError:
|
|
274
|
+
continue
|
|
268
275
|
|
|
269
|
-
|
|
276
|
+
return injectable
|
|
270
277
|
|
|
271
278
|
raise NoInjectable(cls)
|
|
272
279
|
|
|
273
280
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
274
|
-
return any(
|
|
281
|
+
return any(report in self.__records for report in analyze_types(cls))
|
|
275
282
|
|
|
276
283
|
@property
|
|
277
284
|
def is_locked(self) -> bool:
|
|
@@ -282,16 +289,16 @@ class Container(Broker):
|
|
|
282
289
|
return frozenset(injectable for injectable, _ in self.__records.values())
|
|
283
290
|
|
|
284
291
|
@synchronized()
|
|
285
|
-
def update(
|
|
292
|
+
def update[T](
|
|
286
293
|
self,
|
|
287
|
-
classes: Iterable[type | UnionType],
|
|
288
|
-
injectable: Injectable,
|
|
294
|
+
classes: Iterable[type[T] | UnionType],
|
|
295
|
+
injectable: Injectable[T],
|
|
289
296
|
mode: Mode | ModeStr,
|
|
290
|
-
):
|
|
297
|
+
) -> Self:
|
|
291
298
|
mode = Mode(mode)
|
|
292
299
|
records = {
|
|
293
|
-
|
|
294
|
-
for
|
|
300
|
+
report: Record(injectable, mode)
|
|
301
|
+
for report in self.__prepare_reports_for_updating(classes, mode)
|
|
295
302
|
}
|
|
296
303
|
|
|
297
304
|
if records:
|
|
@@ -303,43 +310,43 @@ class Container(Broker):
|
|
|
303
310
|
return self
|
|
304
311
|
|
|
305
312
|
@synchronized()
|
|
306
|
-
def unlock(self):
|
|
313
|
+
def unlock(self) -> Self:
|
|
307
314
|
for injectable in self.__injectables:
|
|
308
315
|
injectable.unlock()
|
|
309
316
|
|
|
310
317
|
return self
|
|
311
318
|
|
|
312
|
-
def add_listener(self, listener: EventListener):
|
|
319
|
+
def add_listener(self, listener: EventListener) -> Self:
|
|
313
320
|
self.__channel.add_listener(listener)
|
|
314
321
|
return self
|
|
315
322
|
|
|
316
|
-
def notify(self, event: Event):
|
|
323
|
+
def notify(self, event: Event) -> ContextManager:
|
|
317
324
|
return self.__channel.dispatch(event)
|
|
318
325
|
|
|
319
|
-
def
|
|
326
|
+
def __prepare_reports_for_updating(
|
|
320
327
|
self,
|
|
321
328
|
classes: Iterable[type | UnionType],
|
|
322
329
|
mode: Mode,
|
|
323
|
-
) -> Iterator[
|
|
330
|
+
) -> Iterator[TypeReport]:
|
|
324
331
|
rank = mode.rank
|
|
325
332
|
|
|
326
|
-
for
|
|
333
|
+
for report in frozenset(analyze_types(*classes)):
|
|
327
334
|
try:
|
|
328
|
-
_, current_mode = self.__records[
|
|
335
|
+
_, current_mode = self.__records[report]
|
|
329
336
|
|
|
330
337
|
except KeyError:
|
|
331
338
|
pass
|
|
332
339
|
|
|
333
340
|
else:
|
|
334
|
-
if mode == current_mode:
|
|
341
|
+
if mode == current_mode and mode != Mode.OVERRIDE:
|
|
335
342
|
raise RuntimeError(
|
|
336
|
-
f"An injectable already exists for the class `{
|
|
343
|
+
f"An injectable already exists for the class `{report.cls}`."
|
|
337
344
|
)
|
|
338
345
|
|
|
339
346
|
elif rank < current_mode.rank:
|
|
340
347
|
continue
|
|
341
348
|
|
|
342
|
-
yield
|
|
349
|
+
yield report
|
|
343
350
|
|
|
344
351
|
|
|
345
352
|
"""
|
|
@@ -347,16 +354,18 @@ Module
|
|
|
347
354
|
"""
|
|
348
355
|
|
|
349
356
|
|
|
350
|
-
class Priority(
|
|
357
|
+
class Priority(StrEnum):
|
|
351
358
|
LOW = "low"
|
|
352
359
|
HIGH = "high"
|
|
353
360
|
|
|
354
361
|
@classmethod
|
|
355
|
-
def get_default(cls):
|
|
362
|
+
def get_default(cls) -> Priority:
|
|
356
363
|
return cls.LOW
|
|
357
364
|
|
|
358
365
|
|
|
359
|
-
PriorityStr = Literal["low", "high"]
|
|
366
|
+
type PriorityStr = Literal["low", "high"]
|
|
367
|
+
|
|
368
|
+
type InjectableFactory[T] = Callable[[Callable[..., T]], Injectable[T]]
|
|
360
369
|
|
|
361
370
|
|
|
362
371
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
@@ -372,14 +381,14 @@ class Module(EventListener, Broker):
|
|
|
372
381
|
def __post_init__(self):
|
|
373
382
|
self.__container.add_listener(self)
|
|
374
383
|
|
|
375
|
-
def __getitem__(self, cls: type[
|
|
384
|
+
def __getitem__[T](self, cls: type[T] | UnionType, /) -> Injectable[T]:
|
|
376
385
|
for broker in self.__brokers:
|
|
377
386
|
with suppress(KeyError):
|
|
378
387
|
return broker[cls]
|
|
379
388
|
|
|
380
389
|
raise NoInjectable(cls)
|
|
381
390
|
|
|
382
|
-
def __setitem__(self, cls: type | UnionType, injectable: Injectable, /):
|
|
391
|
+
def __setitem__[T](self, cls: type[T] | UnionType, injectable: Injectable[T], /):
|
|
383
392
|
self.update((cls,), injectable)
|
|
384
393
|
|
|
385
394
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
@@ -397,20 +406,20 @@ class Module(EventListener, Broker):
|
|
|
397
406
|
yield from tuple(self.__modules)
|
|
398
407
|
yield self.__container
|
|
399
408
|
|
|
400
|
-
def injectable(
|
|
409
|
+
def injectable[T](
|
|
401
410
|
self,
|
|
402
|
-
wrapped: Callable[...,
|
|
411
|
+
wrapped: Callable[..., T] = None,
|
|
403
412
|
/,
|
|
404
413
|
*,
|
|
405
|
-
cls:
|
|
414
|
+
cls: InjectableFactory[T] = NewInjectable,
|
|
406
415
|
inject: bool = True,
|
|
407
|
-
on:
|
|
416
|
+
on: TypeInfo[T] = (),
|
|
408
417
|
mode: Mode | ModeStr = Mode.get_default(),
|
|
409
418
|
):
|
|
410
419
|
def decorator(wp):
|
|
411
420
|
factory = self.inject(wp, return_factory=True) if inject else wp
|
|
412
421
|
injectable = cls(factory)
|
|
413
|
-
classes =
|
|
422
|
+
classes = get_return_types(wp, on)
|
|
414
423
|
self.update(classes, injectable, mode)
|
|
415
424
|
return wp
|
|
416
425
|
|
|
@@ -429,18 +438,18 @@ class Module(EventListener, Broker):
|
|
|
429
438
|
|
|
430
439
|
return decorator(wrapped) if wrapped else decorator
|
|
431
440
|
|
|
432
|
-
def set_constant(
|
|
441
|
+
def set_constant[T](
|
|
433
442
|
self,
|
|
434
|
-
instance:
|
|
435
|
-
on:
|
|
443
|
+
instance: T,
|
|
444
|
+
on: TypeInfo[T] = (),
|
|
436
445
|
*,
|
|
437
446
|
mode: Mode | ModeStr = Mode.get_default(),
|
|
438
|
-
) ->
|
|
447
|
+
) -> T:
|
|
439
448
|
cls = type(instance)
|
|
440
449
|
self.injectable(
|
|
441
450
|
lambda: instance,
|
|
442
451
|
inject=False,
|
|
443
|
-
on=(cls, on),
|
|
452
|
+
on=(cls, on),
|
|
444
453
|
mode=mode,
|
|
445
454
|
)
|
|
446
455
|
return instance
|
|
@@ -468,22 +477,22 @@ class Module(EventListener, Broker):
|
|
|
468
477
|
|
|
469
478
|
return decorator(wrapped) if wrapped else decorator
|
|
470
479
|
|
|
471
|
-
def resolve(self, cls: type[
|
|
480
|
+
def resolve[T](self, cls: type[T]) -> T:
|
|
472
481
|
injectable = self[cls]
|
|
473
482
|
return injectable.get_instance()
|
|
474
483
|
|
|
475
|
-
def get_instance(self, cls: type[
|
|
484
|
+
def get_instance[T](self, cls: type[T]) -> T | None:
|
|
476
485
|
try:
|
|
477
486
|
return self.resolve(cls)
|
|
478
487
|
except KeyError:
|
|
479
488
|
return None
|
|
480
489
|
|
|
481
|
-
def get_lazy_instance(
|
|
490
|
+
def get_lazy_instance[T](
|
|
482
491
|
self,
|
|
483
|
-
cls: type[
|
|
492
|
+
cls: type[T],
|
|
484
493
|
*,
|
|
485
494
|
cache: bool = False,
|
|
486
|
-
) -> Invertible[
|
|
495
|
+
) -> Invertible[T | None]:
|
|
487
496
|
if cache:
|
|
488
497
|
return Lazy(lambda: self.get_instance(cls))
|
|
489
498
|
|
|
@@ -491,21 +500,30 @@ class Module(EventListener, Broker):
|
|
|
491
500
|
function.set_owner(cls)
|
|
492
501
|
return SimpleInvertible(function)
|
|
493
502
|
|
|
494
|
-
def update(
|
|
503
|
+
def update[T](
|
|
495
504
|
self,
|
|
496
|
-
classes: Iterable[type | UnionType],
|
|
497
|
-
injectable: Injectable,
|
|
505
|
+
classes: Iterable[type[T] | UnionType],
|
|
506
|
+
injectable: Injectable[T],
|
|
498
507
|
mode: Mode | ModeStr = Mode.get_default(),
|
|
499
|
-
):
|
|
508
|
+
) -> Self:
|
|
500
509
|
self.__container.update(classes, injectable, mode)
|
|
501
510
|
return self
|
|
502
511
|
|
|
512
|
+
def init_modules(self, *modules: Module) -> Self:
|
|
513
|
+
for module in tuple(self.__modules):
|
|
514
|
+
self.stop_using(module)
|
|
515
|
+
|
|
516
|
+
for module in modules:
|
|
517
|
+
self.use(module)
|
|
518
|
+
|
|
519
|
+
return self
|
|
520
|
+
|
|
503
521
|
def use(
|
|
504
522
|
self,
|
|
505
523
|
module: Module,
|
|
506
524
|
*,
|
|
507
525
|
priority: Priority | PriorityStr = Priority.get_default(),
|
|
508
|
-
):
|
|
526
|
+
) -> Self:
|
|
509
527
|
if module is self:
|
|
510
528
|
raise ModuleError("Module can't be used by itself.")
|
|
511
529
|
|
|
@@ -522,7 +540,7 @@ class Module(EventListener, Broker):
|
|
|
522
540
|
|
|
523
541
|
return self
|
|
524
542
|
|
|
525
|
-
def stop_using(self, module: Module):
|
|
543
|
+
def stop_using(self, module: Module) -> Self:
|
|
526
544
|
event = ModuleRemoved(self, module)
|
|
527
545
|
|
|
528
546
|
with suppress(KeyError):
|
|
@@ -543,7 +561,7 @@ class Module(EventListener, Broker):
|
|
|
543
561
|
yield
|
|
544
562
|
self.stop_using(module)
|
|
545
563
|
|
|
546
|
-
def change_priority(self, module: Module, priority: Priority | PriorityStr):
|
|
564
|
+
def change_priority(self, module: Module, priority: Priority | PriorityStr) -> Self:
|
|
547
565
|
priority = Priority(priority)
|
|
548
566
|
event = ModulePriorityUpdated(self, module, priority)
|
|
549
567
|
|
|
@@ -553,17 +571,17 @@ class Module(EventListener, Broker):
|
|
|
553
571
|
return self
|
|
554
572
|
|
|
555
573
|
@synchronized()
|
|
556
|
-
def unlock(self):
|
|
574
|
+
def unlock(self) -> Self:
|
|
557
575
|
for broker in self.__brokers:
|
|
558
576
|
broker.unlock()
|
|
559
577
|
|
|
560
578
|
return self
|
|
561
579
|
|
|
562
|
-
def add_listener(self, listener: EventListener):
|
|
580
|
+
def add_listener(self, listener: EventListener) -> Self:
|
|
563
581
|
self.__channel.add_listener(listener)
|
|
564
582
|
return self
|
|
565
583
|
|
|
566
|
-
def remove_listener(self, listener: EventListener):
|
|
584
|
+
def remove_listener(self, listener: EventListener) -> Self:
|
|
567
585
|
self.__channel.remove_listener(listener)
|
|
568
586
|
return self
|
|
569
587
|
|
|
@@ -622,15 +640,15 @@ class Dependencies:
|
|
|
622
640
|
return OrderedDict(self)
|
|
623
641
|
|
|
624
642
|
@classmethod
|
|
625
|
-
def from_mapping(cls, mapping: Mapping[str, Injectable]):
|
|
643
|
+
def from_mapping(cls, mapping: Mapping[str, Injectable]) -> Self:
|
|
626
644
|
return cls(mapping=mapping)
|
|
627
645
|
|
|
628
646
|
@classmethod
|
|
629
|
-
def empty(cls):
|
|
647
|
+
def empty(cls) -> Self:
|
|
630
648
|
return cls.from_mapping({})
|
|
631
649
|
|
|
632
650
|
@classmethod
|
|
633
|
-
def resolve(cls, signature: Signature, module: Module, owner: type = None):
|
|
651
|
+
def resolve(cls, signature: Signature, module: Module, owner: type = None) -> Self:
|
|
634
652
|
dependencies = LazyMapping(cls.__resolver(signature, module, owner))
|
|
635
653
|
return cls.from_mapping(dependencies)
|
|
636
654
|
|
|
@@ -684,8 +702,10 @@ class InjectedFunction(EventListener):
|
|
|
684
702
|
update_wrapper(self, wrapped, updated=())
|
|
685
703
|
self.__dependencies = Dependencies.empty()
|
|
686
704
|
self.__owner = None
|
|
687
|
-
|
|
688
|
-
|
|
705
|
+
|
|
706
|
+
queue = Queue[Callable[[], Any]]()
|
|
707
|
+
queue.put_nowait(self.__set_signature)
|
|
708
|
+
self.__setup_queue = queue
|
|
689
709
|
|
|
690
710
|
def __repr__(self) -> str: # pragma: no cover
|
|
691
711
|
return repr(self.wrapped)
|
|
@@ -694,13 +714,17 @@ class InjectedFunction(EventListener):
|
|
|
694
714
|
return str(self.wrapped)
|
|
695
715
|
|
|
696
716
|
def __call__(self, /, *args, **kwargs) -> Any:
|
|
697
|
-
|
|
698
|
-
|
|
717
|
+
queue = self.__setup_queue
|
|
718
|
+
|
|
719
|
+
while not queue.empty():
|
|
720
|
+
setup = queue.get()
|
|
721
|
+
setup()
|
|
722
|
+
queue.task_done()
|
|
699
723
|
|
|
700
724
|
arguments = self.bind(args, kwargs)
|
|
701
725
|
return self.wrapped(*arguments.args, **arguments.kwargs)
|
|
702
726
|
|
|
703
|
-
def __get__(self, instance: object = None, owner: type = None):
|
|
727
|
+
def __get__(self, instance: object = None, owner: type = None) -> Self | MethodType:
|
|
704
728
|
if instance is None:
|
|
705
729
|
return self
|
|
706
730
|
|
|
@@ -734,7 +758,7 @@ class InjectedFunction(EventListener):
|
|
|
734
758
|
)
|
|
735
759
|
return Arguments(bound.args, bound.kwargs)
|
|
736
760
|
|
|
737
|
-
def set_owner(self, owner: type):
|
|
761
|
+
def set_owner(self, owner: type) -> Self:
|
|
738
762
|
if self.__dependencies.are_resolved:
|
|
739
763
|
raise TypeError(
|
|
740
764
|
"Function owner must be assigned before dependencies are resolved."
|
|
@@ -747,19 +771,19 @@ class InjectedFunction(EventListener):
|
|
|
747
771
|
return self
|
|
748
772
|
|
|
749
773
|
@synchronized()
|
|
750
|
-
def update(self, module: Module):
|
|
774
|
+
def update(self, module: Module) -> Self:
|
|
751
775
|
self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
|
|
752
776
|
return self
|
|
753
777
|
|
|
754
778
|
def on_setup(self, wrapped: Callable[[], Any] = None, /):
|
|
755
779
|
def decorator(wp):
|
|
756
|
-
self.__setup_queue.
|
|
780
|
+
self.__setup_queue.put(wp)
|
|
757
781
|
return wp
|
|
758
782
|
|
|
759
783
|
return decorator(wrapped) if wrapped else decorator
|
|
760
784
|
|
|
761
785
|
@singledispatchmethod
|
|
762
|
-
def on_event(self, event: Event, /) ->
|
|
786
|
+
def on_event(self, event: Event, /) -> None: # type: ignore
|
|
763
787
|
return None
|
|
764
788
|
|
|
765
789
|
@on_event.register
|
|
@@ -768,7 +792,7 @@ class InjectedFunction(EventListener):
|
|
|
768
792
|
yield
|
|
769
793
|
self.update(event.on_module)
|
|
770
794
|
|
|
771
|
-
def __set_signature(self):
|
|
795
|
+
def __set_signature(self) -> Self:
|
|
772
796
|
self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
|
|
773
797
|
return self
|
|
774
798
|
|
|
@@ -781,12 +805,15 @@ class InjectedFunction(EventListener):
|
|
|
781
805
|
self.__update_vars(variables)
|
|
782
806
|
|
|
783
807
|
def __update_vars(self, variables: Mapping[str, Any]):
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
restricted_vars = frozenset(var for var in dir(self) if not is_dunder(var))
|
|
808
|
+
restricted_vars = frozenset(
|
|
809
|
+
var for var in dir(self) if not self.__is_dunder(var)
|
|
810
|
+
)
|
|
788
811
|
vars(self).update(
|
|
789
812
|
(var, value)
|
|
790
813
|
for var, value in variables.items()
|
|
791
814
|
if var not in restricted_vars
|
|
792
815
|
)
|
|
816
|
+
|
|
817
|
+
@staticmethod
|
|
818
|
+
def __is_dunder(var: str) -> bool:
|
|
819
|
+
return var.startswith("__") and var.endswith("__")
|
injection/exceptions.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from injection.common.tools.type import format_type
|
|
4
|
-
|
|
5
3
|
__all__ = (
|
|
6
4
|
"InjectionError",
|
|
7
5
|
"NoInjectable",
|
|
@@ -15,15 +13,15 @@ class InjectionError(Exception):
|
|
|
15
13
|
pass
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
class NoInjectable(KeyError, InjectionError):
|
|
16
|
+
class NoInjectable[T](KeyError, InjectionError):
|
|
19
17
|
__slots__ = ("__class",)
|
|
20
18
|
|
|
21
|
-
def __init__(self, cls: type | Any):
|
|
22
|
-
super().__init__(f"No injectable for `{
|
|
19
|
+
def __init__(self, cls: type[T] | Any):
|
|
20
|
+
super().__init__(f"No injectable for `{cls}`.")
|
|
23
21
|
self.__class = cls
|
|
24
22
|
|
|
25
23
|
@property
|
|
26
|
-
def cls(self) -> type:
|
|
24
|
+
def cls(self) -> type[T]:
|
|
27
25
|
return self.__class
|
|
28
26
|
|
|
29
27
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from rodi import ContainerProtocol
|
|
4
4
|
|
|
@@ -6,8 +6,6 @@ from injection import Module, default_module
|
|
|
6
6
|
|
|
7
7
|
__all__ = ("InjectionServices",)
|
|
8
8
|
|
|
9
|
-
_T = TypeVar("_T")
|
|
10
|
-
|
|
11
9
|
|
|
12
10
|
class InjectionServices(ContainerProtocol):
|
|
13
11
|
"""
|
|
@@ -26,5 +24,5 @@ class InjectionServices(ContainerProtocol):
|
|
|
26
24
|
self.__module.injectable(obj_type)
|
|
27
25
|
return self
|
|
28
26
|
|
|
29
|
-
def resolve(self, obj_type: type[
|
|
27
|
+
def resolve[T](self, obj_type: type[T] | Any, *args, **kwargs) -> T:
|
|
30
28
|
return self.__module.resolve(obj_type)
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
|
5
5
|
Home-page: https://github.com/100nm/python-injection
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: dependencies,inject,injection
|
|
8
8
|
Author: remimd
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.12,<4
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
13
|
Project-URL: Repository, https://github.com/100nm/python-injection
|
|
16
14
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
injection/__init__.py,sha256=Bf6S99E2srD3752xlJf3uAdiGIzY2YHOZafcwEiwY70,739
|
|
2
|
+
injection/__init__.pyi,sha256=HEk1HXAmwtq2ZEFLf5A7VWhdpPVWVQpjsLip9JzepEY,6023
|
|
3
|
+
injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
injection/common/event.py,sha256=5Rdb2m3vAMCic8cQAVkStJDbrDrW_lk6kav8wYwmexM,1283
|
|
5
|
+
injection/common/invertible.py,sha256=noNcmJ96IQi0XJms0dyfrx_AvKZnQM0sZyxhc2l6qo0,527
|
|
6
|
+
injection/common/lazy.py,sha256=FEas6ewwOGWvRR8cflmyVvSLZd_-Fxd0QeIdvAM5I9c,1313
|
|
7
|
+
injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
injection/common/tools/threading.py,sha256=Urcy59Rp34psRWmzzQyNDu4Zm-DuCkPsp4qO1x_W4qQ,165
|
|
9
|
+
injection/common/tools/type.py,sha256=5Ixswd9-sSqJdmh9lTd3jADFHWgD0wZSwecVi1opeO0,1715
|
|
10
|
+
injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
|
|
11
|
+
injection/core/module.py,sha256=nCa10YQu-o-W0jMt-EnjCuXc1VioqN5CZXK2EoS4zgo,21241
|
|
12
|
+
injection/exceptions.py,sha256=RsWWiWwKSMU0vxXQqQSn6CKHLMrGu4SSzYUAy9OJRXk,626
|
|
13
|
+
injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
injection/integrations/blacksheep.py,sha256=GQBZCyl_DNDuCJdV56EYaiYCXGDMmP66cEahRzUZpmc,737
|
|
15
|
+
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
17
|
+
python_injection-0.9.0.dist-info/METADATA,sha256=j_ikavu9sPJ2xkKcX6Vr8MmhoNayLEiVNLPQ99TzWNY,3570
|
|
18
|
+
python_injection-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
19
|
+
python_injection-0.9.0.dist-info/RECORD,,
|
injection/common/queue.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
|
-
from collections import deque
|
|
3
|
-
from collections.abc import Iterator
|
|
4
|
-
from dataclasses import dataclass, field
|
|
5
|
-
from typing import NoReturn, Protocol, TypeVar
|
|
6
|
-
|
|
7
|
-
__all__ = ("LimitedQueue",)
|
|
8
|
-
|
|
9
|
-
_T = TypeVar("_T")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Queue(Iterator[_T], Protocol):
|
|
13
|
-
__slots__ = ()
|
|
14
|
-
|
|
15
|
-
@abstractmethod
|
|
16
|
-
def add(self, item: _T):
|
|
17
|
-
raise NotImplementedError
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@dataclass(repr=False, frozen=True, slots=True)
|
|
21
|
-
class SimpleQueue(Queue[_T]):
|
|
22
|
-
__items: deque[_T] = field(default_factory=deque, init=False)
|
|
23
|
-
|
|
24
|
-
def __next__(self) -> _T:
|
|
25
|
-
try:
|
|
26
|
-
return self.__items.popleft()
|
|
27
|
-
except IndexError as exc:
|
|
28
|
-
raise StopIteration from exc
|
|
29
|
-
|
|
30
|
-
def add(self, item: _T):
|
|
31
|
-
self.__items.append(item)
|
|
32
|
-
return self
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class DeadQueue(Queue[_T]):
|
|
36
|
-
__slots__ = ()
|
|
37
|
-
|
|
38
|
-
def __bool__(self) -> bool:
|
|
39
|
-
return False
|
|
40
|
-
|
|
41
|
-
def __next__(self) -> NoReturn:
|
|
42
|
-
raise StopIteration
|
|
43
|
-
|
|
44
|
-
def add(self, item: _T) -> NoReturn:
|
|
45
|
-
raise TypeError("Queue is dead.")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@dataclass(repr=False, slots=True)
|
|
49
|
-
class LimitedQueue(Queue[_T]):
|
|
50
|
-
__state: Queue[_T] = field(default_factory=SimpleQueue)
|
|
51
|
-
|
|
52
|
-
def __next__(self) -> _T:
|
|
53
|
-
try:
|
|
54
|
-
return next(self.__state)
|
|
55
|
-
except StopIteration as exc:
|
|
56
|
-
if self.__state:
|
|
57
|
-
self.__state = DeadQueue()
|
|
58
|
-
|
|
59
|
-
raise exc
|
|
60
|
-
|
|
61
|
-
def add(self, item: _T):
|
|
62
|
-
self.__state.add(item)
|
|
63
|
-
return self
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
injection/__init__.py,sha256=Bf6S99E2srD3752xlJf3uAdiGIzY2YHOZafcwEiwY70,739
|
|
2
|
-
injection/__init__.pyi,sha256=fNTW5TUZQmaxPooaa_vJ_nyR_-DqZ13hSWHh_TsdeOo,5913
|
|
3
|
-
injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
injection/common/event.py,sha256=TvkFv-5zF_oUTPhh5U0BKD5HanvCJKHA0H7yceMRy5c,1261
|
|
5
|
-
injection/common/invertible.py,sha256=BZnkDg_NZDsTXGca5w5Wg_nYE3oRO_Wlodi2XtE55ck,595
|
|
6
|
-
injection/common/lazy.py,sha256=1C34uoG229Gl0DEUcD9-eQrL4K_oIofOLzdQ1SiY6rw,1401
|
|
7
|
-
injection/common/queue.py,sha256=mV0AGxp5aYMr438MxmoIsZcV5jmqi5x_GD2S-utrnzA,1443
|
|
8
|
-
injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
injection/common/tools/threading.py,sha256=RAtzBFLVNJMflWIHxrP83fjafnFq8_JLgFoYQg8nVyE,182
|
|
10
|
-
injection/common/tools/type.py,sha256=05fD5UkUI1kPoFWEjQz4j266SYfJQMK-Ti88JXNxb_M,1276
|
|
11
|
-
injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
|
|
12
|
-
injection/core/module.py,sha256=jofZzYk_-1Rsfj-fDrPs-64RfVXvnMCWcKk2NSCsyTk,20507
|
|
13
|
-
injection/exceptions.py,sha256=f2lVSTAx-Nv89s0skn15y-sCkr656ROuWYs-XlrcEn8,683
|
|
14
|
-
injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
injection/integrations/blacksheep.py,sha256=dFXzrxkJRy9z44CcwG0ROYshT3zUZTVyuQkRy390RvU,765
|
|
16
|
-
injection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
18
|
-
python_injection-0.8.4.post2.dist-info/METADATA,sha256=V-_8VSQ0z_JX5t32nfkbXOuJA-9b7ykveITH1S54gHQ,3678
|
|
19
|
-
python_injection-0.8.4.post2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
20
|
-
python_injection-0.8.4.post2.dist-info/RECORD,,
|
|
File without changes
|