Skip to content

Commit 9935446

Browse files
committed
Updated docs and increased test coverage
1 parent 1df2b69 commit 9935446

File tree

5 files changed

+72
-32
lines changed

5 files changed

+72
-32
lines changed

docs/overview/providers.md

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ There are different scope which defines different ways a service is instantiated
128128
Whenever a transient scoped provider is required, a new instance of the provider is created
129129

130130
```python
131+
# main.py
132+
131133
from ellar.di import EllarInjector, transient_scope, injectable
132134

133135
injector = EllarInjector(auto_bind=False)
@@ -141,17 +143,24 @@ injector.container.register(ATransientClass)
141143
# OR
142144
# injector.container.register_transient(ATransientClass)
143145

144-
a_transient_instance_1 = injector.get(ATransientClass)
145-
a_transient_instance_2 = injector.get(ATransientClass)
146+
def validate_transient_scope():
147+
a_transient_instance_1 = injector.get(ATransientClass)
148+
a_transient_instance_2 = injector.get(ATransientClass)
149+
150+
assert a_transient_instance_2 != a_transient_instance_1 # True
146151

147-
assert a_transient_instance_2 != a_transient_instance_1 # True
152+
153+
if __name__ == "__main__":
154+
validate_transient_scope()
148155
```
149156

150157
### **`singleton_scope`**:
151158
A singleton scoped provider is created once throughout the lifespan of the Container instance.
152159

153160
For example:
154161
```python
162+
# main.py
163+
155164
from ellar.di import EllarInjector, singleton_scope, injectable
156165

157166
injector = EllarInjector(auto_bind=False)
@@ -165,18 +174,25 @@ injector.container.register(ASingletonClass)
165174
# OR
166175
# injector.container.register_singleton(ASingletonClass)
167176

168-
a_singleton_instance_1 = injector.get(ASingletonClass)
169-
a_singleton_instance_2 = injector.get(ASingletonClass)
177+
def validate_singleton_scope():
178+
a_singleton_instance_1 = injector.get(ASingletonClass)
179+
a_singleton_instance_2 = injector.get(ASingletonClass)
180+
181+
assert a_singleton_instance_2 == a_singleton_instance_1 # True
170182

171-
assert a_singleton_instance_2 == a_singleton_instance_1 # True
183+
if __name__ == "__main__":
184+
validate_singleton_scope()
172185
```
173186

174187
### **`request_scope`**:
175188
A request scoped provider is instantiated once during the scope of the request. And it's destroyed once the request is complete.
176189
It is important to note that `request_scope` behaves like a `singleton_scope` during HTTPConnection mode and behaves like a `transient_scope` outside HTTPConnection mode.
177190

178191
```python
179-
from ellar.di import EllarInjector, request_scope, injectable, RequestServiceProvider
192+
# main.py
193+
194+
import uvicorn
195+
from ellar.di import EllarInjector, request_scope, injectable
180196

181197
injector = EllarInjector(auto_bind=False)
182198

@@ -187,16 +203,22 @@ class ARequestScopeClass:
187203

188204

189205
injector.container.register(ARequestScopeClass)
190-
request_injector = RequestServiceProvider(injector.container)
191206

192-
request_instance_1 = request_injector.get(ARequestScopeClass)
193-
request_instance_2 = request_injector.get(ARequestScopeClass)
194-
assert request_instance_2 == request_instance_1
195207

196-
request_instance_1 = injector.get(ARequestScopeClass)
197-
request_instance_2 = injector.get(ARequestScopeClass)
208+
async def scoped_request(scope, receive, send):
209+
async with injector.create_asgi_args(scope, receive, send) as request_injector:
210+
request_instance_1 = request_injector.get(ARequestScopeClass)
211+
request_instance_2 = request_injector.get(ARequestScopeClass)
212+
assert request_instance_2 == request_instance_1
213+
214+
request_instance_1 = injector.get(ARequestScopeClass)
215+
request_instance_2 = injector.get(ARequestScopeClass)
216+
217+
assert request_instance_2 != request_instance_1
218+
198219

199-
assert request_instance_2 != request_instance_1
220+
if __name__ == "__main__":
221+
uvicorn.run("main:scoped_request", port=5000, log_level="info")
200222

201223
```
202224

@@ -208,6 +230,8 @@ With `ProviderConfig`, we can register a `base_type` against a `concrete_type` O
208230

