mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-01-31 11:51:39 +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 report --rcfile=./.coveragerc
|
||||||
coverage html --rcfile=./.coveragerc
|
coverage html --rcfile=./.coveragerc
|
||||||
|
|
||||||
test-py3: build
|
test: build
|
||||||
# Unit tests with coverage report
|
# Unit tests with coverage report
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run --rcfile=./.coveragerc -m unittest2 discover -s tests/unit/ -p test_*py3*.py
|
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
|
dynamic
|
||||||
specialization
|
specialization
|
||||||
overriding
|
overriding
|
||||||
|
reset_singletons
|
||||||
traversal
|
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
|
From version 0.7.6 *Dependency Injector* framework strictly
|
||||||
follows `Semantic versioning`_
|
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
|
4.17.0
|
||||||
------
|
------
|
||||||
- Add ``FastAPI`` + ``SQLAlchemy`` example.
|
- 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."""
|
"""Top-level package."""
|
||||||
|
|
||||||
__version__ = '4.17.0'
|
__version__ = '4.18.0'
|
||||||
"""Version number.
|
"""Version number.
|
||||||
|
|
||||||
:type: str
|
:type: str
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,8 @@ class Container:
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
def init_resources(self) -> Optional[Awaitable]: ...
|
def init_resources(self) -> Optional[Awaitable]: ...
|
||||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
||||||
|
def apply_container_providers_overridings(self) -> None: ...
|
||||||
|
def reset_singletons(self) -> None: ...
|
||||||
@overload
|
@overload
|
||||||
def traverse(self, types: Optional[Sequence[Type]] = None) -> Iterator[Provider]: ...
|
def traverse(self, types: Optional[Sequence[Type]] = None) -> Iterator[Provider]: ...
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -268,11 +268,14 @@ class DynamicContainer(Container):
|
||||||
|
|
||||||
def apply_container_providers_overridings(self):
|
def apply_container_providers_overridings(self):
|
||||||
"""Apply container providers' overridings."""
|
"""Apply container providers' overridings."""
|
||||||
for provider in self.providers.values():
|
for provider in self.traverse(types=[providers.Container]):
|
||||||
if not isinstance(provider, providers.Container):
|
|
||||||
continue
|
|
||||||
provider.apply_overridings()
|
provider.apply_overridings()
|
||||||
|
|
||||||
|
def reset_singletons(self):
|
||||||
|
"""Reset all container singletons."""
|
||||||
|
for provider in self.traverse(types=[providers.Singleton]):
|
||||||
|
provider.reset()
|
||||||
|
|
||||||
|
|
||||||
class DeclarativeContainerMetaClass(type):
|
class DeclarativeContainerMetaClass(type):
|
||||||
"""Declarative inversion of control container meta class."""
|
"""Declarative inversion of control container meta class."""
|
||||||
|
|
|
@ -140,7 +140,7 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
container_a.override_providers(unknown=providers.Provider())
|
container_a.override_providers(unknown=providers.Provider())
|
||||||
|
|
||||||
def test_reset_last_overridding(self):
|
def test_reset_last_overriding(self):
|
||||||
class _Container(containers.DeclarativeContainer):
|
class _Container(containers.DeclarativeContainer):
|
||||||
p11 = providers.Provider()
|
p11 = providers.Provider()
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
|
||||||
self.assertEqual(container.p11.overridden,
|
self.assertEqual(container.p11.overridden,
|
||||||
(overriding_container1.p11,))
|
(overriding_container1.p11,))
|
||||||
|
|
||||||
def test_reset_last_overridding_when_not_overridden(self):
|
def test_reset_last_overriding_when_not_overridden(self):
|
||||||
container = ContainerA()
|
container = ContainerA()
|
||||||
|
|
||||||
with self.assertRaises(errors.Error):
|
with self.assertRaises(errors.Error):
|
||||||
|
@ -287,3 +287,51 @@ class DeclarativeContainerInstanceTests(unittest.TestCase):
|
||||||
self.assertEqual(_init1.shutdown_counter, 2)
|
self.assertEqual(_init1.shutdown_counter, 2)
|
||||||
self.assertEqual(_init2.init_counter, 2)
|
self.assertEqual(_init2.init_counter, 2)
|
||||||
self.assertEqual(_init2.shutdown_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'))
|
provider.override(providers.Object('foo'))
|
||||||
|
|
||||||
def test_lazy_overriding(self):
|
def test_lazy_overriding(self):
|
||||||
|
# See: https://github.com/ets-labs/python-dependency-injector/issues/354
|
||||||
|
|
||||||
class D(containers.DeclarativeContainer):
|
class D(containers.DeclarativeContainer):
|
||||||
foo = providers.Object("foo")
|
foo = providers.Object("foo")
|
||||||
|
|
||||||
|
@ -150,4 +152,26 @@ class ContainerTests(unittest.TestCase):
|
||||||
b = B(d=D())
|
b = B(d=D())
|
||||||
result = b.a().bar()
|
result = b.a().bar()
|
||||||
self.assertEqual(result, 'foo++')
|
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.value = value
|
||||||
self.values = [self.value]
|
self.values = [self.value]
|
||||||
|
|
||||||
def get_value(self):
|
def __call__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return self.values[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:
|
class Client:
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
|
@ -45,6 +53,15 @@ class Container(containers.DeclarativeContainer):
|
||||||
Client,
|
Client,
|
||||||
value=service.provided.get_value.call(),
|
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):
|
class ProvidedInstanceTests(unittest.TestCase):
|
||||||
|
@ -71,6 +88,14 @@ class ProvidedInstanceTests(unittest.TestCase):
|
||||||
client = self.container.client_method_call()
|
client = self.container.client_method_call()
|
||||||
self.assertEqual(client.value, 'foo')
|
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):
|
def test_call_overridden(self):
|
||||||
value = 'bar'
|
value = 'bar'
|
||||||
with self.container.service.override(Service(value)):
|
with self.container.service.override(Service(value)):
|
||||||
|
|
|
@ -6,7 +6,7 @@ import sys
|
||||||
|
|
||||||
import unittest2 as unittest
|
import unittest2 as unittest
|
||||||
|
|
||||||
from dependency_injector import providers
|
from dependency_injector import providers, errors
|
||||||
|
|
||||||
|
|
||||||
class SelectorTests(unittest.TestCase):
|
class SelectorTests(unittest.TestCase):
|
||||||
|
@ -33,6 +33,28 @@ class SelectorTests(unittest.TestCase):
|
||||||
with self.selector.override('two'):
|
with self.selector.override('two'):
|
||||||
self.assertEqual(provider(), 2)
|
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):
|
def test_call_any_callable(self):
|
||||||
provider = providers.Selector(
|
provider = providers.Selector(
|
||||||
functools.partial(next, itertools.cycle(['one', 'two'])),
|
functools.partial(next, itertools.cycle(['one', 'two'])),
|
||||||
|
@ -70,6 +92,19 @@ class SelectorTests(unittest.TestCase):
|
||||||
self.assertIs(provider.one, provider_one)
|
self.assertIs(provider.one, provider_one)
|
||||||
self.assertIs(provider.two, provider_two)
|
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):
|
def test_call_overridden(self):
|
||||||
provider = providers.Selector(self.selector, sample=providers.Object(1))
|
provider = providers.Selector(self.selector, sample=providers.Object(1))
|
||||||
overriding_provider1 = providers.Selector(self.selector, sample=providers.Object(2))
|
overriding_provider1 = providers.Selector(self.selector, sample=providers.Object(2))
|
||||||
|
@ -81,6 +116,18 @@ class SelectorTests(unittest.TestCase):
|
||||||
with self.selector.override('sample'):
|
with self.selector.override('sample'):
|
||||||
self.assertEqual(provider(), 3)
|
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):
|
def test_deepcopy(self):
|
||||||
provider = providers.Selector(self.selector)
|
provider = providers.Selector(self.selector)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user