python-injection 0.8.0__tar.gz → 0.8.1.post0__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.0 → python_injection-0.8.1.post0}/PKG-INFO +27 -28
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/documentation/basic-usage.md +26 -27
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/_pkg.pyi +9 -4
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/event.py +1 -3
- python_injection-0.8.1.post0/injection/common/invertible.py +23 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/lazy.py +6 -8
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/queue.py +7 -12
- python_injection-0.8.1.post0/injection/common/tools/threading.py +13 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/core/module.py +74 -81
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/pyproject.toml +4 -2
- python_injection-0.8.0/injection/common/tools/threading.py +0 -28
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/__init__.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/_pkg.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/__init__.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/tools/__init__.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/common/tools/type.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/core/__init__.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/exceptions.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/integrations/__init__.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/integrations/blacksheep.py +0 -0
- {python_injection-0.8.0 → python_injection-0.8.1.post0}/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.1.post0
|
|
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
139
|
If a class is registered in a package and you want to override it, there is the `override` 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, override=True)
|
|
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
122
|
If a class is registered in a package and you want to override it, there is the `override` 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, override=True)
|
|
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
|
```
|
|
@@ -13,7 +13,7 @@ from typing import (
|
|
|
13
13
|
runtime_checkable,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
from injection.common.
|
|
16
|
+
from injection.common.invertible import Invertible
|
|
17
17
|
|
|
18
18
|
_T = TypeVar("_T")
|
|
19
19
|
|
|
@@ -104,11 +104,16 @@ class Module:
|
|
|
104
104
|
will be raised.
|
|
105
105
|
"""
|
|
106
106
|
|
|
107
|
-
def get_lazy_instance(
|
|
107
|
+
def get_lazy_instance(
|
|
108
|
+
self,
|
|
109
|
+
cls: type[_T],
|
|
110
|
+
cache: bool = ...,
|
|
111
|
+
) -> Invertible[_T | None]:
|
|
108
112
|
"""
|
|
109
113
|
Function used to retrieve an instance associated with the type passed in
|
|
110
|
-
parameter or `None`. Return a `
|
|
111
|
-
in
|
|
114
|
+
parameter or `None`. Return a `Invertible` object. To access the instance
|
|
115
|
+
contained in an invertible object, simply use a wavy line (~).
|
|
116
|
+
With `cache=True`, the instance retrieved will always be the same.
|
|
112
117
|
|
|
113
118
|
Example: instance = ~lazy_instance
|
|
114
119
|
"""
|
|
@@ -4,8 +4,6 @@ from dataclasses import dataclass, field
|
|
|
4
4
|
from typing import ContextManager
|
|
5
5
|
from weakref import WeakSet
|
|
6
6
|
|
|
7
|
-
from injection.common.tools.threading import frozen_collection
|
|
8
|
-
|
|
9
7
|
__all__ = ("Event", "EventChannel", "EventListener")
|
|
10
8
|
|
|
11
9
|
|
|
@@ -28,7 +26,7 @@ class EventChannel:
|
|
|
28
26
|
@contextmanager
|
|
29
27
|
def dispatch(self, event: Event) -> ContextManager | ContextDecorator:
|
|
30
28
|
with ExitStack() as stack:
|
|
31
|
-
for listener in
|
|
29
|
+
for listener in tuple(self.__listeners):
|
|
32
30
|
context_manager = listener.on_event(event)
|
|
33
31
|
|
|
34
32
|
if context_manager is None:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Protocol, TypeVar, runtime_checkable
|
|
5
|
+
|
|
6
|
+
__all__ = ("Invertible", "SimpleInvertible")
|
|
7
|
+
|
|
8
|
+
_T = TypeVar("_T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class Invertible(Protocol[_T]):
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def __invert__(self) -> _T:
|
|
15
|
+
raise NotImplementedError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
19
|
+
class SimpleInvertible(Invertible[_T]):
|
|
20
|
+
callable: Callable[[], _T]
|
|
21
|
+
|
|
22
|
+
def __invert__(self) -> _T:
|
|
23
|
+
return self.callable()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterator, Mapping
|
|
2
2
|
from types import MappingProxyType
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TypeVar
|
|
4
4
|
|
|
5
|
-
from injection.common.
|
|
5
|
+
from injection.common.invertible import Invertible
|
|
6
6
|
|
|
7
7
|
__all__ = ("Lazy", "LazyMapping")
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ _K = TypeVar("_K")
|
|
|
11
11
|
_V = TypeVar("_V")
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class Lazy(
|
|
14
|
+
class Lazy(Invertible[_T]):
|
|
15
15
|
__slots__ = ("__cache", "__is_set")
|
|
16
16
|
|
|
17
17
|
def __init__(self, factory: Callable[[], _T]):
|
|
@@ -25,18 +25,16 @@ class Lazy(Generic[_T]):
|
|
|
25
25
|
return self.__is_set
|
|
26
26
|
|
|
27
27
|
def __setup_cache(self, factory: Callable[[], _T]):
|
|
28
|
-
def
|
|
29
|
-
with thread_lock:
|
|
30
|
-
self.__is_set = True
|
|
31
|
-
|
|
28
|
+
def cache_generator() -> Iterator[_T]:
|
|
32
29
|
nonlocal factory
|
|
33
30
|
cached = factory()
|
|
31
|
+
self.__is_set = True
|
|
34
32
|
del factory
|
|
35
33
|
|
|
36
34
|
while True:
|
|
37
35
|
yield cached
|
|
38
36
|
|
|
39
|
-
self.__cache =
|
|
37
|
+
self.__cache = cache_generator()
|
|
40
38
|
self.__is_set = False
|
|
41
39
|
|
|
42
40
|
|
|
@@ -4,8 +4,6 @@ from collections.abc import Iterator
|
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
5
|
from typing import NoReturn, Protocol, TypeVar
|
|
6
6
|
|
|
7
|
-
from injection.common.tools.threading import thread_lock
|
|
8
|
-
|
|
9
7
|
__all__ = ("LimitedQueue",)
|
|
10
8
|
|
|
11
9
|
_T = TypeVar("_T")
|
|
@@ -34,7 +32,7 @@ class SimpleQueue(Queue[_T]):
|
|
|
34
32
|
return self
|
|
35
33
|
|
|
36
34
|
|
|
37
|
-
class
|
|
35
|
+
class DeadQueue(Queue[_T]):
|
|
38
36
|
__slots__ = ()
|
|
39
37
|
|
|
40
38
|
def __bool__(self) -> bool:
|
|
@@ -44,25 +42,22 @@ class NoQueue(Queue[_T]):
|
|
|
44
42
|
raise StopIteration
|
|
45
43
|
|
|
46
44
|
def add(self, item: _T) -> NoReturn:
|
|
47
|
-
raise TypeError("Queue
|
|
45
|
+
raise TypeError("Queue is dead.")
|
|
48
46
|
|
|
49
47
|
|
|
50
48
|
@dataclass(repr=False, slots=True)
|
|
51
49
|
class LimitedQueue(Queue[_T]):
|
|
52
|
-
|
|
50
|
+
__state: Queue[_T] = field(default_factory=SimpleQueue)
|
|
53
51
|
|
|
54
52
|
def __next__(self) -> _T:
|
|
55
|
-
if not self.__queue:
|
|
56
|
-
raise StopIteration
|
|
57
|
-
|
|
58
53
|
try:
|
|
59
|
-
return next(self.
|
|
54
|
+
return next(self.__state)
|
|
60
55
|
except StopIteration as exc:
|
|
61
|
-
|
|
62
|
-
self.
|
|
56
|
+
if self.__state:
|
|
57
|
+
self.__state = DeadQueue()
|
|
63
58
|
|
|
64
59
|
raise exc
|
|
65
60
|
|
|
66
61
|
def add(self, item: _T):
|
|
67
|
-
self.
|
|
62
|
+
self.__state.add(item)
|
|
68
63
|
return self
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from contextlib import ContextDecorator, contextmanager
|
|
2
|
+
from threading import RLock
|
|
3
|
+
from typing import ContextManager
|
|
4
|
+
|
|
5
|
+
__all__ = ("synchronized",)
|
|
6
|
+
|
|
7
|
+
__lock = RLock()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def synchronized() -> ContextManager | ContextDecorator:
|
|
12
|
+
with __lock:
|
|
13
|
+
yield
|
|
@@ -16,14 +16,9 @@ from collections.abc import (
|
|
|
16
16
|
from contextlib import ContextDecorator, contextmanager, suppress
|
|
17
17
|
from dataclasses import dataclass, field
|
|
18
18
|
from enum import Enum, auto
|
|
19
|
-
from functools import
|
|
20
|
-
partialmethod,
|
|
21
|
-
singledispatchmethod,
|
|
22
|
-
update_wrapper,
|
|
23
|
-
wraps,
|
|
24
|
-
)
|
|
19
|
+
from functools import partialmethod, singledispatchmethod, update_wrapper
|
|
25
20
|
from inspect import Signature, isclass
|
|
26
|
-
from types import UnionType
|
|
21
|
+
from types import MethodType, UnionType
|
|
27
22
|
from typing import (
|
|
28
23
|
Any,
|
|
29
24
|
ClassVar,
|
|
@@ -37,13 +32,10 @@ from typing import (
|
|
|
37
32
|
)
|
|
38
33
|
|
|
39
34
|
from injection.common.event import Event, EventChannel, EventListener
|
|
35
|
+
from injection.common.invertible import Invertible, SimpleInvertible
|
|
40
36
|
from injection.common.lazy import Lazy, LazyMapping
|
|
41
37
|
from injection.common.queue import LimitedQueue
|
|
42
|
-
from injection.common.tools.threading import
|
|
43
|
-
frozen_collection,
|
|
44
|
-
synchronized,
|
|
45
|
-
thread_lock,
|
|
46
|
-
)
|
|
38
|
+
from injection.common.tools.threading import synchronized
|
|
47
39
|
from injection.common.tools.type import find_types, format_type, get_origins
|
|
48
40
|
from injection.exceptions import (
|
|
49
41
|
InjectionError,
|
|
@@ -161,6 +153,13 @@ class Injectable(Protocol[_T]):
|
|
|
161
153
|
raise NotImplementedError
|
|
162
154
|
|
|
163
155
|
|
|
156
|
+
class FallbackInjectable(Injectable[_T], ABC):
|
|
157
|
+
__slots__ = ()
|
|
158
|
+
|
|
159
|
+
def __bool__(self) -> bool:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
|
|
164
163
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
165
164
|
class BaseInjectable(Injectable[_T], ABC):
|
|
166
165
|
factory: Callable[[], _T]
|
|
@@ -193,7 +192,7 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
193
192
|
with suppress(KeyError):
|
|
194
193
|
return self.cache[self.__INSTANCE_KEY]
|
|
195
194
|
|
|
196
|
-
with
|
|
195
|
+
with synchronized():
|
|
197
196
|
instance = self.factory()
|
|
198
197
|
self.cache[self.__INSTANCE_KEY] = instance
|
|
199
198
|
|
|
@@ -201,12 +200,9 @@ class SingletonInjectable(BaseInjectable[_T]):
|
|
|
201
200
|
|
|
202
201
|
|
|
203
202
|
@dataclass(repr=False, frozen=True, slots=True)
|
|
204
|
-
class ShouldBeInjectable(
|
|
203
|
+
class ShouldBeInjectable(FallbackInjectable[_T]):
|
|
205
204
|
cls: type[_T]
|
|
206
205
|
|
|
207
|
-
def __bool__(self) -> bool:
|
|
208
|
-
return False
|
|
209
|
-
|
|
210
206
|
def get_instance(self) -> NoReturn:
|
|
211
207
|
raise InjectionError(f"`{format_type(self.cls)}` should be an injectable.")
|
|
212
208
|
|
|
@@ -264,36 +260,38 @@ class Container(Broker):
|
|
|
264
260
|
|
|
265
261
|
@property
|
|
266
262
|
def __classes(self) -> frozenset[type]:
|
|
267
|
-
return frozenset(self.__data
|
|
263
|
+
return frozenset(self.__data)
|
|
268
264
|
|
|
269
265
|
@property
|
|
270
266
|
def __injectables(self) -> frozenset[Injectable]:
|
|
271
267
|
return frozenset(self.__data.values())
|
|
272
268
|
|
|
269
|
+
@synchronized()
|
|
273
270
|
def update(self, classes: Iterable[type], injectable: Injectable, override: bool):
|
|
274
271
|
classes = frozenset(get_origins(*classes))
|
|
275
272
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
override = True
|
|
273
|
+
if not injectable:
|
|
274
|
+
classes -= self.__classes
|
|
275
|
+
override = True
|
|
280
276
|
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
if classes:
|
|
278
|
+
event = ContainerDependenciesUpdated(self, classes, override)
|
|
283
279
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
with self.notify(event):
|
|
281
|
+
if not override:
|
|
282
|
+
self.__check_if_exists(classes)
|
|
287
283
|
|
|
288
|
-
|
|
284
|
+
self.__data.update((cls, injectable) for cls in classes)
|
|
289
285
|
|
|
290
286
|
return self
|
|
291
287
|
|
|
292
|
-
@synchronized
|
|
288
|
+
@synchronized()
|
|
293
289
|
def unlock(self):
|
|
294
290
|
for injectable in self.__injectables:
|
|
295
291
|
injectable.unlock()
|
|
296
292
|
|
|
293
|
+
return self
|
|
294
|
+
|
|
297
295
|
def add_listener(self, listener: EventListener):
|
|
298
296
|
self.__channel.add_listener(listener)
|
|
299
297
|
return self
|
|
@@ -360,7 +358,7 @@ class Module(EventListener, Broker):
|
|
|
360
358
|
|
|
361
359
|
@property
|
|
362
360
|
def __brokers(self) -> Iterator[Broker]:
|
|
363
|
-
yield from
|
|
361
|
+
yield from tuple(self.__modules)
|
|
364
362
|
yield self.__container
|
|
365
363
|
|
|
366
364
|
def injectable(
|
|
@@ -421,7 +419,7 @@ class Module(EventListener, Broker):
|
|
|
421
419
|
|
|
422
420
|
function = InjectedFunction(wp)
|
|
423
421
|
|
|
424
|
-
@function.
|
|
422
|
+
@function.on_setup
|
|
425
423
|
def listen():
|
|
426
424
|
function.update(self)
|
|
427
425
|
self.add_listener(function)
|
|
@@ -442,8 +440,17 @@ class Module(EventListener, Broker):
|
|
|
442
440
|
instance = injectable.get_instance()
|
|
443
441
|
return cast(cls, instance)
|
|
444
442
|
|
|
445
|
-
def get_lazy_instance(
|
|
446
|
-
|
|
443
|
+
def get_lazy_instance(
|
|
444
|
+
self,
|
|
445
|
+
cls: type[_T],
|
|
446
|
+
cache: bool = False,
|
|
447
|
+
) -> Invertible[_T | None]:
|
|
448
|
+
if cache:
|
|
449
|
+
return Lazy(lambda: self.get_instance(cls))
|
|
450
|
+
|
|
451
|
+
function = self.inject(lambda instance=None: instance)
|
|
452
|
+
function.set_owner(cls)
|
|
453
|
+
return SimpleInvertible(function)
|
|
447
454
|
|
|
448
455
|
def update(
|
|
449
456
|
self,
|
|
@@ -502,11 +509,13 @@ class Module(EventListener, Broker):
|
|
|
502
509
|
|
|
503
510
|
return self
|
|
504
511
|
|
|
505
|
-
@synchronized
|
|
512
|
+
@synchronized()
|
|
506
513
|
def unlock(self):
|
|
507
514
|
for broker in self.__brokers:
|
|
508
515
|
broker.unlock()
|
|
509
516
|
|
|
517
|
+
return self
|
|
518
|
+
|
|
510
519
|
def add_listener(self, listener: EventListener):
|
|
511
520
|
self.__channel.add_listener(listener)
|
|
512
521
|
return self
|
|
@@ -621,64 +630,49 @@ class InjectedFunction(EventListener):
|
|
|
621
630
|
__slots__ = (
|
|
622
631
|
"__dict__",
|
|
623
632
|
"__signature__",
|
|
633
|
+
"__wrapped__",
|
|
624
634
|
"__dependencies",
|
|
625
635
|
"__owner",
|
|
626
636
|
"__setup_queue",
|
|
627
|
-
"__wrapper",
|
|
628
637
|
)
|
|
629
638
|
|
|
630
639
|
def __init__(self, wrapped: Callable[..., Any], /):
|
|
631
640
|
update_wrapper(self, wrapped)
|
|
632
|
-
|
|
633
|
-
@wraps(wrapped)
|
|
634
|
-
def wrapper(*args, **kwargs):
|
|
635
|
-
self.__consume_setup_queue()
|
|
636
|
-
args, kwargs = self.bind(args, kwargs)
|
|
637
|
-
return wrapped(*args, **kwargs)
|
|
638
|
-
|
|
639
|
-
self.__wrapper = wrapper
|
|
640
641
|
self.__dependencies = Dependencies.empty()
|
|
641
642
|
self.__owner = None
|
|
642
643
|
self.__setup_queue = LimitedQueue[Callable[[], Any]]()
|
|
643
|
-
self.
|
|
644
|
-
lambda: self.__set_signature(
|
|
645
|
-
inspect.signature(
|
|
646
|
-
wrapped,
|
|
647
|
-
eval_str=True,
|
|
648
|
-
)
|
|
649
|
-
)
|
|
650
|
-
)
|
|
644
|
+
self.on_setup(self.__set_signature)
|
|
651
645
|
|
|
652
646
|
def __repr__(self) -> str:
|
|
653
|
-
return repr(self.
|
|
647
|
+
return repr(self.wrapped)
|
|
654
648
|
|
|
655
649
|
def __str__(self) -> str:
|
|
656
|
-
return str(self.
|
|
650
|
+
return str(self.wrapped)
|
|
657
651
|
|
|
658
652
|
def __call__(self, /, *args, **kwargs) -> Any:
|
|
659
|
-
|
|
653
|
+
for function in self.__setup_queue:
|
|
654
|
+
function()
|
|
655
|
+
|
|
656
|
+
args, kwargs = self.bind(args, kwargs)
|
|
657
|
+
return self.wrapped(*args, **kwargs)
|
|
660
658
|
|
|
661
659
|
def __get__(self, instance: object = None, owner: type = None):
|
|
662
660
|
if instance is None:
|
|
663
661
|
return self
|
|
664
662
|
|
|
665
|
-
return self
|
|
663
|
+
return MethodType(self, instance)
|
|
666
664
|
|
|
667
665
|
def __set_name__(self, owner: type, name: str):
|
|
668
|
-
|
|
669
|
-
raise TypeError(
|
|
670
|
-
"Function owner must be assigned before dependencies are resolved."
|
|
671
|
-
)
|
|
672
|
-
|
|
673
|
-
if self.__owner:
|
|
674
|
-
raise TypeError("Function owner is already defined.")
|
|
675
|
-
|
|
676
|
-
self.__owner = owner
|
|
666
|
+
self.set_owner(owner)
|
|
677
667
|
|
|
678
668
|
@property
|
|
679
669
|
def signature(self) -> Signature:
|
|
680
670
|
return self.__signature__
|
|
681
671
|
|
|
672
|
+
@property
|
|
673
|
+
def wrapped(self) -> Callable[..., Any]:
|
|
674
|
+
return self.__wrapped__
|
|
675
|
+
|
|
682
676
|
def bind(
|
|
683
677
|
self,
|
|
684
678
|
args: Iterable[Any] = (),
|
|
@@ -696,17 +690,24 @@ class InjectedFunction(EventListener):
|
|
|
696
690
|
)
|
|
697
691
|
return Arguments(bound.args, bound.kwargs)
|
|
698
692
|
|
|
699
|
-
def
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
module,
|
|
704
|
-
self.__owner,
|
|
693
|
+
def set_owner(self, owner: type):
|
|
694
|
+
if self.__dependencies.are_resolved:
|
|
695
|
+
raise TypeError(
|
|
696
|
+
"Function owner must be assigned before dependencies are resolved."
|
|
705
697
|
)
|
|
706
698
|
|
|
699
|
+
if self.__owner:
|
|
700
|
+
raise TypeError("Function owner is already defined.")
|
|
701
|
+
|
|
702
|
+
self.__owner = owner
|
|
703
|
+
return self
|
|
704
|
+
|
|
705
|
+
@synchronized()
|
|
706
|
+
def update(self, module: Module):
|
|
707
|
+
self.__dependencies = Dependencies.resolve(self.signature, module, self.__owner)
|
|
707
708
|
return self
|
|
708
709
|
|
|
709
|
-
def
|
|
710
|
+
def on_setup(self, wrapped: Callable[[], Any] = None, /):
|
|
710
711
|
def decorator(wp):
|
|
711
712
|
self.__setup_queue.add(wp)
|
|
712
713
|
return wp
|
|
@@ -723,14 +724,6 @@ class InjectedFunction(EventListener):
|
|
|
723
724
|
yield
|
|
724
725
|
self.update(event.on_module)
|
|
725
726
|
|
|
726
|
-
def
|
|
727
|
-
|
|
728
|
-
function()
|
|
729
|
-
|
|
730
|
-
return self
|
|
731
|
-
|
|
732
|
-
def __set_signature(self, signature: Signature):
|
|
733
|
-
with thread_lock:
|
|
734
|
-
self.__signature__ = signature
|
|
735
|
-
|
|
727
|
+
def __set_signature(self):
|
|
728
|
+
self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
|
|
736
729
|
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-injection"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.1.post0"
|
|
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,28 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable, Collection, Iterator
|
|
2
|
-
from functools import wraps
|
|
3
|
-
from threading import RLock
|
|
4
|
-
from typing import Any, TypeVar
|
|
5
|
-
|
|
6
|
-
__all__ = ("frozen_collection", "synchronized", "thread_lock")
|
|
7
|
-
|
|
8
|
-
_T = TypeVar("_T")
|
|
9
|
-
thread_lock = RLock()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def synchronized(function: Callable[..., Any] = None, /):
|
|
13
|
-
def decorator(fn):
|
|
14
|
-
@wraps(fn)
|
|
15
|
-
def wrapper(*args, **kwargs):
|
|
16
|
-
with thread_lock:
|
|
17
|
-
return fn(*args, **kwargs)
|
|
18
|
-
|
|
19
|
-
return wrapper
|
|
20
|
-
|
|
21
|
-
return decorator(function) if function else decorator
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def frozen_collection(collection: Collection[_T]) -> Iterator[_T]:
|
|
25
|
-
with thread_lock:
|
|
26
|
-
t = tuple(collection)
|
|
27
|
-
|
|
28
|
-
yield from t
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_injection-0.8.0 → python_injection-0.8.1.post0}/injection/integrations/blacksheep.py
RENAMED
|
File without changes
|
|
File without changes
|