dependency-injection-pattern 0.0.0__tar.gz → 0.0.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.
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/LICENSE +1 -1
- {dependency_injection_pattern-0.0.0/src/dependency_injection_pattern.egg-info → dependency_injection_pattern-0.0.2}/PKG-INFO +5 -4
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/README.md +2 -2
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/pyproject.toml +2 -1
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2/src/dependency_injection_pattern.egg-info}/PKG-INFO +5 -4
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/SOURCES.txt +0 -6
- dependency_injection_pattern-0.0.2/src/dependency_injection_pattern.egg-info/requires.txt +6 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/container.py +15 -7
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/dependency.py +1 -1
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/factory.py +1 -1
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/provider.py +1 -1
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_container.py +66 -2
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_examples.py +49 -1
- dependency_injection_pattern-0.0.0/src/dependency_injection_pattern.egg-info/requires.txt +0 -5
- dependency_injection_pattern-0.0.0/src/di/core/reflection/__init__.py +0 -126
- dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter.py +0 -66
- dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter_kind.py +0 -9
- dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter_mapper.py +0 -55
- dependency_injection_pattern-0.0.0/src/di/core/reflection/signature.py +0 -45
- dependency_injection_pattern-0.0.0/src/di/core/reflection/undefined.py +0 -24
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/setup.cfg +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/dependency_links.txt +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/top_level.txt +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/__init__.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/default_scope.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/add_exception.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/container_sealed_error.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/factory_provide_exception.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/implementation_exception.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/provide_exception.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/seal_exception.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/scoped_factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/singleton_factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/transient_factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/helpers.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/log.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/named_service.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/container.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/provider.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/scope.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/service_request.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/exceptions.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_factory.py +0 -0
- {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_provider.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dependency-injection-pattern
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Python implementation of the Dependency Injection pattern.
|
|
5
5
|
Author-email: Anders Madsen <anders.madsen@alphavue.com>
|
|
6
6
|
License: MIT
|
|
@@ -25,7 +25,8 @@ Classifier: Typing :: Typed
|
|
|
25
25
|
Requires-Python: >=3.10
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
|
-
Requires-Dist: typing-utilities
|
|
28
|
+
Requires-Dist: typing-utilities<0.1,>=0.0.3
|
|
29
|
+
Requires-Dist: runtime-reflection-lite<0.1,>=0.0.0
|
|
29
30
|
Provides-Extra: test
|
|
30
31
|
Requires-Dist: pytest>=8.3; extra == "test"
|
|
31
32
|
Requires-Dist: pytest-cov>=6.1; extra == "test"
|
|
@@ -99,6 +100,6 @@ app = provider.provide(Application)
|
|
|
99
100
|
value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
##
|
|
103
|
+
## Full documentation
|
|
103
104
|
|
|
104
|
-
[Go to
|
|
105
|
+
[Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
|
|
@@ -66,6 +66,6 @@ app = provider.provide(Application)
|
|
|
66
66
|
value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
##
|
|
69
|
+
## Full documentation
|
|
70
70
|
|
|
71
|
-
[Go to
|
|
71
|
+
[Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dependency-injection-pattern
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: Python implementation of the Dependency Injection pattern.
|
|
5
5
|
Author-email: Anders Madsen <anders.madsen@alphavue.com>
|
|
6
6
|
License: MIT
|
|
@@ -25,7 +25,8 @@ Classifier: Typing :: Typed
|
|
|
25
25
|
Requires-Python: >=3.10
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
|
-
Requires-Dist: typing-utilities
|
|
28
|
+
Requires-Dist: typing-utilities<0.1,>=0.0.3
|
|
29
|
+
Requires-Dist: runtime-reflection-lite<0.1,>=0.0.0
|
|
29
30
|
Provides-Extra: test
|
|
30
31
|
Requires-Dist: pytest>=8.3; extra == "test"
|
|
31
32
|
Requires-Dist: pytest-cov>=6.1; extra == "test"
|
|
@@ -99,6 +100,6 @@ app = provider.provide(Application)
|
|
|
99
100
|
value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
##
|
|
103
|
+
## Full documentation
|
|
103
104
|
|
|
104
|
-
[Go to
|
|
105
|
+
[Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
|
|
@@ -31,12 +31,6 @@ src/di/core/factories/transient_factory.py
|
|
|
31
31
|
src/di/core/protocols/container.py
|
|
32
32
|
src/di/core/protocols/factory.py
|
|
33
33
|
src/di/core/protocols/provider.py
|
|
34
|
-
src/di/core/reflection/__init__.py
|
|
35
|
-
src/di/core/reflection/parameter.py
|
|
36
|
-
src/di/core/reflection/parameter_kind.py
|
|
37
|
-
src/di/core/reflection/parameter_mapper.py
|
|
38
|
-
src/di/core/reflection/signature.py
|
|
39
|
-
src/di/core/reflection/undefined.py
|
|
40
34
|
tests/test_container.py
|
|
41
35
|
tests/test_examples.py
|
|
42
36
|
tests/test_factory.py
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/container.py
RENAMED
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
from typing import TypeVar, Callable, Any, cast, overload, TYPE_CHECKING
|
|
3
3
|
from typingutils import is_type, isinstance_typing, get_optional_type, AnyFunction
|
|
4
4
|
from types import FunctionType
|
|
5
|
+
from weakref import ref
|
|
6
|
+
from runtime.reflection.lite import reflect_function
|
|
5
7
|
|
|
6
8
|
from di.core.log import LOG
|
|
7
|
-
from di.core.reflection import reflect_function
|
|
8
9
|
from di.core.scope import Scope
|
|
9
10
|
from di.core.service_request import ServiceRequest
|
|
10
11
|
from di.core.factories.singleton_factory import SingletonFactory
|
|
@@ -34,12 +35,12 @@ class Container:
|
|
|
34
35
|
provider = container.provider() # container is sealed, and no more services can be added beyound this point
|
|
35
36
|
provider.provide(Service) # => Service
|
|
36
37
|
"""
|
|
37
|
-
__slots__ = ["__factories", "__named_factories", "__list_factories", "__default_scope", "__provider"]
|
|
38
|
+
__slots__ = ["__factories", "__named_factories", "__list_factories", "__default_scope", "__provider", "__weakref__"]
|
|
38
39
|
|
|
39
40
|
def __init__(self):
|
|
40
41
|
self.__factories: dict[type[Any], Factory[Any]] = {}
|
|
41
42
|
self.__named_factories: dict[str, tuple[type[Any], Factory[Any]]] = {}
|
|
42
|
-
self.__provider: Provider | None = None
|
|
43
|
+
self.__provider: ref[Provider] | None = None
|
|
43
44
|
self.__default_scope = DefaultScope()
|
|
44
45
|
|
|
45
46
|
@property
|
|
@@ -185,10 +186,11 @@ class Container:
|
|
|
185
186
|
Returns:
|
|
186
187
|
Provider -- The Provider instance
|
|
187
188
|
"""
|
|
189
|
+
from di.core.provider import Provider
|
|
188
190
|
if self.__provider is None:
|
|
189
|
-
|
|
190
|
-
self.__provider =
|
|
191
|
-
self.__add_factory(SingletonFactory, Provider, self.
|
|
191
|
+
provider = Provider(self)
|
|
192
|
+
self.__provider = ref(provider)
|
|
193
|
+
self.__add_factory(SingletonFactory, Provider, self.provider)
|
|
192
194
|
|
|
193
195
|
factories = { **self.__factories, **{ service: factory for service, factory in self.__named_factories.values() } }
|
|
194
196
|
for service, factory in factories.items():
|
|
@@ -204,7 +206,13 @@ class Container:
|
|
|
204
206
|
elif isinstance(factories[dep.service], TransientFactory):
|
|
205
207
|
raise SealException(f"Singleton service '{get_service_name(service)}' depends on transient service {get_service_name(dep.service)}' which is not permitted.")
|
|
206
208
|
|
|
207
|
-
|
|
209
|
+
return provider
|
|
210
|
+
elif provider :=self.__provider():
|
|
211
|
+
return provider
|
|
212
|
+
else:
|
|
213
|
+
provider = Provider(self)
|
|
214
|
+
self.__provider = ref(provider)
|
|
215
|
+
return provider
|
|
208
216
|
|
|
209
217
|
def __add_factory_pre(self, factory_type: type[Factory[T]], *args: Any) -> tuple[str, type[Any]]:
|
|
210
218
|
if self.__provider != None:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
from typingutils import get_optional_type
|
|
3
|
+
from runtime.reflection.lite import Undefined
|
|
3
4
|
|
|
4
5
|
from di.core.helpers import get_provided_service_from_annotation
|
|
5
|
-
from di.core.reflection.undefined import Undefined
|
|
6
6
|
|
|
7
7
|
class Dependency:
|
|
8
8
|
__slots__ = [ "__name", "__service", "__factory", "__optional", "__default" ]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import TypeVar, Callable, Generic, Any, cast
|
|
2
2
|
from typingutils import get_optional_type, issubclass_typing, isinstance_typing
|
|
3
|
+
from runtime.reflection.lite import Undefined, ParameterKind, reflect_function, get_constructor
|
|
3
4
|
|
|
4
|
-
from di.core.reflection import Undefined, ParameterKind, reflect_function, get_constructor
|
|
5
5
|
from di.core.scope import Scope
|
|
6
6
|
from di.core.named_service import NamedService
|
|
7
7
|
from di.core.service_request import ServiceRequest
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/provider.py
RENAMED
|
@@ -14,7 +14,7 @@ class Provider:
|
|
|
14
14
|
"""
|
|
15
15
|
The Provider is connected to the Container and provides the services defined.
|
|
16
16
|
"""
|
|
17
|
-
__slots__ = ["__container"]
|
|
17
|
+
__slots__ = ["__container", "__weakref__"]
|
|
18
18
|
|
|
19
19
|
def __init__(self, container: Container):
|
|
20
20
|
self.__container = container
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_container.py
RENAMED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
from typing import cast, Sequence
|
|
4
4
|
from pytest import raises as assert_raises
|
|
5
5
|
from threading import Thread
|
|
6
|
+
from weakref import ref
|
|
7
|
+
from gc import get_referrers, collect
|
|
6
8
|
|
|
7
|
-
from di import Container, Provider
|
|
9
|
+
from di import Container, Provider, Factory
|
|
8
10
|
from di.exceptions import ImplementationException, AddException, SealException, ProvideException, ContainerSealedError
|
|
9
11
|
|
|
10
|
-
from tests.classes import Service, Options1, DependentService, DependentServiceFail1, IService, Service2
|
|
12
|
+
from tests.classes import Service, Options1, DependentService, DependentServiceFail1, IService, Service2, Service5, DependentService5, Application
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
|
|
@@ -93,3 +95,65 @@ def test_collections():
|
|
|
93
95
|
with assert_raises(ImplementationException):
|
|
94
96
|
container.add_singleton(Sequence[IService], [ Service, Service2 ])
|
|
95
97
|
|
|
98
|
+
def test_lifetime():
|
|
99
|
+
|
|
100
|
+
container = Container()
|
|
101
|
+
container.add_transient(Service5)
|
|
102
|
+
|
|
103
|
+
provider = container.provider()
|
|
104
|
+
provider_id = id(provider)
|
|
105
|
+
svc = provider.provide(Service5)
|
|
106
|
+
provider = ref(provider)
|
|
107
|
+
|
|
108
|
+
collect()
|
|
109
|
+
|
|
110
|
+
assert not provider() # provider should have been collected
|
|
111
|
+
|
|
112
|
+
provider = container.provider()
|
|
113
|
+
assert id(provider) != provider_id # this provider be a new instance
|
|
114
|
+
|
|
115
|
+
provider = ref(provider)
|
|
116
|
+
container = ref(container)
|
|
117
|
+
|
|
118
|
+
collect()
|
|
119
|
+
|
|
120
|
+
# provider and container should have been collected
|
|
121
|
+
assert not provider()
|
|
122
|
+
assert not container()
|
|
123
|
+
|
|
124
|
+
### cleanup
|
|
125
|
+
del container
|
|
126
|
+
del provider
|
|
127
|
+
del svc
|
|
128
|
+
collect()
|
|
129
|
+
###
|
|
130
|
+
|
|
131
|
+
container = Container()
|
|
132
|
+
container.add_transient(Application)
|
|
133
|
+
container.add_transient(Service5)
|
|
134
|
+
container.add_transient(DependentService5)
|
|
135
|
+
provider = container.provider()
|
|
136
|
+
|
|
137
|
+
app = provider.provide(Application)
|
|
138
|
+
svc = app.service_factory()
|
|
139
|
+
|
|
140
|
+
container = ref(container)
|
|
141
|
+
provider = ref(provider)
|
|
142
|
+
|
|
143
|
+
collect()
|
|
144
|
+
|
|
145
|
+
# app has a live reference to Factory[Service5],
|
|
146
|
+
# so neither container or provider should be collected
|
|
147
|
+
assert container()
|
|
148
|
+
assert provider()
|
|
149
|
+
|
|
150
|
+
app = ref(app)
|
|
151
|
+
|
|
152
|
+
collect()
|
|
153
|
+
|
|
154
|
+
# app has been collected thus any references to container and provider
|
|
155
|
+
# has been removed and container and provider has therefore
|
|
156
|
+
# also been collected
|
|
157
|
+
assert not app()
|
|
158
|
+
assert not provider()
|
|
159
|
+
assert not container()
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_examples.py
RENAMED
|
@@ -175,4 +175,52 @@ def test_example_7():
|
|
|
175
175
|
service1, service2 = app.services # => Service1, Service2
|
|
176
176
|
|
|
177
177
|
assert isinstance(service1, Service1)
|
|
178
|
-
assert isinstance(service2, Service2)
|
|
178
|
+
assert isinstance(service2, Service2)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_example_8():
|
|
182
|
+
from di import Container, DefaultScope, Scope, Provider
|
|
183
|
+
from random import Random
|
|
184
|
+
from threading import Thread
|
|
185
|
+
|
|
186
|
+
class RandGen:
|
|
187
|
+
rnd: Random | None
|
|
188
|
+
|
|
189
|
+
def generate(self) -> int:
|
|
190
|
+
if self.rnd:
|
|
191
|
+
return self.rnd.randint(0, 100)
|
|
192
|
+
else:
|
|
193
|
+
return -1
|
|
194
|
+
|
|
195
|
+
def __enter__(self):
|
|
196
|
+
self.rnd = Random()
|
|
197
|
+
return self
|
|
198
|
+
|
|
199
|
+
def __exit__(self):
|
|
200
|
+
del self.rnd
|
|
201
|
+
|
|
202
|
+
container = Container()
|
|
203
|
+
container.add_scoped(RandGen)
|
|
204
|
+
|
|
205
|
+
scope = DefaultScope()
|
|
206
|
+
provider = container.provider()
|
|
207
|
+
|
|
208
|
+
generators: set[int] = set()
|
|
209
|
+
ints: list[int] = []
|
|
210
|
+
|
|
211
|
+
def fn(provider: Provider, scope: Scope):
|
|
212
|
+
for i in range(3):
|
|
213
|
+
gen = provider.provide(RandGen, scope)
|
|
214
|
+
generators.add(id(gen))
|
|
215
|
+
ints.append(gen.generate())
|
|
216
|
+
|
|
217
|
+
thread1 = Thread(target = fn, args = (provider, scope))
|
|
218
|
+
thread1.start()
|
|
219
|
+
thread2 = Thread(target = fn, args = (provider, scope))
|
|
220
|
+
thread2.start()
|
|
221
|
+
thread1.join()
|
|
222
|
+
thread2.join()
|
|
223
|
+
|
|
224
|
+
assert len(generators) == 2
|
|
225
|
+
assert len(ints) == 6
|
|
226
|
+
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
from typing import Any, cast
|
|
2
|
-
from typingutils import AnyFunction
|
|
3
|
-
from types import FrameType, MethodType
|
|
4
|
-
from sys import modules
|
|
5
|
-
from inspect import Parameter as InspectParameter, FrameInfo, signature as get_signature, stack, unwrap
|
|
6
|
-
|
|
7
|
-
from di.core.reflection.undefined import Undefined
|
|
8
|
-
from di.core.reflection.signature import Signature
|
|
9
|
-
from di.core.reflection.parameter import Parameter
|
|
10
|
-
from di.core.reflection.parameter_kind import ParameterKind
|
|
11
|
-
from di.core.reflection.parameter_mapper import ParameterMapper
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def resolve(
|
|
15
|
-
annotation: str,
|
|
16
|
-
globals: dict[str, Any] | None = None,
|
|
17
|
-
builtins: dict[str, Any] | None = None,
|
|
18
|
-
locals: dict[str, Any] | None = None
|
|
19
|
-
) -> Any:
|
|
20
|
-
if globals: # pragma: no cover
|
|
21
|
-
globals = { **globals , **(builtins or __builtins__)}
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
result = eval(annotation, globals, locals)
|
|
25
|
-
return result
|
|
26
|
-
except:
|
|
27
|
-
pass
|
|
28
|
-
|
|
29
|
-
# fallback
|
|
30
|
-
for frame in stack()[1:]: # pragma: no cover
|
|
31
|
-
if frame.filename == __file__:
|
|
32
|
-
continue
|
|
33
|
-
|
|
34
|
-
globals = { **frame.frame.f_globals , **frame.frame.f_builtins}
|
|
35
|
-
locals = frame.frame.f_locals
|
|
36
|
-
|
|
37
|
-
try:
|
|
38
|
-
return eval(annotation, globals, locals)
|
|
39
|
-
except:
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
raise Exception(f"Unable to resolve {annotation}") # pragma: no cover
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_frame(fn: Any, stack: list[FrameInfo], cls: Any | None) -> FrameType | None: # pragma: no cover
|
|
48
|
-
module = None
|
|
49
|
-
|
|
50
|
-
if hasattr(fn, "__module__"):
|
|
51
|
-
module = modules[getattr(fn, "__module__")]
|
|
52
|
-
elif cls and hasattr(cls, "__module__"):
|
|
53
|
-
module = modules[getattr(cls, "__module__")]
|
|
54
|
-
|
|
55
|
-
if module and hasattr(module, "__file__"):
|
|
56
|
-
for frame in stack:
|
|
57
|
-
if frame.filename == module.__file__:
|
|
58
|
-
frame_locals = frame.frame.f_locals.values()
|
|
59
|
-
if cls is not None and cls in frame_locals:
|
|
60
|
-
return frame.frame
|
|
61
|
-
elif fn in frame_locals:
|
|
62
|
-
return frame.frame
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def reflect_function(fn: AnyFunction, cls: object | None = None) -> Signature:
|
|
66
|
-
fn = unwrap(fn)
|
|
67
|
-
sig = get_signature(fn)
|
|
68
|
-
|
|
69
|
-
if hasattr(fn, "__self__") and ( self := getattr(fn, "__self__") ):
|
|
70
|
-
cls = self
|
|
71
|
-
|
|
72
|
-
parameters = list(sig.parameters.values())
|
|
73
|
-
globals: dict[str, Any] | None = getattr(fn, "__globals__") if hasattr(fn, "__globals__") else None
|
|
74
|
-
builtins: dict[str, Any] | None = getattr(fn, "__builtins__") if hasattr(fn, "__builtins__") else None
|
|
75
|
-
locals: dict[str, Any] | None = None
|
|
76
|
-
|
|
77
|
-
if frame := get_frame(fn, stack()[1:], cls): # pragma: no cover
|
|
78
|
-
locals = frame.f_locals
|
|
79
|
-
|
|
80
|
-
if parameters:
|
|
81
|
-
first_param = parameters[0].name.lower()
|
|
82
|
-
|
|
83
|
-
if isinstance(fn, MethodType) and first_param in ("self", "cls"):
|
|
84
|
-
parameters = parameters[1:] # pragma: no cover
|
|
85
|
-
elif first_param in ("self", "cls") and hasattr(fn, "__self__"):
|
|
86
|
-
parameters = parameters[1:] # pragma: no cover
|
|
87
|
-
elif first_param == "self":
|
|
88
|
-
if hasattr(fn, "__call__") and ( call := getattr(fn, "__call__") ): # pragma: no cover
|
|
89
|
-
if hasattr(call, "__text_signature__") and ( text_sig := getattr(call, "__text_signature__") ):
|
|
90
|
-
if cast(str, text_sig).startswith("($self"):
|
|
91
|
-
parameters = parameters[1:]
|
|
92
|
-
|
|
93
|
-
for index, parameter in enumerate(parameters):
|
|
94
|
-
changed = False
|
|
95
|
-
|
|
96
|
-
if isinstance(parameter.annotation, str):
|
|
97
|
-
parameter_type = resolve(parameter.annotation, globals, builtins, locals)
|
|
98
|
-
parameter = parameter.replace(annotation = parameter_type)
|
|
99
|
-
changed = True
|
|
100
|
-
|
|
101
|
-
if changed:
|
|
102
|
-
parameters[index] = parameter
|
|
103
|
-
|
|
104
|
-
if sig.return_annotation is InspectParameter.empty:
|
|
105
|
-
return_type = Undefined
|
|
106
|
-
elif isinstance(sig.return_annotation, str):
|
|
107
|
-
return_type = resolve(sig.return_annotation, globals, builtins, locals)
|
|
108
|
-
else:
|
|
109
|
-
return_type = sig.return_annotation
|
|
110
|
-
|
|
111
|
-
return Signature(
|
|
112
|
-
ParameterMapper(
|
|
113
|
-
[
|
|
114
|
-
Parameter(
|
|
115
|
-
p.name,
|
|
116
|
-
ParameterKind(p.kind),
|
|
117
|
-
Undefined if p.annotation is InspectParameter.empty else p.annotation,
|
|
118
|
-
Undefined if p.default is InspectParameter.empty else p.default
|
|
119
|
-
) for p in parameters
|
|
120
|
-
]
|
|
121
|
-
), return_type
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def get_constructor(cls: type[Any]) -> Signature:
|
|
126
|
-
return reflect_function(getattr(cls, "__init__"), cls)
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
from typing import Any, cast
|
|
2
|
-
from typingutils import get_type_name
|
|
3
|
-
from inspect import Parameter as InspectParameter
|
|
4
|
-
|
|
5
|
-
from di.core.reflection.undefined import Undefined
|
|
6
|
-
from di.core.reflection.parameter_kind import ParameterKind
|
|
7
|
-
|
|
8
|
-
class Parameter:
|
|
9
|
-
__slots__ = ["__name", "__kind", "__doc", "__type", "__default"]
|
|
10
|
-
|
|
11
|
-
def __init__(
|
|
12
|
-
self,
|
|
13
|
-
name: str,
|
|
14
|
-
kind: ParameterKind,
|
|
15
|
-
parameter_type: type[Any],
|
|
16
|
-
default: Any
|
|
17
|
-
):
|
|
18
|
-
self.__name = name
|
|
19
|
-
self.__kind = kind
|
|
20
|
-
self.__type = parameter_type
|
|
21
|
-
self.__default = default
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def name(self) -> str:
|
|
25
|
-
"""The parameter name
|
|
26
|
-
"""
|
|
27
|
-
return self.__name
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def kind(self) -> ParameterKind:
|
|
31
|
-
"""The parameter kind
|
|
32
|
-
"""
|
|
33
|
-
return self.__kind
|
|
34
|
-
|
|
35
|
-
@property
|
|
36
|
-
def parameter_type(self) -> type[Any]:
|
|
37
|
-
"""The parameter type
|
|
38
|
-
"""
|
|
39
|
-
return self.__type
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def default(self) -> Any:
|
|
43
|
-
"""The parameter default
|
|
44
|
-
"""
|
|
45
|
-
return self.__default
|
|
46
|
-
|
|
47
|
-
def __repr__(self) -> str:
|
|
48
|
-
str_type = f": {get_type_name(cast(type, self.__type))}" if self.__type and self.__type != Undefined else ""
|
|
49
|
-
str_default = f"={self.__default}" if self.__default else ""
|
|
50
|
-
return f"{self.__name}{str_type}{str_default}"
|
|
51
|
-
|
|
52
|
-
def __eq__(self, o: object) -> bool: # pragma: no cover
|
|
53
|
-
if isinstance(o, Parameter):
|
|
54
|
-
return (
|
|
55
|
-
self.name == o.name
|
|
56
|
-
and self.kind == o.kind
|
|
57
|
-
and self.parameter_type == o.parameter_type
|
|
58
|
-
)
|
|
59
|
-
elif isinstance(o, InspectParameter):
|
|
60
|
-
return (
|
|
61
|
-
self.name == o.name
|
|
62
|
-
and cast(int, self.kind) == o.kind
|
|
63
|
-
and self.parameter_type == o.annotation
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
return False
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from inspect import Parameter
|
|
3
|
-
|
|
4
|
-
class ParameterKind(Enum):
|
|
5
|
-
POSITIONAL = Parameter.POSITIONAL_ONLY
|
|
6
|
-
KEYWORD = Parameter.KEYWORD_ONLY
|
|
7
|
-
POSITIONAL_OR_KEYWORD = Parameter.POSITIONAL_OR_KEYWORD
|
|
8
|
-
ARGS = Parameter.VAR_POSITIONAL
|
|
9
|
-
KWARGS = Parameter.VAR_KEYWORD
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
from typing import Iterator, Mapping, Iterable, Sequence
|
|
2
|
-
|
|
3
|
-
from di.core.reflection.parameter import Parameter
|
|
4
|
-
|
|
5
|
-
class ParameterMapper(Iterable[Parameter]):
|
|
6
|
-
__slots__ = ["__parameters", "__index"]
|
|
7
|
-
|
|
8
|
-
def __init__(self, parameters: Sequence[Parameter]):
|
|
9
|
-
self.__parameters = list(parameters)
|
|
10
|
-
self.__index: Mapping[str, int] | None = None
|
|
11
|
-
|
|
12
|
-
def __get_index(self) -> Mapping[str, int]:
|
|
13
|
-
if not self.__index:
|
|
14
|
-
self.__index = {
|
|
15
|
-
parameter.name: self.__parameters.index(parameter)
|
|
16
|
-
for parameter in self.__parameters
|
|
17
|
-
}
|
|
18
|
-
return self.__index # pragma: no cover
|
|
19
|
-
|
|
20
|
-
def __getitem__(self, index: int | str) -> Parameter: # pragma: no cover
|
|
21
|
-
if isinstance(index, int):
|
|
22
|
-
return self.__parameters[index]
|
|
23
|
-
else:
|
|
24
|
-
p_index = self.__get_index()
|
|
25
|
-
|
|
26
|
-
if index in p_index:
|
|
27
|
-
return self.__parameters[p_index[index]]
|
|
28
|
-
else:
|
|
29
|
-
raise KeyError(index)
|
|
30
|
-
|
|
31
|
-
def __iter__(self) -> Iterator[Parameter]:
|
|
32
|
-
return iter(self.__parameters)
|
|
33
|
-
|
|
34
|
-
def __len__(self) -> int: # pragma: no cover
|
|
35
|
-
return len(self.__parameters)
|
|
36
|
-
|
|
37
|
-
def __delitem__(self, index: int | str) -> None: # pragma: no cover
|
|
38
|
-
if isinstance(index, int):
|
|
39
|
-
del self.__parameters[index]
|
|
40
|
-
else:
|
|
41
|
-
p_index = self.__get_index()
|
|
42
|
-
|
|
43
|
-
if index in p_index:
|
|
44
|
-
del self.__parameters[p_index[index]]
|
|
45
|
-
else:
|
|
46
|
-
raise KeyError(index)
|
|
47
|
-
|
|
48
|
-
def __contains__(self, name: str) -> bool: # pragma: no cover
|
|
49
|
-
p_index = self.__get_index()
|
|
50
|
-
return name in p_index
|
|
51
|
-
|
|
52
|
-
def __eq__(self, o: object) -> bool: # pragma: no cover
|
|
53
|
-
if isinstance(o, ParameterMapper):
|
|
54
|
-
return self.__parameters == o.__parameters
|
|
55
|
-
return False
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
from typingutils import get_type_name
|
|
3
|
-
|
|
4
|
-
from di.core.reflection.parameter_mapper import ParameterMapper
|
|
5
|
-
|
|
6
|
-
class Signature:
|
|
7
|
-
__slots__ = ["__parameters", "__return_type"]
|
|
8
|
-
|
|
9
|
-
def __init__(
|
|
10
|
-
self,
|
|
11
|
-
parameters: ParameterMapper,
|
|
12
|
-
return_type: type[Any]
|
|
13
|
-
):
|
|
14
|
-
self.__parameters = parameters
|
|
15
|
-
self.__return_type = return_type
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def parameters(self) -> ParameterMapper:
|
|
19
|
-
"""The signature parameters
|
|
20
|
-
"""
|
|
21
|
-
return self.__parameters
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def return_type(self) -> type[Any]:
|
|
25
|
-
"""The signature return type
|
|
26
|
-
"""
|
|
27
|
-
return self.__return_type
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def __repr__(self) -> str:
|
|
32
|
-
str_parameters = ", ".join([ str(p) for p in self.__parameters ]) if self.__parameters else ""
|
|
33
|
-
str_return = f" -> {get_type_name(self.__return_type)}" if self.__return_type else ""
|
|
34
|
-
return f"({str_parameters}){str_return}"
|
|
35
|
-
|
|
36
|
-
def __eq__(self, o: object) -> bool: # pragma: no cover
|
|
37
|
-
if self is o:
|
|
38
|
-
return True
|
|
39
|
-
elif isinstance(o, Signature):
|
|
40
|
-
return (
|
|
41
|
-
self.parameters == o.parameters
|
|
42
|
-
and self.return_type == o.return_type
|
|
43
|
-
)
|
|
44
|
-
return False
|
|
45
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from inspect import Signature, Parameter
|
|
2
|
-
|
|
3
|
-
class Meta(type): # pragma: no cover
|
|
4
|
-
|
|
5
|
-
def __bool__(self):
|
|
6
|
-
return False
|
|
7
|
-
|
|
8
|
-
def __eq__(self, other: object) -> bool:
|
|
9
|
-
return other is Undefined or other in [ Signature.empty, Parameter.empty, None ]
|
|
10
|
-
|
|
11
|
-
def __repr__(cls):
|
|
12
|
-
return "Undefined"
|
|
13
|
-
|
|
14
|
-
def __str__(self) -> str:
|
|
15
|
-
return "Undefined"
|
|
16
|
-
|
|
17
|
-
def __hash__(self) -> int:
|
|
18
|
-
return type.__hash__(self)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Undefined(metaclass=Meta):
|
|
22
|
-
def __new__(cls):
|
|
23
|
-
raise Exception("Undefined type is not supposed to be instantiated") # pragma: no cover
|
|
24
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/__init__.py
RENAMED
|
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
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factory.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/helpers.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/scope.py
RENAMED
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/exceptions.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_factory.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_provider.py
RENAMED
|
File without changes
|