Container.reset_singletons() (#390)

* Rename container tests

* Add implementation + tests

* Update changelog

* Add examples and docs
This commit is contained in:
Roman Mogylatov 2021-02-05 17:14:10 -05:00 committed by GitHub
parent c4892af31e
commit c964253204
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1056 additions and 725 deletions

View File

@ -23,4 +23,5 @@ Containers module API docs - :py:mod:`dependency_injector.containers`.
dynamic
specialization
overriding
reset_singletons
traversal

View File

@ -0,0 +1,19 @@
Reset container singletons
--------------------------
To reset all container singletons use method ``.reset_singletons()``.
.. literalinclude:: ../../examples/containers/reset_singletons.py
:language: python
:lines: 3-
:emphasize-lines: 16
Method ``.reset_singletons()`` also resets singletons in sub-containers: ``providers.Container`` and
``providers.DependenciesContainer.``
.. literalinclude:: ../../examples/containers/reset_singletons_subcontainers.py
:language: python
:lines: 3-
:emphasize-lines: 21
.. disqus::

View File

@ -9,6 +9,7 @@ follows `Semantic versioning`_
Development version
-------------------
- Add ``container.reset_singleton()`` method to reset container singletons.
- Refactor ``container.apply_container_providers_overridings()`` to use ``container.traverse()``.
This enables deep lazy initialization of ``Container`` providers.
- Add tests for ``Selector`` provider.

View File

@ -0,0 +1,21 @@
"""Container reset singletons example."""
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
service1 = providers.Singleton(object)
service2 = providers.Singleton(object)
if __name__ == '__main__':
container = Container()
service1 = container.service1()
service2 = container.service2()
container.reset_singletons()
assert service1 is not container.service1()
assert service2 is not container.service2()

View File

@ -0,0 +1,26 @@
"""Container reset singletons in subcontainer example."""
from dependency_injector import containers, providers
class SubContainer(containers.DeclarativeContainer):
service = providers.Singleton(object)
class Container(containers.DeclarativeContainer):
service = providers.Singleton(object)
sub = providers.Container(SubContainer)
if __name__ == '__main__':
container = Container()
service1 = container.service()
service2 = container.sub().service()
container.reset_singletons()
assert service1 is not container.service()
assert service2 is not container.sub().service()

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,8 @@ class Container:
def unwire(self) -> None: ...
def init_resources(self) -> Optional[Awaitable]: ...
def shutdown_resources(self) -> Optional[Awaitable]: ...
def apply_container_providers_overridings(self) -> None: ...
def reset_singletons(self) -> None: ...
@overload
def traverse(self, types: Optional[Sequence[Type]] = None) -> Iterator[Provider]: ...
@classmethod

View File

@ -271,6 +271,11 @@ class DynamicContainer(Container):
for provider in self.traverse(types=[providers.Container]):
provider.apply_overridings()
def reset_singletons(self):
"""Reset all container singletons."""
for provider in self.traverse(types=[providers.Singleton]):
provider.reset()
class DeclarativeContainerMetaClass(type):
"""Declarative inversion of control container meta class."""

View File

@ -140,7 +140,7 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
with self.assertRaises(AttributeError):
container_a.override_providers(unknown=providers.Provider())
def test_reset_last_overridding(self):
def test_reset_last_overriding(self):
class _Container(containers.DeclarativeContainer):
p11 = providers.Provider()
@ -164,7 +164,7 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
self.assertEqual(container.p11.overridden,
(overriding_container1.p11,))
def test_reset_last_overridding_when_not_overridden(self):
def test_reset_last_overriding_when_not_overridden(self):
container = ContainerA()
with self.assertRaises(errors.Error):
@ -287,3 +287,51 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
self.assertEqual(_init1.shutdown_counter, 2)
self.assertEqual(_init2.init_counter, 2)
self.assertEqual(_init2.shutdown_counter, 2)
def reset_singletons(self):
class SubSubContainer(containers.DeclarativeContainer):
singleton = providers.Singleton(object)
class SubContainer(containers.DeclarativeContainer):
singleton = providers.Singleton(object)
sub_sub_container = providers.Container(SubSubContainer)
class Container(containers.DeclarativeContainer):
singleton = providers.Singleton(object)
sub_container = providers.Container(SubContainer)
container = Container()
obj11 = container.singleton()
obj12 = container.sub_container().singleton()
obj13 = container.sub_container().sub_sub_container().singleton()
obj21 = container.singleton()
obj22 = container.sub_container().singleton()
obj23 = container.sub_container().sub_sub_container().singleton()
self.assertIs(obj11, obj21)
self.assertIs(obj12, obj22)
self.assertIs(obj13, obj23)
container.reset_singletons()
obj31 = container.singleton()
obj32 = container.sub_container().singleton()
obj33 = container.sub_container().sub_sub_container().singleton()
obj41 = container.singleton()
obj42 = container.sub_container().singleton()
obj43 = container.sub_container().sub_sub_container().singleton()
self.assertIsNot(obj11, obj31)
self.assertIsNot(obj12, obj32)
self.assertIsNot(obj13, obj33)
self.assertIsNot(obj21, obj31)
self.assertIsNot(obj22, obj32)
self.assertIsNot(obj23, obj33)
self.assertIs(obj31, obj41)
self.assertIs(obj32, obj42)
self.assertIs(obj33, obj43)