python-injection 0.8.1__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of python-injection might be problematic. Click here for more details.
- {python_injection-0.8.1 → python_injection-0.8.2}/PKG-INFO +28 -29
- {python_injection-0.8.1 → python_injection-0.8.2}/documentation/basic-usage.md +27 -28
- python_injection-0.8.1/injection/_pkg.py → python_injection-0.8.2/injection/__init__.py +1 -2
- python_injection-0.8.1/injection/_pkg.pyi → python_injection-0.8.2/injection/__init__.pyi +11 -14
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/core/module.py +120 -109
- {python_injection-0.8.1 → python_injection-0.8.2}/pyproject.toml +4 -2
- python_injection-0.8.1/injection/__init__.py +0 -1
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/__init__.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/event.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/invertible.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/lazy.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/queue.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/tools/__init__.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/tools/threading.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/common/tools/type.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/core/__init__.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/exceptions.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/integrations/__init__.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/integrations/blacksheep.py +0 -0
- {python_injection-0.8.1 → python_injection-0.8.2}/injection/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
|
5
5
|
Home-page: https://github.com/100nm/python-injection
|
|
6
6
|
License: MIT
|
|
@@ -27,7 +27,7 @@ If you wish to inject a singleton, use `singleton` decorator.
|
|
|
27
27
|
from injection import singleton
|
|
28
28
|
|
|
29
29
|
@singleton
|
|
30
|
-
class
|
|
30
|
+
class ServiceA:
|
|
31
31
|
""" class implementation """
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -37,7 +37,7 @@ If you wish to inject a new instance each time, use `injectable` decorator.
|
|
|
37
37
|
from injection import injectable
|
|
38
38
|
|
|
39
39
|
@injectable
|
|
40
|
-
class
|
|
40
|
+
class ServiceB:
|
|
41
41
|
""" class implementation """
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -47,7 +47,10 @@ function.
|
|
|
47
47
|
```python
|
|
48
48
|
from injection import set_constant
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
class ServiceC:
|
|
51
|
+
""" class implementation """
|
|
52
|
+
|
|
53
|
+
service_c = set_constant(ServiceC())
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
## Inject an instance
|
|
@@ -59,7 +62,7 @@ _Don't forget to annotate type of parameter to inject._
|
|
|
59
62
|
from injection import inject
|
|
60
63
|
|
|
61
64
|
@inject
|
|
62
|
-
def
|
|
65
|
+
def some_function(service_a: ServiceA):
|
|
63
66
|
""" function implementation """
|
|
64
67
|
```
|
|
65
68
|
|
|
@@ -76,8 +79,8 @@ from injection import inject
|
|
|
76
79
|
|
|
77
80
|
@inject
|
|
78
81
|
@dataclass
|
|
79
|
-
class
|
|
80
|
-
|
|
82
|
+
class SomeDataClass:
|
|
83
|
+
service_a: ServiceA = ...
|
|
81
84
|
```
|
|
82
85
|
|
|
83
86
|
## Get an instance
|
|
@@ -87,7 +90,7 @@ _Example with `get_instance` function:_
|
|
|
87
90
|
```python
|
|
88
91
|
from injection import get_instance
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
service_a = get_instance(ServiceA)
|
|
91
94
|
```
|
|
92
95
|
|
|
93
96
|
_Example with `get_lazy_instance` function:_
|
|
@@ -95,9 +98,9 @@ _Example with `get_lazy_instance` function:_
|
|
|
95
98
|
```python
|
|
96
99
|
from injection import get_lazy_instance
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
lazy_service_a = get_lazy_instance(ServiceA)
|
|
99
102
|
# ...
|
|
100
|
-
|
|
103
|
+
service_a = ~lazy_service_a
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
## Inheritance
|
|
@@ -111,43 +114,39 @@ classes.
|
|
|
111
114
|
_Example with one class:_
|
|
112
115
|
|
|
113
116
|
```python
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class A:
|
|
117
|
+
class AbstractService(ABC):
|
|
117
118
|
...
|
|
118
119
|
|
|
119
|
-
@
|
|
120
|
-
class
|
|
120
|
+
@injectable(on=AbstractService)
|
|
121
|
+
class ConcreteService(AbstractService):
|
|
121
122
|
...
|
|
122
123
|
```
|
|
123
124
|
|
|
124
125
|
_Example with several classes:_
|
|
125
126
|
|
|
126
127
|
```python
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class A:
|
|
128
|
+
class AbstractService(ABC):
|
|
130
129
|
...
|
|
131
130
|
|
|
132
|
-
class
|
|
131
|
+
class ConcreteService(AbstractService):
|
|
133
132
|
...
|
|
134
133
|
|
|
135
|
-
@
|
|
136
|
-
class
|
|
134
|
+
@injectable(on=(AbstractService, ConcreteService))
|
|
135
|
+
class ConcreteServiceOverload(ConcreteService):
|
|
137
136
|
...
|
|
138
137
|
```
|
|
139
138
|
|
|
140
|
-
If a class is registered in a package and you want to override it, there is the `
|
|
139
|
+
If a class is registered in a package and you want to override it, there is the `mode` parameter:
|
|
141
140
|
|
|
142
141
|
```python
|
|
143
|
-
@
|
|
144
|
-
class
|
|
142
|
+
@injectable
|
|
143
|
+
class InaccessibleService:
|
|
145
144
|
...
|
|
146
145
|
|
|
147
146
|
# ...
|
|
148
147
|
|
|
149
|
-
@
|
|
150
|
-
class
|
|
148
|
+
@injectable(on=InaccessibleService, mode="override")
|
|
149
|
+
class ServiceOverload(InaccessibleService):
|
|
151
150
|
...
|
|
152
151
|
```
|
|
153
152
|
|
|
@@ -157,10 +156,10 @@ A recipe is a function that tells the injector how to construct the instance to
|
|
|
157
156
|
the return type annotation when defining the recipe.
|
|
158
157
|
|
|
159
158
|
```python
|
|
160
|
-
from injection import
|
|
159
|
+
from injection import injectable
|
|
161
160
|
|
|
162
|
-
@
|
|
163
|
-
def
|
|
161
|
+
@injectable
|
|
162
|
+
def service_d_recipe() -> ServiceD:
|
|
164
163
|
""" recipe implementation """
|
|
165
164
|
```
|
|
166
165
|
|
|
@@ -10,7 +10,7 @@ If you wish to inject a singleton, use `singleton` decorator.
|
|
|
10
10
|
from injection import singleton
|
|
11
11
|
|
|
12
12
|
@singleton
|
|
13
|
-
class
|
|
13
|
+
class ServiceA:
|
|
14
14
|
""" class implementation """
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -20,7 +20,7 @@ If you wish to inject a new instance each time, use `injectable` decorator.
|
|
|
20
20
|
from injection import injectable
|
|
21
21
|
|
|
22
22
|
@injectable
|
|
23
|
-
class
|
|
23
|
+
class ServiceB:
|
|
24
24
|
""" class implementation """
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -30,7 +30,10 @@ function.
|
|
|
30
30
|
```python
|
|
31
31
|
from injection import set_constant
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
class ServiceC:
|
|
34
|
+
""" class implementation """
|
|
35
|
+
|
|
36
|
+
service_c = set_constant(ServiceC())
|
|
34
37
|
```
|
|
35
38
|
|
|
36
39
|
## Inject an instance
|
|
@@ -42,7 +45,7 @@ _Don't forget to annotate type of parameter to inject._
|
|
|
42
45
|
from injection import inject
|
|
43
46
|
|
|
44
47
|
@inject
|
|
45
|
-
def
|
|
48
|
+
def some_function(service_a: ServiceA):
|
|
46
49
|
""" function implementation """
|
|
47
50
|
```
|
|
48
51
|
|
|
@@ -59,8 +62,8 @@ from injection import inject
|
|
|
59
62
|
|
|
60
63
|
@inject
|
|
61
64
|
@dataclass
|
|
62
|
-
class
|
|
63
|
-
|
|
65
|
+
class SomeDataClass:
|
|
66
|
+
service_a: ServiceA = ...
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
## Get an instance
|
|
@@ -70,7 +73,7 @@ _Example with `get_instance` function:_
|
|
|
70
73
|
```python
|
|
71
74
|
from injection import get_instance
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
service_a = get_instance(ServiceA)
|
|
74
77
|
```
|
|
75
78
|
|
|
76
79
|
_Example with `get_lazy_instance` function:_
|
|
@@ -78,9 +81,9 @@ _Example with `get_lazy_instance` function:_
|
|
|
78
81
|
```python
|
|
79
82
|
from injection import get_lazy_instance
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
lazy_service_a = get_lazy_instance(ServiceA)
|
|
82
85
|
# ...
|
|
83
|
-
|
|
86
|
+
service_a = ~lazy_service_a
|
|
84
87
|
```
|
|
85
88
|
|
|
86
89
|
## Inheritance
|
|
@@ -94,43 +97,39 @@ classes.
|
|
|
94
97
|
_Example with one class:_
|
|
95
98
|
|
|
96
99
|
```python
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class A:
|
|
100
|
+
class AbstractService(ABC):
|
|
100
101
|
...
|
|
101
102
|
|
|
102
|
-
@
|
|
103
|
-
class
|
|
103
|
+
@injectable(on=AbstractService)
|
|
104
|
+
class ConcreteService(AbstractService):
|
|
104
105
|
...
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
_Example with several classes:_
|
|
108
109
|
|
|
109
110
|
```python
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class A:
|
|
111
|
+
class AbstractService(ABC):
|
|
113
112
|
...
|
|
114
113
|
|
|
115
|
-
class
|
|
114
|
+
class ConcreteService(AbstractService):
|
|
116
115
|
...
|
|
117
116
|
|
|
118
|
-
@
|
|
119
|
-
class
|
|
117
|
+
@injectable(on=(AbstractService, ConcreteService))
|
|
118
|
+
class ConcreteServiceOverload(ConcreteService):
|
|
120
119
|
...
|
|
121
120
|
```
|
|
122
121
|
|
|
123
|
-
If a class is registered in a package and you want to override it, there is the `
|
|
122
|
+
If a class is registered in a package and you want to override it, there is the `mode` parameter:
|
|
124
123
|
|
|
125
124
|
```python
|
|
126
|
-
@
|
|
127
|
-
class
|
|
125
|
+
@injectable
|
|
126
|
+
class InaccessibleService:
|
|
128
127
|
...
|
|
129
128
|
|
|
130
129
|
# ...
|
|
131
130
|
|
|
132
|
-
@
|
|
133
|
-
class
|
|
131
|
+
@injectable(on=InaccessibleService, mode="override")
|
|
132
|
+
class ServiceOverload(InaccessibleService):
|
|
134
133
|
...
|
|
135
134
|
```
|
|
136
135
|
|
|
@@ -140,9 +139,9 @@ A recipe is a function that tells the injector how to construct the instance to
|
|
|
140
139
|
the return type annotation when defining the recipe.
|
|
141
140
|
|
|
142
141
|
```python
|
|
143
|
-
from injection import
|
|
142
|
+
from injection import injectable
|
|
144
143
|
|
|
145
|
-
@
|
|
146
|
-
def
|
|
144
|
+
@injectable
|
|
145
|
+
def service_d_recipe() -> ServiceD:
|
|
147
146
|
""" recipe implementation """
|
|
148
147
|
```
|
|
@@ -1,19 +1,19 @@
|
|
|
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
|
|
5
4
|
from types import UnionType
|
|
6
5
|
from typing import (
|
|
7
6
|
Any,
|
|
8
7
|
ContextManager,
|
|
9
8
|
Final,
|
|
9
|
+
Literal,
|
|
10
10
|
Protocol,
|
|
11
11
|
TypeVar,
|
|
12
12
|
final,
|
|
13
13
|
runtime_checkable,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from .common.invertible import Invertible
|
|
17
17
|
|
|
18
18
|
_T = TypeVar("_T")
|
|
19
19
|
|
|
@@ -54,7 +54,7 @@ class Module:
|
|
|
54
54
|
cls: type[Injectable] = ...,
|
|
55
55
|
inject: bool = ...,
|
|
56
56
|
on: type | Iterable[type] | UnionType = ...,
|
|
57
|
-
|
|
57
|
+
mode: Literal["fallback", "normal", "override"] = ...,
|
|
58
58
|
):
|
|
59
59
|
"""
|
|
60
60
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -69,7 +69,7 @@ class Module:
|
|
|
69
69
|
*,
|
|
70
70
|
inject: bool = ...,
|
|
71
71
|
on: type | Iterable[type] | UnionType = ...,
|
|
72
|
-
|
|
72
|
+
mode: Literal["fallback", "normal", "override"] = ...,
|
|
73
73
|
):
|
|
74
74
|
"""
|
|
75
75
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
@@ -89,7 +89,7 @@ class Module:
|
|
|
89
89
|
instance: _T,
|
|
90
90
|
on: type | Iterable[type] | UnionType = ...,
|
|
91
91
|
*,
|
|
92
|
-
|
|
92
|
+
mode: Literal["fallback", "normal", "override"] = ...,
|
|
93
93
|
) -> _T:
|
|
94
94
|
"""
|
|
95
95
|
Function for registering a specific instance to be injected. This is useful for
|
|
@@ -97,7 +97,7 @@ class Module:
|
|
|
97
97
|
that no dependencies are resolved, so the module doesn't need to be locked.
|
|
98
98
|
"""
|
|
99
99
|
|
|
100
|
-
def get_instance(self, cls: type[_T], none: bool = ...) -> _T | None:
|
|
100
|
+
def get_instance(self, cls: type[_T], *, none: bool = ...) -> _T | None:
|
|
101
101
|
"""
|
|
102
102
|
Function used to retrieve an instance associated with the type passed in
|
|
103
103
|
parameter or return `None` but if `none` parameter is `False` an exception
|
|
@@ -107,6 +107,7 @@ class Module:
|
|
|
107
107
|
def get_lazy_instance(
|
|
108
108
|
self,
|
|
109
109
|
cls: type[_T],
|
|
110
|
+
*,
|
|
110
111
|
cache: bool = ...,
|
|
111
112
|
) -> Invertible[_T | None]:
|
|
112
113
|
"""
|
|
@@ -118,7 +119,7 @@ class Module:
|
|
|
118
119
|
Example: instance = ~lazy_instance
|
|
119
120
|
"""
|
|
120
121
|
|
|
121
|
-
def use(self, module: Module, priority:
|
|
122
|
+
def use(self, module: Module, *, priority: Literal["low", "high"] = ...):
|
|
122
123
|
"""
|
|
123
124
|
Function for using another module. Using another module replaces the module's
|
|
124
125
|
dependencies with those of the module used. If the dependency is not found, it
|
|
@@ -133,13 +134,14 @@ class Module:
|
|
|
133
134
|
def use_temporarily(
|
|
134
135
|
self,
|
|
135
136
|
module: Module,
|
|
136
|
-
|
|
137
|
+
*,
|
|
138
|
+
priority: Literal["low", "high"] = ...,
|
|
137
139
|
) -> ContextManager | ContextDecorator:
|
|
138
140
|
"""
|
|
139
141
|
Context manager or decorator for temporary use of a module.
|
|
140
142
|
"""
|
|
141
143
|
|
|
142
|
-
def change_priority(self, module: Module, priority:
|
|
144
|
+
def change_priority(self, module: Module, priority: Literal["low", "high"]):
|
|
143
145
|
"""
|
|
144
146
|
Function for changing the priority of a module in use.
|
|
145
147
|
There are two priority values:
|
|
@@ -153,11 +155,6 @@ class Module:
|
|
|
153
155
|
Function to unlock the module by deleting cached instances of singletons.
|
|
154
156
|
"""
|
|
155
157
|
|
|
156
|
-
@final
|
|
157
|
-
class ModulePriority(Enum):
|
|
158
|
-
HIGH = ...
|
|
159
|
-
LOW = ...
|
|
160
|
-
|
|
161
158
|
@runtime_checkable
|
|
162
159
|
class Injectable(Protocol[_T]):
|
|
163
160
|
def __init__(self, factory: Callable[[], _T] = ..., /): ...
|
|
@@ -11,28 +11,23 @@ from collections.abc import (
|
|
|
11
11
|
Iterator,
|
|
12
12
|
Mapping,
|
|
13
13
|
MutableMapping,
|
|
14
|
-
Set,
|
|
15
14
|
)
|
|
16
15
|
from contextlib import ContextDecorator, contextmanager, suppress
|
|
17
16
|
from dataclasses import dataclass, field
|
|
18
|
-
from
|
|
19
|
-
from functools import (
|
|
20
|
-
partialmethod,
|
|
21
|
-
singledispatchmethod,
|
|
22
|
-
update_wrapper,
|
|
23
|
-
wraps,
|
|
24
|
-
)
|
|
17
|
+
from functools import partialmethod, singledispatchmethod, update_wrapper
|
|
25
18
|
from inspect import Signature, isclass
|
|
26
|
-
from types import UnionType
|
|
19
|
+
from types import MethodType, UnionType
|
|
27
20
|
from typing import (
|
|
28
21
|
Any,
|
|
29
22
|
ClassVar,
|
|
30
23
|
ContextManager,
|
|
24
|
+
Literal,
|
|
31
25
|
NamedTuple,
|
|
32
26
|
NoReturn,
|
|
33
27
|
Protocol,
|
|
34
28
|
TypeVar,
|
|
35
29
|
cast,
|
|
30
|
+
get_args,
|
|
36
31
|
runtime_checkable,
|
|
37
32
|
)
|
|
38
33
|
|
|
@@ -50,12 +45,21 @@ from injection.exceptions import (
|
|
|
50
45
|
NoInjectable,
|
|
51
46
|
)
|
|
52
47
|
|
|
53
|
-
__all__ = ("Injectable", "Module"
|
|
48
|
+
__all__ = ("Injectable", "Module")
|
|
54
49
|
|
|
55
50
|
_logger = logging.getLogger(__name__)
|
|
56
51
|
|
|
57
52
|
_T = TypeVar("_T")
|
|
58
|
-
|
|
53
|
+
_Types = Iterable[type] | UnionType
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
Literal types
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
_Mode = Literal["fallback", "normal", "override"]
|
|
62
|
+
_Priority = Literal["low", "high"]
|
|
59
63
|
|
|
60
64
|
|
|
61
65
|
"""
|
|
@@ -71,7 +75,7 @@ class ContainerEvent(Event, ABC):
|
|
|
71
75
|
@dataclass(frozen=True, slots=True)
|
|
72
76
|
class ContainerDependenciesUpdated(ContainerEvent):
|
|
73
77
|
classes: Collection[type]
|
|
74
|
-
|
|
78
|
+
mode: _Mode
|
|
75
79
|
|
|
76
80
|
def __str__(self) -> str:
|
|
77
81
|
length = len(self.classes)
|
|
@@ -125,11 +129,11 @@ class ModuleRemoved(ModuleEvent):
|
|
|
125
129
|
@dataclass(frozen=True, slots=True)
|
|
126
130
|
class ModulePriorityUpdated(ModuleEvent):
|
|
127
131
|
module_updated: Module
|
|
128
|
-
priority:
|
|
132
|
+
priority: _Priority
|
|
129
133
|
|
|
130
134
|
def __str__(self) -> str:
|
|
131
135
|
return (
|
|
132
|
-
f"In `{self.on_module}`, the priority `{self.priority
|
|
136
|
+
f"In `{self.on_module}`, the priority `{self.priority}` "
|
|
133
137
|
f"has been applied to `{self.module_updated}`."
|
|
134
138
|
)
|
|
135
139
|
|
|
@@ -158,13 +162,6 @@ class Injectable(Protocol[_T]):
|
|
|
158
162
|
raise NotImplementedError
|
|
159
163
|
|
|
160
164
|
|
|
161
|
-
class FallbackInjectable(Injectable[_T], ABC):
|
|
162
|
-
__slots__ = ()
|
|
163
|
-
|
|
164
|
-
def __bool__(self) -> bool:
|
|
165
|
-
return False
|
|
166
|
-
|
|
167
|
-
|
|
168
165
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
169
166
|
class BaseInjectable(Injectable[_T], ABC):
|
|
170
167
|
factory: Callable[[], _T]
|
|
@@ -205,7 +202,7 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
205
202
|
|
|
206
203
|
|
|
207
204
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
208
|
-
class ShouldBeInjectable(
|
|
205
|
+
class ShouldBeInjectable(Injectable[_T]):
|
|
209
206
|
cls: type[_T]
|
|
210
207
|
|
|
211
208
|
def get_instance(self) -> NoReturn:
|
|
@@ -244,49 +241,50 @@ Container
|
|
|
244
241
|
"""
|
|
245
242
|
|
|
246
243
|
|
|
244
|
+
class Record(NamedTuple):
|
|
245
|
+
injectable: Injectable
|
|
246
|
+
mode: _Mode
|
|
247
|
+
|
|
248
|
+
|
|
247
249
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
248
250
|
class Container(Broker):
|
|
249
|
-
__data: dict[type,
|
|
251
|
+
__data: dict[type, Record] = field(default_factory=dict, init=False)
|
|
250
252
|
__channel: EventChannel = field(default_factory=EventChannel, init=False)
|
|
251
253
|
|
|
252
254
|
def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
|
|
253
|
-
for
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
for cls in get_origins(cls):
|
|
256
|
+
try:
|
|
257
|
+
injectable, _ = self.__data[cls]
|
|
258
|
+
except KeyError:
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
return injectable
|
|
256
262
|
|
|
257
263
|
raise NoInjectable(cls)
|
|
258
264
|
|
|
259
265
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
260
|
-
return any(
|
|
266
|
+
return any(cls in self.__data for cls in get_origins(cls))
|
|
261
267
|
|
|
262
268
|
@property
|
|
263
269
|
def is_locked(self) -> bool:
|
|
264
270
|
return any(injectable.is_locked for injectable in self.__injectables)
|
|
265
271
|
|
|
266
|
-
@property
|
|
267
|
-
def __classes(self) -> frozenset[type]:
|
|
268
|
-
return frozenset(self.__data)
|
|
269
|
-
|
|
270
272
|
@property
|
|
271
273
|
def __injectables(self) -> frozenset[Injectable]:
|
|
272
|
-
return frozenset(self.__data.values())
|
|
274
|
+
return frozenset(injectable for injectable, _ in self.__data.values())
|
|
273
275
|
|
|
274
276
|
@synchronized()
|
|
275
|
-
def update(self, classes: Iterable[type], injectable: Injectable,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
override = True
|
|
277
|
+
def update(self, classes: Iterable[type], injectable: Injectable, mode: _Mode):
|
|
278
|
+
records = {
|
|
279
|
+
cls: Record(injectable, mode)
|
|
280
|
+
for cls in self.__filter_classes(classes, mode)
|
|
281
|
+
}
|
|
281
282
|
|
|
282
|
-
if
|
|
283
|
-
event = ContainerDependenciesUpdated(self,
|
|
283
|
+
if records:
|
|
284
|
+
event = ContainerDependenciesUpdated(self, records.keys(), mode)
|
|
284
285
|
|
|
285
286
|
with self.notify(event):
|
|
286
|
-
|
|
287
|
-
self.__check_if_exists(classes)
|
|
288
|
-
|
|
289
|
-
self.__data.update((cls, injectable) for cls in classes)
|
|
287
|
+
self.__data.update(records)
|
|
290
288
|
|
|
291
289
|
return self
|
|
292
290
|
|
|
@@ -304,28 +302,32 @@ class Container(Broker):
|
|
|
304
302
|
def notify(self, event: Event) -> ContextManager | ContextDecorator:
|
|
305
303
|
return self.__channel.dispatch(event)
|
|
306
304
|
|
|
307
|
-
def
|
|
308
|
-
|
|
305
|
+
def __filter_classes(self, classes: Iterable[type], mode: _Mode) -> Iterator[type]:
|
|
306
|
+
modes = get_args(_Mode)
|
|
307
|
+
rank = modes.index(mode)
|
|
309
308
|
|
|
310
|
-
for cls in
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
314
|
-
)
|
|
309
|
+
for cls in frozenset(get_origins(*classes)):
|
|
310
|
+
try:
|
|
311
|
+
_, current_mode = self.__data[cls]
|
|
315
312
|
|
|
313
|
+
except KeyError:
|
|
314
|
+
pass
|
|
316
315
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
else:
|
|
317
|
+
if mode == current_mode:
|
|
318
|
+
raise RuntimeError(
|
|
319
|
+
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
320
|
+
)
|
|
320
321
|
|
|
322
|
+
elif rank < modes.index(current_mode):
|
|
323
|
+
continue
|
|
321
324
|
|
|
322
|
-
|
|
323
|
-
HIGH = auto()
|
|
324
|
-
LOW = auto()
|
|
325
|
+
yield cls
|
|
325
326
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
|
|
328
|
+
"""
|
|
329
|
+
Module
|
|
330
|
+
"""
|
|
329
331
|
|
|
330
332
|
|
|
331
333
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
@@ -373,14 +375,14 @@ class Module(EventListener, Broker):
|
|
|
373
375
|
*,
|
|
374
376
|
cls: type[Injectable] = NewInjectable,
|
|
375
377
|
inject: bool = True,
|
|
376
|
-
on: type |
|
|
377
|
-
|
|
378
|
+
on: type | _Types = None,
|
|
379
|
+
mode: _Mode = "normal",
|
|
378
380
|
):
|
|
379
381
|
def decorator(wp):
|
|
380
382
|
factory = self.inject(wp, return_factory=True) if inject else wp
|
|
381
383
|
injectable = cls(factory)
|
|
382
384
|
classes = find_types(wp, on)
|
|
383
|
-
self.update(classes, injectable,
|
|
385
|
+
self.update(classes, injectable, mode)
|
|
384
386
|
return wp
|
|
385
387
|
|
|
386
388
|
return decorator(wrapped) if wrapped else decorator
|
|
@@ -389,7 +391,11 @@ class Module(EventListener, Broker):
|
|
|
389
391
|
|
|
390
392
|
def should_be_injectable(self, wrapped: type = None, /):
|
|
391
393
|
def decorator(wp):
|
|
392
|
-
self
|
|
394
|
+
self.update(
|
|
395
|
+
(wp,),
|
|
396
|
+
ShouldBeInjectable(wp),
|
|
397
|
+
mode="fallback",
|
|
398
|
+
)
|
|
393
399
|
return wp
|
|
394
400
|
|
|
395
401
|
return decorator(wrapped) if wrapped else decorator
|
|
@@ -397,16 +403,16 @@ class Module(EventListener, Broker):
|
|
|
397
403
|
def set_constant(
|
|
398
404
|
self,
|
|
399
405
|
instance: _T,
|
|
400
|
-
on: type |
|
|
406
|
+
on: type | _Types = None,
|
|
401
407
|
*,
|
|
402
|
-
|
|
408
|
+
mode: _Mode = "normal",
|
|
403
409
|
) -> _T:
|
|
404
410
|
cls = type(instance)
|
|
405
411
|
self.injectable(
|
|
406
412
|
lambda: instance,
|
|
407
413
|
inject=False,
|
|
408
414
|
on=(cls, on),
|
|
409
|
-
|
|
415
|
+
mode=mode,
|
|
410
416
|
)
|
|
411
417
|
return instance
|
|
412
418
|
|
|
@@ -433,7 +439,7 @@ class Module(EventListener, Broker):
|
|
|
433
439
|
|
|
434
440
|
return decorator(wrapped) if wrapped else decorator
|
|
435
441
|
|
|
436
|
-
def get_instance(self, cls: type[_T], none: bool = True) -> _T | None:
|
|
442
|
+
def get_instance(self, cls: type[_T], *, none: bool = True) -> _T | None:
|
|
437
443
|
try:
|
|
438
444
|
injectable = self[cls]
|
|
439
445
|
except KeyError as exc:
|
|
@@ -448,6 +454,7 @@ class Module(EventListener, Broker):
|
|
|
448
454
|
def get_lazy_instance(
|
|
449
455
|
self,
|
|
450
456
|
cls: type[_T],
|
|
457
|
+
*,
|
|
451
458
|
cache: bool = False,
|
|
452
459
|
) -> Invertible[_T | None]:
|
|
453
460
|
if cache:
|
|
@@ -461,16 +468,12 @@ class Module(EventListener, Broker):
|
|
|
461
468
|
self,
|
|
462
469
|
classes: Iterable[type],
|
|
463
470
|
injectable: Injectable,
|
|
464
|
-
|
|
471
|
+
mode: _Mode = "normal",
|
|
465
472
|
):
|
|
466
|
-
self.__container.update(classes, injectable,
|
|
473
|
+
self.__container.update(classes, injectable, mode)
|
|
467
474
|
return self
|
|
468
475
|
|
|
469
|
-
def use(
|
|
470
|
-
self,
|
|
471
|
-
module: Module,
|
|
472
|
-
priority: ModulePriority = ModulePriority.get_default(),
|
|
473
|
-
):
|
|
476
|
+
def use(self, module: Module, *, priority: _Priority = "low"):
|
|
474
477
|
if module is self:
|
|
475
478
|
raise ModuleError("Module can't be used by itself.")
|
|
476
479
|
|
|
@@ -500,13 +503,14 @@ class Module(EventListener, Broker):
|
|
|
500
503
|
def use_temporarily(
|
|
501
504
|
self,
|
|
502
505
|
module: Module,
|
|
503
|
-
|
|
506
|
+
*,
|
|
507
|
+
priority: _Priority = "low",
|
|
504
508
|
) -> ContextManager | ContextDecorator:
|
|
505
|
-
self.use(module, priority)
|
|
509
|
+
self.use(module, priority=priority)
|
|
506
510
|
yield
|
|
507
511
|
self.stop_using(module)
|
|
508
512
|
|
|
509
|
-
def change_priority(self, module: Module, priority:
|
|
513
|
+
def change_priority(self, module: Module, priority: _Priority):
|
|
510
514
|
event = ModulePriorityUpdated(self, module, priority)
|
|
511
515
|
|
|
512
516
|
with self.notify(event):
|
|
@@ -545,8 +549,8 @@ class Module(EventListener, Broker):
|
|
|
545
549
|
if self.is_locked:
|
|
546
550
|
raise ModuleLockError(f"`{self}` is locked.")
|
|
547
551
|
|
|
548
|
-
def __move_module(self, module: Module, priority:
|
|
549
|
-
last = priority
|
|
552
|
+
def __move_module(self, module: Module, priority: _Priority):
|
|
553
|
+
last = priority != "high"
|
|
550
554
|
|
|
551
555
|
try:
|
|
552
556
|
self.__modules.move_to_end(module, last=last)
|
|
@@ -635,48 +639,45 @@ class InjectedFunction(EventListener):
|
|
|
635
639
|
__slots__ = (
|
|
636
640
|
"__dict__",
|
|
637
641
|
"__signature__",
|
|
642
|
+
"__wrapped__",
|
|
638
643
|
"__dependencies",
|
|
639
644
|
"__owner",
|
|
640
645
|
"__setup_queue",
|
|
641
|
-
"__wrapper",
|
|
642
646
|
)
|
|
643
647
|
|
|
644
648
|
def __init__(self, wrapped: Callable[..., Any], /):
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
self
|
|
649
|
+
try:
|
|
650
|
+
variables = vars(wrapped)
|
|
651
|
+
except TypeError:
|
|
652
|
+
pass
|
|
653
|
+
else:
|
|
654
|
+
self.__update_vars(variables)
|
|
655
|
+
del variables
|
|
656
|
+
|
|
657
|
+
update_wrapper(self, wrapped, updated=())
|
|
654
658
|
self.__dependencies = Dependencies.empty()
|
|
655
659
|
self.__owner = None
|
|
656
660
|
self.__setup_queue = LimitedQueue[Callable[[], Any]]()
|
|
657
|
-
self.on_setup(
|
|
658
|
-
lambda: self.__set_signature(
|
|
659
|
-
inspect.signature(
|
|
660
|
-
wrapped,
|
|
661
|
-
eval_str=True,
|
|
662
|
-
)
|
|
663
|
-
)
|
|
664
|
-
)
|
|
661
|
+
self.on_setup(self.__set_signature)
|
|
665
662
|
|
|
666
663
|
def __repr__(self) -> str:
|
|
667
|
-
return repr(self.
|
|
664
|
+
return repr(self.wrapped)
|
|
668
665
|
|
|
669
666
|
def __str__(self) -> str:
|
|
670
|
-
return str(self.
|
|
667
|
+
return str(self.wrapped)
|
|
671
668
|
|
|
672
669
|
def __call__(self, /, *args, **kwargs) -> Any:
|
|
673
|
-
|
|
670
|
+
for function in self.__setup_queue:
|
|
671
|
+
function()
|
|
672
|
+
|
|
673
|
+
args, kwargs = self.bind(args, kwargs)
|
|
674
|
+
return self.wrapped(*args, **kwargs)
|
|
674
675
|
|
|
675
676
|
def __get__(self, instance: object = None, owner: type = None):
|
|
676
677
|
if instance is None:
|
|
677
678
|
return self
|
|
678
679
|
|
|
679
|
-
return self
|
|
680
|
+
return MethodType(self, instance)
|
|
680
681
|
|
|
681
682
|
def __set_name__(self, owner: type, name: str):
|
|
682
683
|
self.set_owner(owner)
|
|
@@ -685,6 +686,10 @@ class InjectedFunction(EventListener):
|
|
|
685
686
|
def signature(self) -> Signature:
|
|
686
687
|
return self.__signature__
|
|
687
688
|
|
|
689
|
+
@property
|
|
690
|
+
def wrapped(self) -> Callable[..., Any]:
|
|
691
|
+
return self.__wrapped__
|
|
692
|
+
|
|
688
693
|
def bind(
|
|
689
694
|
self,
|
|
690
695
|
args: Iterable[Any] = (),
|
|
@@ -736,12 +741,18 @@ class InjectedFunction(EventListener):
|
|
|
736
741
|
yield
|
|
737
742
|
self.update(event.on_module)
|
|
738
743
|
|
|
739
|
-
def
|
|
740
|
-
|
|
741
|
-
function()
|
|
742
|
-
|
|
744
|
+
def __set_signature(self):
|
|
745
|
+
self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
|
|
743
746
|
return self
|
|
744
747
|
|
|
745
|
-
def
|
|
746
|
-
|
|
747
|
-
|
|
748
|
+
def __update_vars(self, variables: Mapping[str, Any], /):
|
|
749
|
+
def is_dunder(var: str) -> bool:
|
|
750
|
+
return var.startswith("__") and var.endswith("__")
|
|
751
|
+
|
|
752
|
+
restricted_vars = frozenset(var for var in dir(self) if not is_dunder(var))
|
|
753
|
+
variables = (
|
|
754
|
+
(var, value)
|
|
755
|
+
for var, value in variables.items()
|
|
756
|
+
if var not in restricted_vars
|
|
757
|
+
)
|
|
758
|
+
vars(self).update(variables)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-injection"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.2"
|
|
4
4
|
description = "Fast and easy dependency injection framework."
|
|
5
5
|
authors = ["remimd"]
|
|
6
6
|
keywords = ["dependencies", "inject", "injection"]
|
|
@@ -13,7 +13,9 @@ repository = "https://github.com/100nm/python-injection"
|
|
|
13
13
|
python = ">=3.10, <4"
|
|
14
14
|
|
|
15
15
|
[tool.poetry.group.dev.dependencies]
|
|
16
|
+
argon2-cffi = "*"
|
|
16
17
|
blacksheep = "*"
|
|
18
|
+
faker = "*"
|
|
17
19
|
pydantic = "*"
|
|
18
20
|
pytest = "*"
|
|
19
21
|
pytest-asyncio = "*"
|
|
@@ -31,7 +33,7 @@ exclude_lines = [
|
|
|
31
33
|
python_files = "test_*.py"
|
|
32
34
|
addopts = "-p no:warnings --tb=short"
|
|
33
35
|
asyncio_mode = "auto"
|
|
34
|
-
testpaths = "tests/"
|
|
36
|
+
testpaths = "**/tests/"
|
|
35
37
|
|
|
36
38
|
[tool.ruff]
|
|
37
39
|
line-length = 88
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ._pkg import *
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|