209231
For example:
210232
```python
233+
# main.py
234+
211235
from ellar.common import Module
212236
from ellar.core import ModuleBase, Config
213237
from ellar.di import ProviderConfig, injectable, EllarInjector
@@ -244,15 +268,20 @@ class AModule(ModuleBase):
244268
self.ifoo_b = ifoo_b
245269

246270

247-
module_ref = create_module_ref_factor(
271+
def validate_provider_config():
272+
module_ref = create_module_ref_factor(
248273
AModule, container=injector.container, config=Config(),
249-
)
250-
module_ref.run_module_register_services()
251-
a_module_instance: AModule = injector.get(AModule)
274+
)
275+
module_ref.run_module_register_services()
276+
a_module_instance: AModule = injector.get(AModule)
277+
278+
assert isinstance(a_module_instance.ifoo, AFooClass)
279+
assert isinstance(a_module_instance.ifoo_b, AFooClass)
280+
assert a_module_instance.ifoo_b == a_foo_instance
252281

253-
assert isinstance(a_module_instance.ifoo, AFooClass)
254-
assert isinstance(a_module_instance.ifoo_b, AFooClass)
255-
assert a_module_instance.ifoo_b == a_foo_instance
282+
283+
if __name__ == "__main__":
284+
validate_provider_config()
256285
```
257286
In above example, we used `ProviderConfig` as a value type as in the case of `IFooB` type and
258287
as a concrete type as in the case of `IFoo` type.
@@ -262,6 +291,8 @@ We can also achieve the same by overriding `register_providers` in any Module cl
262291

263292
For example:
264293
```python
294+
# main.py
295+
265296
from ellar.common import Module
266297
from ellar.core import ModuleBase, Config
267298
from ellar.di import Container, EllarInjector, injectable, ProviderConfig
@@ -293,15 +324,20 @@ class AModule(ModuleBase):
293324
container.register(IFooB, a_foo_instance)
294325

295326

296-
module_ref = create_module_ref_factor(
327+
def validate_register_services():
328+
module_ref = create_module_ref_factor(
297329
AModule, container=injector.container, config=Config(),
298-
)
299-
module_ref.run_module_register_services()
330+
)
331+
module_ref.run_module_register_services()
332+
333+
ifoo_b = injector.get(IFooB)
334+
ifoo = injector.get(IFoo)
335+
336+
assert isinstance(ifoo_b, AFooClass)
337+
assert isinstance(ifoo, AFooClass)
338+
assert ifoo_b == a_foo_instance
300339

301-
ifoo_b = injector.get(IFooB)
302-
ifoo = injector.get(IFoo)
340+
if __name__ == "__main__":
341+
validate_register_services()
303342

304-
assert isinstance(ifoo_b, AFooClass)
305-
assert isinstance(ifoo, AFooClass)
306-
assert ifoo_b == a_foo_instance
307343
```

ellar/asgi_args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.types import TReceive, TScope, TSend
44

5-
if t.TYPE_CHECKING:
5+
if t.TYPE_CHECKING: # pragma: no cover
66
from ellar.di.providers import Provider
77

88

ellar/di/injector/container.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def register(
6464
f"Cannot register {get_name(base_type)} for abstract class "
6565
f"{get_name(concrete_type)}"
6666
)
67-
except TypeError:
67+
except TypeError: # pragma: no cover
6868
# ignore generic types issues
6969
pass
7070

ellar/di/injector/ellar_injector.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ def get(
9595

9696
binding, binder = self.container.get_binding(interface)
9797
scope = binding.scope
98-
if isinstance(scope, ScopeDecorator):
98+
99+
if isinstance(scope, ScopeDecorator): # pragma: no cover
99100
scope = scope.scope
100101
# Fetch the corresponding Scope instance from the Binder.
101102
scope_binding, _ = binder.get_binding(scope)

tests/test_di/test_injector.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ async def test_injector_update_scoped_context():
110110
assert isinstance(asgi_context.context[Foo1], InstanceProvider)
111111
assert isinstance(asgi_context.context[Foo], ClassProvider)
112112

113+
injector.update_scoped_context(Foo1, Foo1())
114+
assert ASGI_CONTEXT_VAR.get() is None
115+
113116

114117
class TestInjectorModuleFunctions:
115118
def setup(self):

0 commit comments

Comments
 (0)