python-injection 0.8.2__py3-none-any.whl → 0.8.4__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__.py +4 -0
- injection/__init__.pyi +44 -17
- injection/common/event.py +2 -2
- injection/common/invertible.py +1 -1
- injection/common/tools/threading.py +2 -3
- injection/common/tools/type.py +1 -1
- injection/core/module.py +131 -97
- injection/exceptions.py +3 -1
- injection/integrations/blacksheep.py +9 -7
- {python_injection-0.8.2.dist-info → python_injection-0.8.4.dist-info}/METADATA +2 -2
- python_injection-0.8.4.dist-info/RECORD +19 -0
- python_injection-0.8.2.dist-info/RECORD +0 -19
- {python_injection-0.8.2.dist-info → python_injection-0.8.4.dist-info}/WHEEL +0 -0
injection/__init__.py
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
from .core import Injectable, Module
|
|
2
|
+
from .core import Mode as InjectableMode
|
|
3
|
+
from .core import Priority as ModulePriority
|
|
2
4
|
|
|
3
5
|
__all__ = (
|
|
4
6
|
"Injectable",
|
|
7
|
+
"InjectableMode",
|
|
5
8
|
"Module",
|
|
9
|
+
"ModulePriority",
|
|
6
10
|
"default_module",
|
|
7
11
|
"get_instance",
|
|
8
12
|
"get_lazy_instance",
|
injection/__init__.pyi
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from collections.abc import Callable, Iterable
|
|
3
3
|
from contextlib import ContextDecorator
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from types import UnionType
|
|
5
6
|
from typing import (
|
|
6
7
|
Any,
|
|
@@ -15,7 +16,8 @@ from typing import (
|
|
|
15
16
|
|
|
16
17
|
from .common.invertible import Invertible
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
_In_T = TypeVar("_In_T", covariant=False)
|
|
20
|
+
_Co_T = TypeVar("_Co_T", covariant=True)
|
|
19
21
|
|
|
20
22
|
default_module: Final[Module] = ...
|
|
21
23
|
|
|
@@ -54,7 +56,7 @@ class Module:
|
|
|
54
56
|
cls: type[Injectable] = ...,
|
|
55
57
|
inject: bool = ...,
|
|
56
58
|
on: type | Iterable[type] | UnionType = ...,
|
|
57
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
59
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
58
60
|
):
|
|
59
61
|
"""
|
|
60
62
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -69,7 +71,7 @@ class Module:
|
|
|
69
71
|
*,
|
|
70
72
|
inject: bool = ...,
|
|
71
73
|
on: type | Iterable[type] | UnionType = ...,
|
|
72
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
74
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
73
75
|
):
|
|
74
76
|
"""
|
|
75
77
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -86,30 +88,35 @@ class Module:
|
|
|
86
88
|
|
|
87
89
|
def set_constant(
|
|
88
90
|
self,
|
|
89
|
-
instance:
|
|
91
|
+
instance: _In_T,
|
|
90
92
|
on: type | Iterable[type] | UnionType = ...,
|
|
91
93
|
*,
|
|
92
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
93
|
-
) ->
|
|
94
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
95
|
+
) -> _In_T:
|
|
94
96
|
"""
|
|
95
97
|
Function for registering a specific instance to be injected. This is useful for
|
|
96
98
|
registering global variables. The difference with the singleton decorator is
|
|
97
99
|
that no dependencies are resolved, so the module doesn't need to be locked.
|
|
98
100
|
"""
|
|
99
101
|
|
|
100
|
-
def
|
|
102
|
+
def resolve(self, cls: type[_In_T]) -> _In_T:
|
|
101
103
|
"""
|
|
102
104
|
Function used to retrieve an instance associated with the type passed in
|
|
103
|
-
parameter or
|
|
104
|
-
|
|
105
|
+
parameter or an exception will be raised.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def get_instance(self, cls: type[_In_T]) -> _In_T | None:
|
|
109
|
+
"""
|
|
110
|
+
Function used to retrieve an instance associated with the type passed in
|
|
111
|
+
parameter or return `None`.
|
|
105
112
|
"""
|
|
106
113
|
|
|
107
114
|
def get_lazy_instance(
|
|
108
115
|
self,
|
|
109
|
-
cls: type[
|
|
116
|
+
cls: type[_In_T],
|
|
110
117
|
*,
|
|
111
118
|
cache: bool = ...,
|
|
112
|
-
) -> Invertible[
|
|
119
|
+
) -> Invertible[_In_T | None]:
|
|
113
120
|
"""
|
|
114
121
|
Function used to retrieve an instance associated with the type passed in
|
|
115
122
|
parameter or `None`. Return a `Invertible` object. To access the instance
|
|
@@ -119,7 +126,12 @@ class Module:
|
|
|
119
126
|
Example: instance = ~lazy_instance
|
|
120
127
|
"""
|
|
121
128
|
|
|
122
|
-
def use(
|
|
129
|
+
def use(
|
|
130
|
+
self,
|
|
131
|
+
module: Module,
|
|
132
|
+
*,
|
|
133
|
+
priority: ModulePriority | Literal["low", "high"] = ...,
|
|
134
|
+
):
|
|
123
135
|
"""
|
|
124
136
|
Function for using another module. Using another module replaces the module's
|
|
125
137
|
dependencies with those of the module used. If the dependency is not found, it
|
|
@@ -135,13 +147,17 @@ class Module:
|
|
|
135
147
|
self,
|
|
136
148
|
module: Module,
|
|
137
149
|
*,
|
|
138
|
-
priority: Literal["low", "high"] = ...,
|
|
150
|
+
priority: ModulePriority | Literal["low", "high"] = ...,
|
|
139
151
|
) -> ContextManager | ContextDecorator:
|
|
140
152
|
"""
|
|
141
153
|
Context manager or decorator for temporary use of a module.
|
|
142
154
|
"""
|
|
143
155
|
|
|
144
|
-
def change_priority(
|
|
156
|
+
def change_priority(
|
|
157
|
+
self,
|
|
158
|
+
module: Module,
|
|
159
|
+
priority: ModulePriority | Literal["low", "high"],
|
|
160
|
+
):
|
|
145
161
|
"""
|
|
146
162
|
Function for changing the priority of a module in use.
|
|
147
163
|
There are two priority values:
|
|
@@ -155,11 +171,22 @@ class Module:
|
|
|
155
171
|
Function to unlock the module by deleting cached instances of singletons.
|
|
156
172
|
"""
|
|
157
173
|
|
|
174
|
+
@final
|
|
175
|
+
class ModulePriority(str, Enum):
|
|
176
|
+
LOW = ...
|
|
177
|
+
HIGH = ...
|
|
178
|
+
|
|
158
179
|
@runtime_checkable
|
|
159
|
-
class Injectable(Protocol[
|
|
160
|
-
def __init__(self, factory: Callable[[],
|
|
180
|
+
class Injectable(Protocol[_Co_T]):
|
|
181
|
+
def __init__(self, factory: Callable[[], _Co_T] = ..., /): ...
|
|
161
182
|
@property
|
|
162
183
|
def is_locked(self) -> bool: ...
|
|
163
184
|
def unlock(self): ...
|
|
164
185
|
@abstractmethod
|
|
165
|
-
def get_instance(self) ->
|
|
186
|
+
def get_instance(self) -> _Co_T: ...
|
|
187
|
+
|
|
188
|
+
@final
|
|
189
|
+
class InjectableMode(str, Enum):
|
|
190
|
+
FALLBACK = ...
|
|
191
|
+
NORMAL = ...
|
|
192
|
+
OVERRIDE = ...
|
injection/common/event.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from contextlib import
|
|
2
|
+
from contextlib import ExitStack, contextmanager, suppress
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from typing import ContextManager
|
|
5
5
|
from weakref import WeakSet
|
|
@@ -24,7 +24,7 @@ class EventChannel:
|
|
|
24
24
|
__listeners: WeakSet[EventListener] = field(default_factory=WeakSet, init=False)
|
|
25
25
|
|
|
26
26
|
@contextmanager
|
|
27
|
-
def dispatch(self, event: Event)
|
|
27
|
+
def dispatch(self, event: Event):
|
|
28
28
|
with ExitStack() as stack:
|
|
29
29
|
for listener in tuple(self.__listeners):
|
|
30
30
|
context_manager = listener.on_event(event)
|
injection/common/invertible.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from contextlib import
|
|
1
|
+
from contextlib import contextmanager
|
|
2
2
|
from threading import RLock
|
|
3
|
-
from typing import ContextManager
|
|
4
3
|
|
|
5
4
|
__all__ = ("synchronized",)
|
|
6
5
|
|
|
@@ -8,6 +7,6 @@ __lock = RLock()
|
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
@contextmanager
|
|
11
|
-
def synchronized()
|
|
10
|
+
def synchronized():
|
|
12
11
|
with __lock:
|
|
13
12
|
yield
|
injection/common/tools/type.py
CHANGED
injection/core/module.py
CHANGED
|
@@ -12,8 +12,9 @@ from collections.abc import (
|
|
|
12
12
|
Mapping,
|
|
13
13
|
MutableMapping,
|
|
14
14
|
)
|
|
15
|
-
from contextlib import
|
|
15
|
+
from contextlib import contextmanager, suppress
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
|
+
from enum import Enum
|
|
17
18
|
from functools import partialmethod, singledispatchmethod, update_wrapper
|
|
18
19
|
from inspect import Signature, isclass
|
|
19
20
|
from types import MethodType, UnionType
|
|
@@ -26,8 +27,6 @@ from typing import (
|
|
|
26
27
|
NoReturn,
|
|
27
28
|
Protocol,
|
|
28
29
|
TypeVar,
|
|
29
|
-
cast,
|
|
30
|
-
get_args,
|
|
31
30
|
runtime_checkable,
|
|
32
31
|
)
|
|
33
32
|
|
|
@@ -45,21 +44,12 @@ from injection.exceptions import (
|
|
|
45
44
|
NoInjectable,
|
|
46
45
|
)
|
|
47
46
|
|
|
48
|
-
__all__ = ("Injectable", "Module")
|
|
47
|
+
__all__ = ("Injectable", "Mode", "Module", "Priority")
|
|
49
48
|
|
|
50
49
|
_logger = logging.getLogger(__name__)
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"""
|
|
57
|
-
Literal types
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
_Mode = Literal["fallback", "normal", "override"]
|
|
62
|
-
_Priority = Literal["low", "high"]
|
|
51
|
+
_In_T = TypeVar("_In_T", covariant=False)
|
|
52
|
+
_Co_T = TypeVar("_Co_T", covariant=True)
|
|
63
53
|
|
|
64
54
|
|
|
65
55
|
"""
|
|
@@ -75,7 +65,7 @@ class ContainerEvent(Event, ABC):
|
|
|
75
65
|
@dataclass(frozen=True, slots=True)
|
|
76
66
|
class ContainerDependenciesUpdated(ContainerEvent):
|
|
77
67
|
classes: Collection[type]
|
|
78
|
-
mode:
|
|
68
|
+
mode: Mode
|
|
79
69
|
|
|
80
70
|
def __str__(self) -> str:
|
|
81
71
|
length = len(self.classes)
|
|
@@ -113,6 +103,7 @@ class ModuleEventProxy(ModuleEvent):
|
|
|
113
103
|
@dataclass(frozen=True, slots=True)
|
|
114
104
|
class ModuleAdded(ModuleEvent):
|
|
115
105
|
module_added: Module
|
|
106
|
+
priority: Priority
|
|
116
107
|
|
|
117
108
|
def __str__(self) -> str:
|
|
118
109
|
return f"`{self.on_module}` now uses `{self.module_added}`."
|
|
@@ -129,7 +120,7 @@ class ModuleRemoved(ModuleEvent):
|
|
|
129
120
|
@dataclass(frozen=True, slots=True)
|
|
130
121
|
class ModulePriorityUpdated(ModuleEvent):
|
|
131
122
|
module_updated: Module
|
|
132
|
-
priority:
|
|
123
|
+
priority: Priority
|
|
133
124
|
|
|
134
125
|
def __str__(self) -> str:
|
|
135
126
|
return (
|
|
@@ -144,10 +135,10 @@ Injectables
|
|
|
144
135
|
|
|
145
136
|
|
|
146
137
|
@runtime_checkable
|
|
147
|
-
class Injectable(Protocol[
|
|
138
|
+
class Injectable(Protocol[_Co_T]):
|
|
148
139
|
__slots__ = ()
|
|
149
140
|
|
|
150
|
-
def __init__(self, __factory: Callable[[],
|
|
141
|
+
def __init__(self, __factory: Callable[[], _Co_T] = None, /):
|
|
151
142
|
pass
|
|
152
143
|
|
|
153
144
|
@property
|
|
@@ -155,26 +146,26 @@ class Injectable(Protocol[_T]):
|
|
|
155
146
|
return False
|
|
156
147
|
|
|
157
148
|
def unlock(self):
|
|
158
|
-
|
|
149
|
+
return
|
|
159
150
|
|
|
160
151
|
@abstractmethod
|
|
161
|
-
def get_instance(self) ->
|
|
152
|
+
def get_instance(self) -> _Co_T:
|
|
162
153
|
raise NotImplementedError
|
|
163
154
|
|
|
164
155
|
|
|
165
156
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
166
|
-
class BaseInjectable(Injectable[
|
|
167
|
-
factory: Callable[[],
|
|
157
|
+
class BaseInjectable(Injectable[_In_T], ABC):
|
|
158
|
+
factory: Callable[[], _In_T]
|
|
168
159
|
|
|
169
160
|
|
|
170
|
-
class NewInjectable(BaseInjectable[
|
|
161
|
+
class NewInjectable(BaseInjectable[_In_T]):
|
|
171
162
|
__slots__ = ()
|
|
172
163
|
|
|
173
|
-
def get_instance(self) ->
|
|
164
|
+
def get_instance(self) -> _In_T:
|
|
174
165
|
return self.factory()
|
|
175
166
|
|
|
176
167
|
|
|
177
|
-
class SingletonInjectable(BaseInjectable[
|
|
168
|
+
class SingletonInjectable(BaseInjectable[_In_T]):
|
|
178
169
|
__slots__ = ("__dict__",)
|
|
179
170
|
|
|
180
171
|
__INSTANCE_KEY: ClassVar[str] = "$instance"
|
|
@@ -190,7 +181,7 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
190
181
|
def unlock(self):
|
|
191
182
|
self.cache.clear()
|
|
192
183
|
|
|
193
|
-
def get_instance(self) ->
|
|
184
|
+
def get_instance(self) -> _In_T:
|
|
194
185
|
with suppress(KeyError):
|
|
195
186
|
return self.cache[self.__INSTANCE_KEY]
|
|
196
187
|
|
|
@@ -202,8 +193,8 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
202
193
|
|
|
203
194
|
|
|
204
195
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
205
|
-
class ShouldBeInjectable(Injectable[
|
|
206
|
-
cls: type[
|
|
196
|
+
class ShouldBeInjectable(Injectable[_In_T]):
|
|
197
|
+
cls: type[_In_T]
|
|
207
198
|
|
|
208
199
|
def get_instance(self) -> NoReturn:
|
|
209
200
|
raise InjectionError(f"`{format_type(self.cls)}` should be an injectable.")
|
|
@@ -219,7 +210,7 @@ class Broker(Protocol):
|
|
|
219
210
|
__slots__ = ()
|
|
220
211
|
|
|
221
212
|
@abstractmethod
|
|
222
|
-
def __getitem__(self, cls: type[
|
|
213
|
+
def __getitem__(self, cls: type[_In_T] | UnionType, /) -> Injectable[_In_T]:
|
|
223
214
|
raise NotImplementedError
|
|
224
215
|
|
|
225
216
|
@abstractmethod
|
|
@@ -241,20 +232,37 @@ Container
|
|
|
241
232
|
"""
|
|
242
233
|
|
|
243
234
|
|
|
235
|
+
class Mode(str, Enum):
|
|
236
|
+
FALLBACK = "fallback"
|
|
237
|
+
NORMAL = "normal"
|
|
238
|
+
OVERRIDE = "override"
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def rank(self) -> int:
|
|
242
|
+
return tuple(type(self)).index(self)
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def get_default(cls):
|
|
246
|
+
return cls.NORMAL
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
ModeStr = Literal["fallback", "normal", "override"]
|
|
250
|
+
|
|
251
|
+
|
|
244
252
|
class Record(NamedTuple):
|
|
245
253
|
injectable: Injectable
|
|
246
|
-
mode:
|
|
254
|
+
mode: Mode
|
|
247
255
|
|
|
248
256
|
|
|
249
257
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
250
258
|
class Container(Broker):
|
|
251
|
-
|
|
259
|
+
__records: dict[type, Record] = field(default_factory=dict, init=False)
|
|
252
260
|
__channel: EventChannel = field(default_factory=EventChannel, init=False)
|
|
253
261
|
|
|
254
|
-
def __getitem__(self, cls: type[
|
|
262
|
+
def __getitem__(self, cls: type[_In_T] | UnionType, /) -> Injectable[_In_T]:
|
|
255
263
|
for cls in get_origins(cls):
|
|
256
264
|
try:
|
|
257
|
-
injectable, _ = self.
|
|
265
|
+
injectable, _ = self.__records[cls]
|
|
258
266
|
except KeyError:
|
|
259
267
|
continue
|
|
260
268
|
|
|
@@ -263,7 +271,7 @@ class Container(Broker):
|
|
|
263
271
|
raise NoInjectable(cls)
|
|
264
272
|
|
|
265
273
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
266
|
-
return any(cls in self.
|
|
274
|
+
return any(cls in self.__records for cls in get_origins(cls))
|
|
267
275
|
|
|
268
276
|
@property
|
|
269
277
|
def is_locked(self) -> bool:
|
|
@@ -271,10 +279,16 @@ class Container(Broker):
|
|
|
271
279
|
|
|
272
280
|
@property
|
|
273
281
|
def __injectables(self) -> frozenset[Injectable]:
|
|
274
|
-
return frozenset(injectable for injectable, _ in self.
|
|
282
|
+
return frozenset(injectable for injectable, _ in self.__records.values())
|
|
275
283
|
|
|
276
284
|
@synchronized()
|
|
277
|
-
def update(
|
|
285
|
+
def update(
|
|
286
|
+
self,
|
|
287
|
+
classes: Iterable[type | UnionType],
|
|
288
|
+
injectable: Injectable,
|
|
289
|
+
mode: Mode | ModeStr,
|
|
290
|
+
):
|
|
291
|
+
mode = Mode(mode)
|
|
278
292
|
records = {
|
|
279
293
|
cls: Record(injectable, mode)
|
|
280
294
|
for cls in self.__filter_classes(classes, mode)
|
|
@@ -284,7 +298,7 @@ class Container(Broker):
|
|
|
284
298
|
event = ContainerDependenciesUpdated(self, records.keys(), mode)
|
|
285
299
|
|
|
286
300
|
with self.notify(event):
|
|
287
|
-
self.
|
|
301
|
+
self.__records.update(records)
|
|
288
302
|
|
|
289
303
|
return self
|
|
290
304
|
|
|
@@ -299,16 +313,19 @@ class Container(Broker):
|
|
|
299
313
|
self.__channel.add_listener(listener)
|
|
300
314
|
return self
|
|
301
315
|
|
|
302
|
-
def notify(self, event: Event)
|
|
316
|
+
def notify(self, event: Event):
|
|
303
317
|
return self.__channel.dispatch(event)
|
|
304
318
|
|
|
305
|
-
def __filter_classes(
|
|
306
|
-
|
|
307
|
-
|
|
319
|
+
def __filter_classes(
|
|
320
|
+
self,
|
|
321
|
+
classes: Iterable[type | UnionType],
|
|
322
|
+
mode: Mode,
|
|
323
|
+
) -> Iterator[type]:
|
|
324
|
+
rank = mode.rank
|
|
308
325
|
|
|
309
326
|
for cls in frozenset(get_origins(*classes)):
|
|
310
327
|
try:
|
|
311
|
-
_, current_mode = self.
|
|
328
|
+
_, current_mode = self.__records[cls]
|
|
312
329
|
|
|
313
330
|
except KeyError:
|
|
314
331
|
pass
|
|
@@ -319,7 +336,7 @@ class Container(Broker):
|
|
|
319
336
|
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
320
337
|
)
|
|
321
338
|
|
|
322
|
-
elif rank <
|
|
339
|
+
elif rank < current_mode.rank:
|
|
323
340
|
continue
|
|
324
341
|
|
|
325
342
|
yield cls
|
|
@@ -330,9 +347,21 @@ Module
|
|
|
330
347
|
"""
|
|
331
348
|
|
|
332
349
|
|
|
350
|
+
class Priority(str, Enum):
|
|
351
|
+
LOW = "low"
|
|
352
|
+
HIGH = "high"
|
|
353
|
+
|
|
354
|
+
@classmethod
|
|
355
|
+
def get_default(cls):
|
|
356
|
+
return cls.LOW
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
PriorityStr = Literal["low", "high"]
|
|
360
|
+
|
|
361
|
+
|
|
333
362
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
334
363
|
class Module(EventListener, Broker):
|
|
335
|
-
name: str = field(default=None)
|
|
364
|
+
name: str | None = field(default=None)
|
|
336
365
|
__channel: EventChannel = field(default_factory=EventChannel, init=False)
|
|
337
366
|
__container: Container = field(default_factory=Container, init=False)
|
|
338
367
|
__modules: OrderedDict[Module, None] = field(
|
|
@@ -343,7 +372,7 @@ class Module(EventListener, Broker):
|
|
|
343
372
|
def __post_init__(self):
|
|
344
373
|
self.__container.add_listener(self)
|
|
345
374
|
|
|
346
|
-
def __getitem__(self, cls: type[
|
|
375
|
+
def __getitem__(self, cls: type[_In_T] | UnionType, /) -> Injectable[_In_T]:
|
|
347
376
|
for broker in self.__brokers:
|
|
348
377
|
with suppress(KeyError):
|
|
349
378
|
return broker[cls]
|
|
@@ -375,8 +404,8 @@ class Module(EventListener, Broker):
|
|
|
375
404
|
*,
|
|
376
405
|
cls: type[Injectable] = NewInjectable,
|
|
377
406
|
inject: bool = True,
|
|
378
|
-
on: type |
|
|
379
|
-
mode:
|
|
407
|
+
on: type | Iterable[type] | UnionType = (),
|
|
408
|
+
mode: Mode | ModeStr = Mode.get_default(),
|
|
380
409
|
):
|
|
381
410
|
def decorator(wp):
|
|
382
411
|
factory = self.inject(wp, return_factory=True) if inject else wp
|
|
@@ -394,7 +423,7 @@ class Module(EventListener, Broker):
|
|
|
394
423
|
self.update(
|
|
395
424
|
(wp,),
|
|
396
425
|
ShouldBeInjectable(wp),
|
|
397
|
-
mode=
|
|
426
|
+
mode=Mode.FALLBACK,
|
|
398
427
|
)
|
|
399
428
|
return wp
|
|
400
429
|
|
|
@@ -402,16 +431,16 @@ class Module(EventListener, Broker):
|
|
|
402
431
|
|
|
403
432
|
def set_constant(
|
|
404
433
|
self,
|
|
405
|
-
instance:
|
|
406
|
-
on: type |
|
|
434
|
+
instance: _In_T,
|
|
435
|
+
on: type | Iterable[type] | UnionType = (),
|
|
407
436
|
*,
|
|
408
|
-
mode:
|
|
409
|
-
) ->
|
|
437
|
+
mode: Mode | ModeStr = Mode.get_default(),
|
|
438
|
+
) -> _In_T:
|
|
410
439
|
cls = type(instance)
|
|
411
440
|
self.injectable(
|
|
412
441
|
lambda: instance,
|
|
413
442
|
inject=False,
|
|
414
|
-
on=(cls, on),
|
|
443
|
+
on=(cls, on), # type: ignore
|
|
415
444
|
mode=mode,
|
|
416
445
|
)
|
|
417
446
|
return instance
|
|
@@ -439,24 +468,22 @@ class Module(EventListener, Broker):
|
|
|
439
468
|
|
|
440
469
|
return decorator(wrapped) if wrapped else decorator
|
|
441
470
|
|
|
442
|
-
def
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
except KeyError as exc:
|
|
446
|
-
if none:
|
|
447
|
-
return None
|
|
448
|
-
|
|
449
|
-
raise exc
|
|
471
|
+
def resolve(self, cls: type[_In_T]) -> _In_T:
|
|
472
|
+
injectable = self[cls]
|
|
473
|
+
return injectable.get_instance()
|
|
450
474
|
|
|
451
|
-
|
|
452
|
-
|
|
475
|
+
def get_instance(self, cls: type[_In_T]) -> _In_T | None:
|
|
476
|
+
try:
|
|
477
|
+
return self.resolve(cls)
|
|
478
|
+
except KeyError:
|
|
479
|
+
return None
|
|
453
480
|
|
|
454
481
|
def get_lazy_instance(
|
|
455
482
|
self,
|
|
456
|
-
cls: type[
|
|
483
|
+
cls: type[_In_T],
|
|
457
484
|
*,
|
|
458
485
|
cache: bool = False,
|
|
459
|
-
) -> Invertible[
|
|
486
|
+
) -> Invertible[_In_T | None]:
|
|
460
487
|
if cache:
|
|
461
488
|
return Lazy(lambda: self.get_instance(cls))
|
|
462
489
|
|
|
@@ -466,21 +493,27 @@ class Module(EventListener, Broker):
|
|
|
466
493
|
|
|
467
494
|
def update(
|
|
468
495
|
self,
|
|
469
|
-
classes: Iterable[type],
|
|
496
|
+
classes: Iterable[type | UnionType],
|
|
470
497
|
injectable: Injectable,
|
|
471
|
-
mode:
|
|
498
|
+
mode: Mode | ModeStr = Mode.get_default(),
|
|
472
499
|
):
|
|
473
500
|
self.__container.update(classes, injectable, mode)
|
|
474
501
|
return self
|
|
475
502
|
|
|
476
|
-
def use(
|
|
503
|
+
def use(
|
|
504
|
+
self,
|
|
505
|
+
module: Module,
|
|
506
|
+
*,
|
|
507
|
+
priority: Priority | PriorityStr = Priority.get_default(),
|
|
508
|
+
):
|
|
477
509
|
if module is self:
|
|
478
510
|
raise ModuleError("Module can't be used by itself.")
|
|
479
511
|
|
|
480
512
|
if module in self.__modules:
|
|
481
513
|
raise ModuleError(f"`{self}` already uses `{module}`.")
|
|
482
514
|
|
|
483
|
-
|
|
515
|
+
priority = Priority(priority)
|
|
516
|
+
event = ModuleAdded(self, module, priority)
|
|
484
517
|
|
|
485
518
|
with self.notify(event):
|
|
486
519
|
self.__modules[module] = None
|
|
@@ -504,13 +537,14 @@ class Module(EventListener, Broker):
|
|
|
504
537
|
self,
|
|
505
538
|
module: Module,
|
|
506
539
|
*,
|
|
507
|
-
priority:
|
|
508
|
-
)
|
|
540
|
+
priority: Priority | PriorityStr = Priority.get_default(),
|
|
541
|
+
):
|
|
509
542
|
self.use(module, priority=priority)
|
|
510
543
|
yield
|
|
511
544
|
self.stop_using(module)
|
|
512
545
|
|
|
513
|
-
def change_priority(self, module: Module, priority:
|
|
546
|
+
def change_priority(self, module: Module, priority: Priority | PriorityStr):
|
|
547
|
+
priority = Priority(priority)
|
|
514
548
|
event = ModulePriorityUpdated(self, module, priority)
|
|
515
549
|
|
|
516
550
|
with self.notify(event):
|
|
@@ -538,7 +572,7 @@ class Module(EventListener, Broker):
|
|
|
538
572
|
return self.notify(self_event)
|
|
539
573
|
|
|
540
574
|
@contextmanager
|
|
541
|
-
def notify(self, event: Event)
|
|
575
|
+
def notify(self, event: Event):
|
|
542
576
|
self.__check_locking()
|
|
543
577
|
|
|
544
578
|
with self.__channel.dispatch(event):
|
|
@@ -549,8 +583,8 @@ class Module(EventListener, Broker):
|
|
|
549
583
|
if self.is_locked:
|
|
550
584
|
raise ModuleLockError(f"`{self}` is locked.")
|
|
551
585
|
|
|
552
|
-
def __move_module(self, module: Module, priority:
|
|
553
|
-
last = priority !=
|
|
586
|
+
def __move_module(self, module: Module, priority: Priority):
|
|
587
|
+
last = priority != Priority.HIGH
|
|
554
588
|
|
|
555
589
|
try:
|
|
556
590
|
self.__modules.move_to_end(module, last=last)
|
|
@@ -609,7 +643,7 @@ class Dependencies:
|
|
|
609
643
|
) -> Iterator[tuple[str, Injectable]]:
|
|
610
644
|
for name, annotation in cls.__get_annotations(signature, owner):
|
|
611
645
|
try:
|
|
612
|
-
injectable = module[annotation]
|
|
646
|
+
injectable: Injectable = module[annotation]
|
|
613
647
|
except KeyError:
|
|
614
648
|
continue
|
|
615
649
|
|
|
@@ -646,32 +680,25 @@ class InjectedFunction(EventListener):
|
|
|
646
680
|
)
|
|
647
681
|
|
|
648
682
|
def __init__(self, wrapped: Callable[..., Any], /):
|
|
649
|
-
|
|
650
|
-
variables = vars(wrapped)
|
|
651
|
-
except TypeError:
|
|
652
|
-
pass
|
|
653
|
-
else:
|
|
654
|
-
self.__update_vars(variables)
|
|
655
|
-
del variables
|
|
656
|
-
|
|
683
|
+
self.__update_vars_from(wrapped)
|
|
657
684
|
update_wrapper(self, wrapped, updated=())
|
|
658
685
|
self.__dependencies = Dependencies.empty()
|
|
659
686
|
self.__owner = None
|
|
660
687
|
self.__setup_queue = LimitedQueue[Callable[[], Any]]()
|
|
661
688
|
self.on_setup(self.__set_signature)
|
|
662
689
|
|
|
663
|
-
def __repr__(self) -> str:
|
|
690
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
664
691
|
return repr(self.wrapped)
|
|
665
692
|
|
|
666
|
-
def __str__(self) -> str:
|
|
693
|
+
def __str__(self) -> str: # pragma: no cover
|
|
667
694
|
return str(self.wrapped)
|
|
668
695
|
|
|
669
696
|
def __call__(self, /, *args, **kwargs) -> Any:
|
|
670
697
|
for function in self.__setup_queue:
|
|
671
698
|
function()
|
|
672
699
|
|
|
673
|
-
|
|
674
|
-
return self.wrapped(*args, **kwargs)
|
|
700
|
+
arguments = self.bind(args, kwargs)
|
|
701
|
+
return self.wrapped(*arguments.args, **arguments.kwargs)
|
|
675
702
|
|
|
676
703
|
def __get__(self, instance: object = None, owner: type = None):
|
|
677
704
|
if instance is None:
|
|
@@ -688,7 +715,7 @@ class InjectedFunction(EventListener):
|
|
|
688
715
|
|
|
689
716
|
@property
|
|
690
717
|
def wrapped(self) -> Callable[..., Any]:
|
|
691
|
-
return self.__wrapped__
|
|
718
|
+
return self.__wrapped__ # type: ignore
|
|
692
719
|
|
|
693
720
|
def bind(
|
|
694
721
|
self,
|
|
@@ -716,7 +743,7 @@ class InjectedFunction(EventListener):
|
|
|
716
743
|
if self.__owner:
|
|
717
744
|
raise TypeError("Function owner is already defined.")
|
|
718
745
|
|
|
719
|
-
self.__owner = owner
|
|
746
|
+
self.__owner = owner # type: ignore
|
|
720
747
|
return self
|
|
721
748
|
|
|
722
749
|
@synchronized()
|
|
@@ -732,12 +759,12 @@ class InjectedFunction(EventListener):
|
|
|
732
759
|
return decorator(wrapped) if wrapped else decorator
|
|
733
760
|
|
|
734
761
|
@singledispatchmethod
|
|
735
|
-
def on_event(self, event: Event, /):
|
|
736
|
-
|
|
762
|
+
def on_event(self, event: Event, /) -> ContextManager | None: # type: ignore
|
|
763
|
+
return None
|
|
737
764
|
|
|
738
765
|
@on_event.register
|
|
739
766
|
@contextmanager
|
|
740
|
-
def _(self, event: ModuleEvent, /)
|
|
767
|
+
def _(self, event: ModuleEvent, /):
|
|
741
768
|
yield
|
|
742
769
|
self.update(event.on_module)
|
|
743
770
|
|
|
@@ -745,14 +772,21 @@ class InjectedFunction(EventListener):
|
|
|
745
772
|
self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
|
|
746
773
|
return self
|
|
747
774
|
|
|
748
|
-
def
|
|
775
|
+
def __update_vars_from(self, obj: Any):
|
|
776
|
+
try:
|
|
777
|
+
variables = vars(obj)
|
|
778
|
+
except TypeError:
|
|
779
|
+
pass
|
|
780
|
+
else:
|
|
781
|
+
self.__update_vars(variables)
|
|
782
|
+
|
|
783
|
+
def __update_vars(self, variables: Mapping[str, Any]):
|
|
749
784
|
def is_dunder(var: str) -> bool:
|
|
750
785
|
return var.startswith("__") and var.endswith("__")
|
|
751
786
|
|
|
752
787
|
restricted_vars = frozenset(var for var in dir(self) if not is_dunder(var))
|
|
753
|
-
|
|
788
|
+
vars(self).update(
|
|
754
789
|
(var, value)
|
|
755
790
|
for var, value in variables.items()
|
|
756
791
|
if var not in restricted_vars
|
|
757
792
|
)
|
|
758
|
-
vars(self).update(variables)
|
injection/exceptions.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
1
3
|
from injection.common.tools.type import format_type
|
|
2
4
|
|
|
3
5
|
__all__ = (
|
|
@@ -16,7 +18,7 @@ class InjectionError(Exception):
|
|
|
16
18
|
class NoInjectable(KeyError, InjectionError):
|
|
17
19
|
__slots__ = ("__class",)
|
|
18
20
|
|
|
19
|
-
def __init__(self, cls: type):
|
|
21
|
+
def __init__(self, cls: type | Any):
|
|
20
22
|
super().__init__(f"No injectable for `{format_type(cls)}`.")
|
|
21
23
|
self.__class = cls
|
|
22
24
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import Any, TypeVar
|
|
2
2
|
|
|
3
|
+
from rodi import ContainerProtocol
|
|
4
|
+
|
|
3
5
|
from injection import Module, default_module
|
|
4
6
|
|
|
5
7
|
__all__ = ("InjectionServices",)
|
|
@@ -7,7 +9,7 @@ __all__ = ("InjectionServices",)
|
|
|
7
9
|
_T = TypeVar("_T")
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
class InjectionServices:
|
|
12
|
+
class InjectionServices(ContainerProtocol):
|
|
11
13
|
"""
|
|
12
14
|
BlackSheep dependency injection container implemented with `python-injection`.
|
|
13
15
|
"""
|
|
@@ -17,12 +19,12 @@ class InjectionServices:
|
|
|
17
19
|
def __init__(self, module: Module = default_module):
|
|
18
20
|
self.__module = module
|
|
19
21
|
|
|
20
|
-
def __contains__(self,
|
|
21
|
-
return
|
|
22
|
+
def __contains__(self, item: Any) -> bool:
|
|
23
|
+
return item in self.__module
|
|
22
24
|
|
|
23
|
-
def register(self,
|
|
24
|
-
self.__module.injectable(
|
|
25
|
+
def register(self, obj_type: type | Any, *args, **kwargs):
|
|
26
|
+
self.__module.injectable(obj_type)
|
|
25
27
|
return self
|
|
26
28
|
|
|
27
|
-
def resolve(self,
|
|
28
|
-
return self.__module.
|
|
29
|
+
def resolve(self, obj_type: type[_T] | Any, *args, **kwargs) -> _T:
|
|
30
|
+
return self.__module.resolve(obj_type)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
|
5
5
|
Home-page: https://github.com/100nm/python-injection
|
|
6
6
|
License: MIT
|
|
@@ -136,7 +136,7 @@ class ConcreteServiceOverload(ConcreteService):
|
|
|
136
136
|
...
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
If a class is registered in a package and you want to override it, there is the `mode` parameter:
|
|
139
|
+
If a class is registered in a package, and you want to override it, there is the `mode` parameter:
|
|
140
140
|
|
|
141
141
|
```python
|
|
142
142
|
@injectable
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
injection/__init__.py,sha256=Bf6S99E2srD3752xlJf3uAdiGIzY2YHOZafcwEiwY70,739
|
|
2
|
+
injection/__init__.pyi,sha256=U3HBDYYrlxiwqMBIVVzgbI7VREP2N8_5Ysimzh6LmZo,5960
|
|
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=bG-_0u8T13lrZbauHoa8n7KzXg0Acpi3hurIIKhlqes,574
|
|
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=UpkT6xLMUW9IG1yCywzyZo_X5RVU8N3ATFmPyKItLu4,20590
|
|
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/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
17
|
+
python_injection-0.8.4.dist-info/METADATA,sha256=P-ER2tDlSc4aDyzfrBneT7k8_lr6obXcIPJ7O3RfS0g,3672
|
|
18
|
+
python_injection-0.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
19
|
+
python_injection-0.8.4.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
injection/__init__.py,sha256=QyzDT1tjjIvXQmwFjBJL_veQkEHYrn1OvATiGYFbzHo,609
|
|
2
|
-
injection/__init__.pyi,sha256=XarvsM5fn9uMI0vNI4o1hrQAcHFL6Gaz2qSlL5hh4GQ,5395
|
|
3
|
-
injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
injection/common/event.py,sha256=uFoGRnxxkohH53JEStn4tN2Pn79HlgGExh7VkXdBwVQ,1316
|
|
5
|
-
injection/common/invertible.py,sha256=CyGp57Ik1pSQ2G7bRvnFWkY0kJkZDD5_19OjMYNvQes,558
|
|
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=02mBkIir2kQaXt47ewMJnOvhCldXCmPI2V-BW_l_6V8,271
|
|
10
|
-
injection/common/tools/type.py,sha256=-zL0dtoVZme71Mscvav7iEWxY2-JltzNTekbWOCPSFo,1276
|
|
11
|
-
injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
|
|
12
|
-
injection/core/module.py,sha256=F6UEi0KEdz9CutM0IzRLYJUCAnCk8XAmHUBPmxHgMjE,19663
|
|
13
|
-
injection/exceptions.py,sha256=nE56jW00ZB1T-Z-dvfPczPShs3CwIc7tIvdYlOXlaXA,653
|
|
14
|
-
injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
injection/integrations/blacksheep.py,sha256=vcLil1IccS7JtXpuVu7s2LqN5Zravfe_7xpAt5cTIU0,723
|
|
16
|
-
injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
17
|
-
python_injection-0.8.2.dist-info/METADATA,sha256=CSKZD9lBsSbRQO3XZYFVa6ofP5L6Gi_dsDhVke_mS1U,3671
|
|
18
|
-
python_injection-0.8.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
19
|
-
python_injection-0.8.2.dist-info/RECORD,,
|
|
File without changes
|