python-injection 0.8.2__py3-none-any.whl → 0.8.3__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 +3 -1
- injection/__init__.pyi +27 -6
- injection/core/module.py +75 -42
- {python_injection-0.8.2.dist-info → python_injection-0.8.3.dist-info}/METADATA +2 -2
- {python_injection-0.8.2.dist-info → python_injection-0.8.3.dist-info}/RECORD +6 -6
- {python_injection-0.8.2.dist-info → python_injection-0.8.3.dist-info}/WHEEL +0 -0
injection/__init__.py
CHANGED
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,
|
|
@@ -54,7 +55,7 @@ class Module:
|
|
|
54
55
|
cls: type[Injectable] = ...,
|
|
55
56
|
inject: bool = ...,
|
|
56
57
|
on: type | Iterable[type] | UnionType = ...,
|
|
57
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
58
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
58
59
|
):
|
|
59
60
|
"""
|
|
60
61
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -69,7 +70,7 @@ class Module:
|
|
|
69
70
|
*,
|
|
70
71
|
inject: bool = ...,
|
|
71
72
|
on: type | Iterable[type] | UnionType = ...,
|
|
72
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
73
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
73
74
|
):
|
|
74
75
|
"""
|
|
75
76
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -89,7 +90,7 @@ class Module:
|
|
|
89
90
|
instance: _T,
|
|
90
91
|
on: type | Iterable[type] | UnionType = ...,
|
|
91
92
|
*,
|
|
92
|
-
mode: Literal["fallback", "normal", "override"] = ...,
|
|
93
|
+
mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
|
|
93
94
|
) -> _T:
|
|
94
95
|
"""
|
|
95
96
|
Function for registering a specific instance to be injected. This is useful for
|
|
@@ -119,7 +120,12 @@ class Module:
|
|
|
119
120
|
Example: instance = ~lazy_instance
|
|
120
121
|
"""
|
|
121
122
|
|
|
122
|
-
def use(
|
|
123
|
+
def use(
|
|
124
|
+
self,
|
|
125
|
+
module: Module,
|
|
126
|
+
*,
|
|
127
|
+
priority: ModulePriority | Literal["low", "high"] = ...,
|
|
128
|
+
):
|
|
123
129
|
"""
|
|
124
130
|
Function for using another module. Using another module replaces the module's
|
|
125
131
|
dependencies with those of the module used. If the dependency is not found, it
|
|
@@ -135,13 +141,17 @@ class Module:
|
|
|
135
141
|
self,
|
|
136
142
|
module: Module,
|
|
137
143
|
*,
|
|
138
|
-
priority: Literal["low", "high"] = ...,
|
|
144
|
+
priority: ModulePriority | Literal["low", "high"] = ...,
|
|
139
145
|
) -> ContextManager | ContextDecorator:
|
|
140
146
|
"""
|
|
141
147
|
Context manager or decorator for temporary use of a module.
|
|
142
148
|
"""
|
|
143
149
|
|
|
144
|
-
def change_priority(
|
|
150
|
+
def change_priority(
|
|
151
|
+
self,
|
|
152
|
+
module: Module,
|
|
153
|
+
priority: ModulePriority | Literal["low", "high"],
|
|
154
|
+
):
|
|
145
155
|
"""
|
|
146
156
|
Function for changing the priority of a module in use.
|
|
147
157
|
There are two priority values:
|
|
@@ -155,6 +165,11 @@ class Module:
|
|
|
155
165
|
Function to unlock the module by deleting cached instances of singletons.
|
|
156
166
|
"""
|
|
157
167
|
|
|
168
|
+
@final
|
|
169
|
+
class ModulePriority(str, Enum):
|
|
170
|
+
LOW = ...
|
|
171
|
+
HIGH = ...
|
|
172
|
+
|
|
158
173
|
@runtime_checkable
|
|
159
174
|
class Injectable(Protocol[_T]):
|
|
160
175
|
def __init__(self, factory: Callable[[], _T] = ..., /): ...
|
|
@@ -163,3 +178,9 @@ class Injectable(Protocol[_T]):
|
|
|
163
178
|
def unlock(self): ...
|
|
164
179
|
@abstractmethod
|
|
165
180
|
def get_instance(self) -> _T: ...
|
|
181
|
+
|
|
182
|
+
@final
|
|
183
|
+
class InjectableMode(str, Enum):
|
|
184
|
+
FALLBACK = ...
|
|
185
|
+
NORMAL = ...
|
|
186
|
+
OVERRIDE = ...
|
injection/core/module.py
CHANGED
|
@@ -14,6 +14,7 @@ from collections.abc import (
|
|
|
14
14
|
)
|
|
15
15
|
from contextlib import ContextDecorator, 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
|
|
@@ -27,7 +28,6 @@ from typing import (
|
|
|
27
28
|
Protocol,
|
|
28
29
|
TypeVar,
|
|
29
30
|
cast,
|
|
30
|
-
get_args,
|
|
31
31
|
runtime_checkable,
|
|
32
32
|
)
|
|
33
33
|
|
|
@@ -45,7 +45,7 @@ from injection.exceptions import (
|
|
|
45
45
|
NoInjectable,
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
__all__ = ("Injectable", "Module")
|
|
48
|
+
__all__ = ("Injectable", "InjectableMode", "Module", "ModulePriority")
|
|
49
49
|
|
|
50
50
|
_logger = logging.getLogger(__name__)
|
|
51
51
|
|
|
@@ -53,15 +53,6 @@ _T = TypeVar("_T")
|
|
|
53
53
|
_Types = Iterable[type] | UnionType
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
"""
|
|
57
|
-
Literal types
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
_Mode = Literal["fallback", "normal", "override"]
|
|
62
|
-
_Priority = Literal["low", "high"]
|
|
63
|
-
|
|
64
|
-
|
|
65
56
|
"""
|
|
66
57
|
Events
|
|
67
58
|
"""
|
|
@@ -75,7 +66,7 @@ class ContainerEvent(Event, ABC):
|
|
|
75
66
|
@dataclass(frozen=True, slots=True)
|
|
76
67
|
class ContainerDependenciesUpdated(ContainerEvent):
|
|
77
68
|
classes: Collection[type]
|
|
78
|
-
mode:
|
|
69
|
+
mode: InjectableMode
|
|
79
70
|
|
|
80
71
|
def __str__(self) -> str:
|
|
81
72
|
length = len(self.classes)
|
|
@@ -113,6 +104,7 @@ class ModuleEventProxy(ModuleEvent):
|
|
|
113
104
|
@dataclass(frozen=True, slots=True)
|
|
114
105
|
class ModuleAdded(ModuleEvent):
|
|
115
106
|
module_added: Module
|
|
107
|
+
priority: ModulePriority
|
|
116
108
|
|
|
117
109
|
def __str__(self) -> str:
|
|
118
110
|
return f"`{self.on_module}` now uses `{self.module_added}`."
|
|
@@ -129,7 +121,7 @@ class ModuleRemoved(ModuleEvent):
|
|
|
129
121
|
@dataclass(frozen=True, slots=True)
|
|
130
122
|
class ModulePriorityUpdated(ModuleEvent):
|
|
131
123
|
module_updated: Module
|
|
132
|
-
priority:
|
|
124
|
+
priority: ModulePriority
|
|
133
125
|
|
|
134
126
|
def __str__(self) -> str:
|
|
135
127
|
return (
|
|
@@ -241,20 +233,37 @@ Container
|
|
|
241
233
|
"""
|
|
242
234
|
|
|
243
235
|
|
|
236
|
+
class InjectableMode(str, Enum):
|
|
237
|
+
FALLBACK = "fallback"
|
|
238
|
+
NORMAL = "normal"
|
|
239
|
+
OVERRIDE = "override"
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def rank(self) -> int:
|
|
243
|
+
return tuple(type(self)).index(self)
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def get_default(cls):
|
|
247
|
+
return cls.NORMAL
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
_Mode = InjectableMode | Literal["fallback", "normal", "override"]
|
|
251
|
+
|
|
252
|
+
|
|
244
253
|
class Record(NamedTuple):
|
|
245
254
|
injectable: Injectable
|
|
246
|
-
mode:
|
|
255
|
+
mode: InjectableMode
|
|
247
256
|
|
|
248
257
|
|
|
249
258
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
250
259
|
class Container(Broker):
|
|
251
|
-
|
|
260
|
+
__records: dict[type, Record] = field(default_factory=dict, init=False)
|
|
252
261
|
__channel: EventChannel = field(default_factory=EventChannel, init=False)
|
|
253
262
|
|
|
254
263
|
def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
|
|
255
264
|
for cls in get_origins(cls):
|
|
256
265
|
try:
|
|
257
|
-
injectable, _ = self.
|
|
266
|
+
injectable, _ = self.__records[cls]
|
|
258
267
|
except KeyError:
|
|
259
268
|
continue
|
|
260
269
|
|
|
@@ -263,7 +272,7 @@ class Container(Broker):
|
|
|
263
272
|
raise NoInjectable(cls)
|
|
264
273
|
|
|
265
274
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
266
|
-
return any(cls in self.
|
|
275
|
+
return any(cls in self.__records for cls in get_origins(cls))
|
|
267
276
|
|
|
268
277
|
@property
|
|
269
278
|
def is_locked(self) -> bool:
|
|
@@ -271,10 +280,11 @@ class Container(Broker):
|
|
|
271
280
|
|
|
272
281
|
@property
|
|
273
282
|
def __injectables(self) -> frozenset[Injectable]:
|
|
274
|
-
return frozenset(injectable for injectable, _ in self.
|
|
283
|
+
return frozenset(injectable for injectable, _ in self.__records.values())
|
|
275
284
|
|
|
276
285
|
@synchronized()
|
|
277
286
|
def update(self, classes: Iterable[type], injectable: Injectable, mode: _Mode):
|
|
287
|
+
mode = InjectableMode(mode)
|
|
278
288
|
records = {
|
|
279
289
|
cls: Record(injectable, mode)
|
|
280
290
|
for cls in self.__filter_classes(classes, mode)
|
|
@@ -284,7 +294,7 @@ class Container(Broker):
|
|
|
284
294
|
event = ContainerDependenciesUpdated(self, records.keys(), mode)
|
|
285
295
|
|
|
286
296
|
with self.notify(event):
|
|
287
|
-
self.
|
|
297
|
+
self.__records.update(records)
|
|
288
298
|
|
|
289
299
|
return self
|
|
290
300
|
|
|
@@ -302,13 +312,16 @@ class Container(Broker):
|
|
|
302
312
|
def notify(self, event: Event) -> ContextManager | ContextDecorator:
|
|
303
313
|
return self.__channel.dispatch(event)
|
|
304
314
|
|
|
305
|
-
def __filter_classes(
|
|
306
|
-
|
|
307
|
-
|
|
315
|
+
def __filter_classes(
|
|
316
|
+
self,
|
|
317
|
+
classes: Iterable[type],
|
|
318
|
+
mode: InjectableMode,
|
|
319
|
+
) -> Iterator[type]:
|
|
320
|
+
rank = mode.rank
|
|
308
321
|
|
|
309
322
|
for cls in frozenset(get_origins(*classes)):
|
|
310
323
|
try:
|
|
311
|
-
_, current_mode = self.
|
|
324
|
+
_, current_mode = self.__records[cls]
|
|
312
325
|
|
|
313
326
|
except KeyError:
|
|
314
327
|
pass
|
|
@@ -319,7 +332,7 @@ class Container(Broker):
|
|
|
319
332
|
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
320
333
|
)
|
|
321
334
|
|
|
322
|
-
elif rank <
|
|
335
|
+
elif rank < current_mode.rank:
|
|
323
336
|
continue
|
|
324
337
|
|
|
325
338
|
yield cls
|
|
@@ -330,6 +343,18 @@ Module
|
|
|
330
343
|
"""
|
|
331
344
|
|
|
332
345
|
|
|
346
|
+
class ModulePriority(str, Enum):
|
|
347
|
+
LOW = "low"
|
|
348
|
+
HIGH = "high"
|
|
349
|
+
|
|
350
|
+
@classmethod
|
|
351
|
+
def get_default(cls):
|
|
352
|
+
return cls.LOW
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
_Priority = ModulePriority | Literal["low", "high"]
|
|
356
|
+
|
|
357
|
+
|
|
333
358
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
334
359
|
class Module(EventListener, Broker):
|
|
335
360
|
name: str = field(default=None)
|
|
@@ -376,7 +401,7 @@ class Module(EventListener, Broker):
|
|
|
376
401
|
cls: type[Injectable] = NewInjectable,
|
|
377
402
|
inject: bool = True,
|
|
378
403
|
on: type | _Types = None,
|
|
379
|
-
mode: _Mode =
|
|
404
|
+
mode: _Mode = InjectableMode.get_default(),
|
|
380
405
|
):
|
|
381
406
|
def decorator(wp):
|
|
382
407
|
factory = self.inject(wp, return_factory=True) if inject else wp
|
|
@@ -394,7 +419,7 @@ class Module(EventListener, Broker):
|
|
|
394
419
|
self.update(
|
|
395
420
|
(wp,),
|
|
396
421
|
ShouldBeInjectable(wp),
|
|
397
|
-
mode=
|
|
422
|
+
mode=InjectableMode.FALLBACK,
|
|
398
423
|
)
|
|
399
424
|
return wp
|
|
400
425
|
|
|
@@ -405,7 +430,7 @@ class Module(EventListener, Broker):
|
|
|
405
430
|
instance: _T,
|
|
406
431
|
on: type | _Types = None,
|
|
407
432
|
*,
|
|
408
|
-
mode: _Mode =
|
|
433
|
+
mode: _Mode = InjectableMode.get_default(),
|
|
409
434
|
) -> _T:
|
|
410
435
|
cls = type(instance)
|
|
411
436
|
self.injectable(
|
|
@@ -468,19 +493,25 @@ class Module(EventListener, Broker):
|
|
|
468
493
|
self,
|
|
469
494
|
classes: Iterable[type],
|
|
470
495
|
injectable: Injectable,
|
|
471
|
-
mode: _Mode =
|
|
496
|
+
mode: _Mode = InjectableMode.get_default(),
|
|
472
497
|
):
|
|
473
498
|
self.__container.update(classes, injectable, mode)
|
|
474
499
|
return self
|
|
475
500
|
|
|
476
|
-
def use(
|
|
501
|
+
def use(
|
|
502
|
+
self,
|
|
503
|
+
module: Module,
|
|
504
|
+
*,
|
|
505
|
+
priority: _Priority = ModulePriority.get_default(),
|
|
506
|
+
):
|
|
477
507
|
if module is self:
|
|
478
508
|
raise ModuleError("Module can't be used by itself.")
|
|
479
509
|
|
|
480
510
|
if module in self.__modules:
|
|
481
511
|
raise ModuleError(f"`{self}` already uses `{module}`.")
|
|
482
512
|
|
|
483
|
-
|
|
513
|
+
priority = ModulePriority(priority)
|
|
514
|
+
event = ModuleAdded(self, module, priority)
|
|
484
515
|
|
|
485
516
|
with self.notify(event):
|
|
486
517
|
self.__modules[module] = None
|
|
@@ -504,13 +535,14 @@ class Module(EventListener, Broker):
|
|
|
504
535
|
self,
|
|
505
536
|
module: Module,
|
|
506
537
|
*,
|
|
507
|
-
priority: _Priority =
|
|
538
|
+
priority: _Priority = ModulePriority.get_default(),
|
|
508
539
|
) -> ContextManager | ContextDecorator:
|
|
509
540
|
self.use(module, priority=priority)
|
|
510
541
|
yield
|
|
511
542
|
self.stop_using(module)
|
|
512
543
|
|
|
513
544
|
def change_priority(self, module: Module, priority: _Priority):
|
|
545
|
+
priority = ModulePriority(priority)
|
|
514
546
|
event = ModulePriorityUpdated(self, module, priority)
|
|
515
547
|
|
|
516
548
|
with self.notify(event):
|
|
@@ -549,8 +581,8 @@ class Module(EventListener, Broker):
|
|
|
549
581
|
if self.is_locked:
|
|
550
582
|
raise ModuleLockError(f"`{self}` is locked.")
|
|
551
583
|
|
|
552
|
-
def __move_module(self, module: Module, priority:
|
|
553
|
-
last = priority !=
|
|
584
|
+
def __move_module(self, module: Module, priority: ModulePriority):
|
|
585
|
+
last = priority != ModulePriority.HIGH
|
|
554
586
|
|
|
555
587
|
try:
|
|
556
588
|
self.__modules.move_to_end(module, last=last)
|
|
@@ -646,14 +678,7 @@ class InjectedFunction(EventListener):
|
|
|
646
678
|
)
|
|
647
679
|
|
|
648
680
|
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
|
-
|
|
681
|
+
self.__update_vars_from(wrapped)
|
|
657
682
|
update_wrapper(self, wrapped, updated=())
|
|
658
683
|
self.__dependencies = Dependencies.empty()
|
|
659
684
|
self.__owner = None
|
|
@@ -745,7 +770,15 @@ class InjectedFunction(EventListener):
|
|
|
745
770
|
self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
|
|
746
771
|
return self
|
|
747
772
|
|
|
748
|
-
def
|
|
773
|
+
def __update_vars_from(self, obj: Any):
|
|
774
|
+
try:
|
|
775
|
+
variables = vars(obj)
|
|
776
|
+
except TypeError:
|
|
777
|
+
pass
|
|
778
|
+
else:
|
|
779
|
+
self.__update_vars(variables)
|
|
780
|
+
|
|
781
|
+
def __update_vars(self, variables: Mapping[str, Any]):
|
|
749
782
|
def is_dunder(var: str) -> bool:
|
|
750
783
|
return var.startswith("__") and var.endswith("__")
|
|
751
784
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
injection/__init__.py,sha256=
|
|
2
|
-
injection/__init__.pyi,sha256=
|
|
1
|
+
injection/__init__.py,sha256=6Z4-h68LLXJ6FlrLqEMeJjw_RG7jyl0b-GQwKC2vIEY,685
|
|
2
|
+
injection/__init__.pyi,sha256=DrMtI3q4JYqPT2nwEoSmY3gVO62TwOUOJToEv9rtPVk,5755
|
|
3
3
|
injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
injection/common/event.py,sha256=uFoGRnxxkohH53JEStn4tN2Pn79HlgGExh7VkXdBwVQ,1316
|
|
5
5
|
injection/common/invertible.py,sha256=CyGp57Ik1pSQ2G7bRvnFWkY0kJkZDD5_19OjMYNvQes,558
|
|
@@ -9,11 +9,11 @@ injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
9
9
|
injection/common/tools/threading.py,sha256=02mBkIir2kQaXt47ewMJnOvhCldXCmPI2V-BW_l_6V8,271
|
|
10
10
|
injection/common/tools/type.py,sha256=-zL0dtoVZme71Mscvav7iEWxY2-JltzNTekbWOCPSFo,1276
|
|
11
11
|
injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
|
|
12
|
-
injection/core/module.py,sha256=
|
|
12
|
+
injection/core/module.py,sha256=mO8vHhbbvYTMFEucmpjFbGrJTeBrtlPiERmQDCxBGao,20545
|
|
13
13
|
injection/exceptions.py,sha256=nE56jW00ZB1T-Z-dvfPczPShs3CwIc7tIvdYlOXlaXA,653
|
|
14
14
|
injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
injection/integrations/blacksheep.py,sha256=vcLil1IccS7JtXpuVu7s2LqN5Zravfe_7xpAt5cTIU0,723
|
|
16
16
|
injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
|
|
17
|
-
python_injection-0.8.
|
|
18
|
-
python_injection-0.8.
|
|
19
|
-
python_injection-0.8.
|
|
17
|
+
python_injection-0.8.3.dist-info/METADATA,sha256=148AKRb_jDQah38kKBXqLv185AoQeq8NTgg_9JMZDY4,3672
|
|
18
|
+
python_injection-0.8.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
19
|
+
python_injection-0.8.3.dist-info/RECORD,,
|
|
File without changes
|