mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 09:57:37 +03:00 
			
		
		
		
	Merge branch 'release/4.18.0' into master
This commit is contained in:
		
						commit
						78f623c05b
					
				
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -52,7 +52,7 @@ test-py2: build
 | 
			
		|||
	coverage report --rcfile=./.coveragerc
 | 
			
		||||
	coverage html --rcfile=./.coveragerc
 | 
			
		||||
 | 
			
		||||
test-py3: build
 | 
			
		||||
test: build
 | 
			
		||||
	# Unit tests with coverage report
 | 
			
		||||
	coverage erase
 | 
			
		||||
	coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*py3*.py
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,4 +23,5 @@ Containers module API docs - :py:mod:`dependency_injector.containers`.
 | 
			
		|||
    dynamic
 | 
			
		||||
    specialization
 | 
			
		||||
    overriding
 | 
			
		||||
    reset_singletons
 | 
			
		||||
    traversal
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								docs/containers/reset_singletons.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								docs/containers/reset_singletons.rst
									
									
									
									
									
										Normal 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::
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,15 @@ that were made in every particular version.
 | 
			
		|||
From version 0.7.6 *Dependency Injector* framework strictly 
 | 
			
		||||
follows `Semantic versioning`_
 | 
			
		||||
 | 
			
		||||
4.18.0
 | 
			
		||||
------
 | 
			
		||||
- 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.
 | 
			
		||||
- Add tests for ``ProvidedInstance`` and ``MethodCaller`` providers.
 | 
			
		||||
- Update Makefile to make Python 3 tests to be a default test command: ``make test``.
 | 
			
		||||
 | 
			
		||||
4.17.0
 | 
			
		||||
------
 | 
			
		||||
- Add ``FastAPI`` + ``SQLAlchemy`` example.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								examples/containers/reset_singletons.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/containers/reset_singletons.py
									
									
									
									
									
										Normal 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()
 | 
			
		||||
							
								
								
									
										26
									
								
								examples/containers/reset_singletons_subcontainers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/containers/reset_singletons_subcontainers.py
									
									
									
									
									
										Normal 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()
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
"""Top-level package."""
 | 
			
		||||
 | 
			
		||||
__version__ = '4.17.0'
 | 
			
		||||
__version__ = '4.18.0'
 | 
			
		||||
