dependency-injection-pattern 0.0.0__tar.gz → 0.0.1__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.
Files changed (45) hide show
  1. {dependency_injection_pattern-0.0.0/src/dependency_injection_pattern.egg-info → dependency_injection_pattern-0.0.1}/PKG-INFO +4 -4
  2. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/README.md +2 -2
  3. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/pyproject.toml +1 -1
  4. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1/src/dependency_injection_pattern.egg-info}/PKG-INFO +4 -4
  5. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/dependency_injection_pattern.egg-info/requires.txt +1 -1
  6. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/container.py +14 -6
  7. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/provider.py +1 -1
  8. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/tests/test_container.py +66 -2
  9. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/tests/test_examples.py +49 -1
  10. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/LICENSE +0 -0
  11. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/setup.cfg +0 -0
  12. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/dependency_injection_pattern.egg-info/SOURCES.txt +0 -0
  13. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/dependency_injection_pattern.egg-info/dependency_links.txt +0 -0
  14. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/dependency_injection_pattern.egg-info/top_level.txt +0 -0
  15. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/__init__.py +0 -0
  16. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/default_scope.py +0 -0
  17. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/add_exception.py +0 -0
  18. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/container_sealed_error.py +0 -0
  19. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/factory_provide_exception.py +0 -0
  20. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/implementation_exception.py +0 -0
  21. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/provide_exception.py +0 -0
  22. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/exceptions/seal_exception.py +0 -0
  23. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factories/dependency.py +0 -0
  24. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factories/factory.py +0 -0
  25. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factories/scoped_factory.py +0 -0
  26. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factories/singleton_factory.py +0 -0
  27. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factories/transient_factory.py +0 -0
  28. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/factory.py +0 -0
  29. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/helpers.py +0 -0
  30. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/log.py +0 -0
  31. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/named_service.py +0 -0
  32. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/protocols/container.py +0 -0
  33. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/protocols/factory.py +0 -0
  34. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/protocols/provider.py +0 -0
  35. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/__init__.py +0 -0
  36. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/parameter.py +0 -0
  37. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/parameter_kind.py +0 -0
  38. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/parameter_mapper.py +0 -0
  39. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/signature.py +0 -0
  40. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/reflection/undefined.py +0 -0
  41. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/scope.py +0 -0
  42. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/core/service_request.py +0 -0
  43. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/src/di/exceptions.py +0 -0
  44. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/tests/test_factory.py +0 -0
  45. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.1}/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.0
3
+ Version: 0.0.1
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,7 @@ 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==0.0.2
28
+ Requires-Dist: typing-utilities<0.1,>=0.0.3
29
29
  Provides-Extra: test
30
30
  Requires-Dist: pytest>=8.3; extra == "test"
31
31
  Requires-Dist: pytest-cov>=6.1; extra == "test"
@@ -99,6 +99,6 @@ app = provider.provide(Application)
99
99
  value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
100
100
  ```
101
101
 
102
- ## API
102
+ ## Full documentation
103
103
 
104
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
104
+ [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
- ## API
69
+ ## Full documentation
70
70
 
71
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
71
+ [Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
@@ -27,7 +27,7 @@ classifiers = [
27
27
  "Typing :: Typed"
28
28
  ]
29
29
  dependencies = [
30
- "typing-utilities==0.0.2"
30
+ "typing-utilities >= 0.0.3, < 0.1"
31
31
  ]
32
32
  requires-python = ">=3.10"
33
33
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dependency-injection-pattern
3
- Version: 0.0.0
3
+ Version: 0.0.1
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,7 @@ 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==0.0.2
28
+ Requires-Dist: typing-utilities<0.1,>=0.0.3
29
29
  Provides-Extra: test
30
30
  Requires-Dist: pytest>=8.3; extra == "test"
31
31
  Requires-Dist: pytest-cov>=6.1; extra == "test"
@@ -99,6 +99,6 @@ app = provider.provide(Application)
99
99
  value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
100
100
  ```
101
101
 
102
- ## API
102
+ ## Full documentation
103
103
 
104
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
104
+ [Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
@@ -1,4 +1,4 @@
1
- typing-utilities==0.0.2
1
+ typing-utilities<0.1,>=0.0.3
2
2
 
3
3
  [test]
4
4
  pytest>=8.3
@@ -2,6 +2,7 @@ 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
5
6
 
6
7
  from di.core.log import LOG
7
8
  from di.core.reflection import reflect_function
@@ -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
- from di.core.provider import Provider
190
- self.__provider = Provider(self)
191
- self.__add_factory(SingletonFactory, Provider, self.__provider)
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
- return self.__provider
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:
@@ -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
@@ -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()
@@ -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
+