"""Version number.
 | 
			
		||||
 | 
			
		||||
:type: str
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,11 +268,14 @@ class DynamicContainer(Container):
 | 
			
		|||
 | 
			
		||||
    def apply_container_providers_overridings(self):
 | 
			
		||||
        """Apply container providers' overridings."""
 | 
			
		||||
        for provider in self.providers.values():
 | 
			
		||||
            if not isinstance(provider, providers.Container):
 | 
			
		||||
                continue
 | 
			
		||||
        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."""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 test_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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -135,6 +135,8 @@ class ContainerTests(unittest.TestCase):
 | 
			
		|||
            provider.override(providers.Object('foo'))
 | 
			
		||||
 | 
			
		||||
    def test_lazy_overriding(self):
 | 
			
		||||
        # See: https://github.com/ets-labs/python-dependency-injector/issues/354
 | 
			
		||||
 | 
			
		||||
        class D(containers.DeclarativeContainer):
 | 
			
		||||
            foo = providers.Object("foo")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -150,4 +152,26 @@ class ContainerTests(unittest.TestCase):
 | 
			
		|||
        b = B(d=D())
 | 
			
		||||
        result = b.a().bar()
 | 
			
		||||
        self.assertEqual(result, 'foo++')
 | 
			
		||||
        # See: https://github.com/ets-labs/python-dependency-injector/issues/354
 | 
			
		||||
 | 
			
		||||
    def test_lazy_overriding_deep(self):
 | 
			
		||||
        # Extended version of test_lazy_overriding()
 | 
			
		||||
 | 
			
		||||
        class D(containers.DeclarativeContainer):
 | 
			
		||||
            foo = providers.Object("foo")
 | 
			
		||||
 | 
			
		||||
        class C(containers.DeclarativeContainer):
 | 
			
		||||
            d = providers.DependenciesContainer()
 | 
			
		||||
            bar = providers.Callable(lambda f: f + "++", d.foo.provided)
 | 
			
		||||
 | 
			
		||||
        class A(containers.DeclarativeContainer):
 | 
			
		||||
            d = providers.DependenciesContainer()
 | 
			
		||||
            c = providers.Container(C, d=d)
 | 
			
		||||
 | 
			
		||||
        class B(containers.DeclarativeContainer):
 | 
			
		||||
            d = providers.DependenciesContainer()
 | 
			
		||||
 | 
			
		||||
            a = providers.Container(A, d=d)
 | 
			
		||||
 | 
			
		||||
        b = B(d=D())
 | 
			
		||||
        result = b.a().c().bar()
 | 
			
		||||
        self.assertEqual(result, 'foo++')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,12 +10,20 @@ class Service:
 | 
			
		|||
        self.value = value
 | 
			
		||||
        self.values = [self.value]
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
    def __call__(self):
 | 
			
		||||
        return self.value
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, item):
 | 
			
		||||
        return self.values[item]
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
        return self.value
 | 
			
		||||
 | 
			
		||||
    def get_closure(self):
 | 
			
		||||
        def closure():
 | 
			
		||||
            return self.value
 | 
			
		||||
        return closure
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Client:
 | 
			
		||||
    def __init__(self, value):
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +53,15 @@ class Container(containers.DeclarativeContainer):
 | 
			
		|||
        Client,
 | 
			
		||||
        value=service.provided.get_value.call(),
 | 
			
		||||
    )
 | 
			
		||||
    client_method_closure_call = providers.Factory(
 | 
			
		||||
        Client,
 | 
			
		||||
        value=service.provided.get_closure.call().call(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    client_provided_call = providers.Factory(
 | 
			
		||||
        Client,
 | 
			
		||||
        value=service.provided.call(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProvidedInstanceTests(unittest.TestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +88,14 @@ class ProvidedInstanceTests(unittest.TestCase):
 | 
			
		|||
        client = self.container.client_method_call()
 | 
			
		||||
        self.assertEqual(client.value, 'foo')
 | 
			
		||||
 | 
			
		||||
    def test_method_closure_call(self):
 | 
			
		||||
        client = self.container.client_method_closure_call()
 | 
			
		||||
        self.assertEqual(client.value, 'foo')
 | 
			
		||||
 | 
			
		||||
    def test_provided_call(self):
 | 
			
		||||
        client = self.container.client_provided_call()
 | 
			
		||||
        self.assertEqual(client.value, 'foo')
 | 
			
		||||
 | 
			
		||||
    def test_call_overridden(self):
 | 
			
		||||
        value = 'bar'
 | 
			
		||||
        with self.container.service.override(Service(value)):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import sys
 | 
			
		|||
 | 
			
		||||
import unittest2 as unittest
 | 
			
		||||
 | 
			
		||||
from dependency_injector import providers
 | 
			
		||||
from dependency_injector import providers, errors
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelectorTests(unittest.TestCase):
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +33,28 @@ class SelectorTests(unittest.TestCase):
 | 
			
		|||
        with self.selector.override('two'):
 | 
			
		||||
            self.assertEqual(provider(), 2)
 | 
			
		||||
 | 
			
		||||
    def test_call_undefined_provider(self):
 | 
			
		||||
        provider = providers.Selector(
 | 
			
		||||
            self.selector,
 | 
			
		||||
            one=providers.Object(1),
 | 
			
		||||
            two=providers.Object(2),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with self.selector.override('three'):
 | 
			
		||||
            with self.assertRaises(errors.Error):
 | 
			
		||||
                provider()
 | 
			
		||||
 | 
			
		||||
    def test_call_selector_is_none(self):
 | 
			
		||||
        provider = providers.Selector(
 | 
			
		||||
            self.selector,
 | 
			
		||||
            one=providers.Object(1),
 | 
			
		||||
            two=providers.Object(2),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with self.selector.override(None):
 | 
			
		||||
            with self.assertRaises(errors.Error):
 | 
			
		||||
                provider()
 | 
			
		||||
 | 
			
		||||
    def test_call_any_callable(self):
 | 
			
		||||
        provider = providers.Selector(
 | 
			
		||||
            functools.partial(next, itertools.cycle(['one', 'two'])),
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +92,19 @@ class SelectorTests(unittest.TestCase):
 | 
			
		|||
        self.assertIs(provider.one, provider_one)
 | 
			
		||||
        self.assertIs(provider.two, provider_two)
 | 
			
		||||
 | 
			
		||||
    def test_getattr_attribute_error(self):
 | 
			
		||||
        provider_one = providers.Object(1)
 | 
			
		||||
        provider_two = providers.Object(2)
 | 
			
		||||
 | 
			
		||||
        provider = providers.Selector(
 | 
			
		||||
            self.selector,
 | 
			
		||||
            one=provider_one,
 | 
			
		||||
            two=provider_two,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(AttributeError):
 | 
			
		||||
            _ = provider.provider_three
 | 
			
		||||
 | 
			
		||||
    def test_call_overridden(self):
 | 
			
		||||
        provider = providers.Selector(self.selector, sample=providers.Object(1))
 | 
			
		||||
        overriding_provider1 = providers.Selector(self.selector, sample=providers.Object(2))
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +116,18 @@ class SelectorTests(unittest.TestCase):
 | 
			
		|||
        with self.selector.override('sample'):
 | 
			
		||||
            self.assertEqual(provider(), 3)
 | 
			
		||||
 | 
			
		||||
    def test_providers_attribute(self):
 | 
			
		||||
        provider_one = providers.Object(1)
 | 
			
		||||
        provider_two = providers.Object(2)
 | 
			
		||||
 | 
			
		||||
        provider = providers.Selector(
 | 
			
		||||
            self.selector,
 | 
			
		||||
            one=provider_one,
 | 
			
		||||
            two=provider_two,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(provider.providers, {'one': provider_one, 'two': provider_two})
 | 
			
		||||
 | 
			
		||||
    def test_deepcopy(self):
 | 
			
		||||
        provider = providers.Selector(self.selector)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user