mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2024-12-01 14:04:01 +03:00
494 lines
14 KiB
Python
494 lines
14 KiB
Python
|
"""Main container instance tests."""
|
||
|
|
||
|
from dependency_injector import containers, providers, errors
|
||
|
from pytest import raises
|
||
|
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
p12 = providers.Provider()
|
||
|
|
||
|
|
||
|
def test_providers_attribute():
|
||
|
container_1 = Container()
|
||
|
container_2 = Container()
|
||
|
|
||
|
assert container_1.p11 is not container_2.p11
|
||
|
assert container_1.p12 is not container_2.p12
|
||
|
assert container_1.providers != container_2.providers
|
||
|
|
||
|
|
||
|
def test_dependencies_attribute():
|
||
|
container = Container()
|
||
|
container.a1 = providers.Dependency()
|
||
|
container.a2 = providers.DependenciesContainer()
|
||
|
assert container.dependencies == {"a1": container.a1, "a2": container.a2}
|
||
|
|
||
|
|
||
|
def test_set_get_del_providers():
|
||
|
p13 = providers.Provider()
|
||
|
|
||
|
container_1 = Container()
|
||
|
container_2 = Container()
|
||
|
|
||
|
container_1.p13 = p13
|
||
|
container_2.p13 = p13
|
||
|
|
||
|
assert Container.providers == dict(p11=Container.p11, p12=Container.p12)
|
||
|
assert Container.cls_providers, dict(p11=Container.p11, p12=Container.p12)
|
||
|
|
||
|
assert container_1.providers == dict(p11=container_1.p11, p12=container_1.p12, p13=p13)
|
||
|
assert container_2.providers == dict(p11=container_2.p11, p12=container_2.p12, p13=p13)
|
||
|
|
||
|
del container_1.p13
|
||
|
assert container_1.providers == dict(p11=container_1.p11, p12=container_1.p12)
|
||
|
|
||
|
del container_2.p13
|
||
|
assert container_2.providers == dict(p11=container_2.p11, p12=container_2.p12)
|
||
|
|
||
|
del container_1.p11
|
||
|
del container_1.p12
|
||
|
assert container_1.providers == dict()
|
||
|
assert Container.providers == dict(p11=Container.p11, p12=Container.p12)
|
||
|
|
||
|
del container_2.p11
|
||
|
del container_2.p12
|
||
|
assert container_2.providers == dict()
|
||
|
assert Container.providers == dict(p11=Container.p11, p12=Container.p12)
|
||
|
|
||
|
|
||
|
def test_set_invalid_provider_type():
|
||
|
container = Container()
|
||
|
container.provider_type = providers.Object
|
||
|
|
||
|
with raises(errors.Error):
|
||
|
container.px = providers.Provider()
|
||
|
|
||
|
assert Container.provider_type is containers.DeclarativeContainer.provider_type
|
||
|
|
||
|
|
||
|
def test_set_providers():
|
||
|
p13 = providers.Provider()
|
||
|
p14 = providers.Provider()
|
||
|
container = Container()
|
||
|
|
||
|
container.set_providers(p13=p13, p14=p14)
|
||
|
|
||
|
assert container.p13 is p13
|
||
|
assert container.p14 is p14
|
||
|
|
||
|
|
||
|
def test_override():
|
||
|
class _Container(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
p12 = providers.Provider()
|
||
|
|
||
|
container = _Container()
|
||
|
overriding_container1 = _OverridingContainer1()
|
||
|
overriding_container2 = _OverridingContainer2()
|
||
|
|
||
|
container.override(overriding_container1)
|
||
|
container.override(overriding_container2)
|
||
|
|
||
|
assert container.overridden == (overriding_container1, overriding_container2)
|
||
|
assert container.p11.overridden == (overriding_container1.p11, overriding_container2.p11)
|
||
|
|
||
|
assert _Container.overridden == tuple()
|
||
|
assert _Container.p11.overridden == tuple()
|
||
|
|
||
|
|
||
|
def test_override_with_it():
|
||
|
container = Container()
|
||
|
with raises(errors.Error):
|
||
|
container.override(container)
|
||
|
|
||
|
|
||
|
def test_override_providers():
|
||
|
p1 = providers.Provider()
|
||
|
p2 = providers.Provider()
|
||
|
container = Container()
|
||
|
|
||
|
container.override_providers(p11=p1, p12=p2)
|
||
|
|
||
|
assert container.p11.last_overriding is p1
|
||
|
assert container.p12.last_overriding is p2
|
||
|
|
||
|
|
||
|
def test_override_providers_context_manager():
|
||
|
p1 = providers.Provider()
|
||
|
p2 = providers.Provider()
|
||
|
container = Container()
|
||
|
|
||
|
with container.override_providers(p11=p1, p12=p2) as context_container:
|
||
|
assert container is context_container
|
||
|
assert container.p11.last_overriding is p1
|
||
|
assert container.p12.last_overriding is p2
|
||
|
|
||
|
assert container.p11.last_overriding is None
|
||
|
assert container.p12.last_overriding is None
|
||
|
|
||
|
|
||
|
def test_override_providers_with_unknown_provider():
|
||
|
container = Container()
|
||
|
with raises(AttributeError):
|
||
|
container.override_providers(unknown=providers.Provider())
|
||
|
|
||
|
|
||
|
def test_reset_last_overriding():
|
||
|
class _Container(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
p12 = providers.Provider()
|
||
|
|
||
|
container = _Container()
|
||
|
overriding_container1 = _OverridingContainer1()
|
||
|
overriding_container2 = _OverridingContainer2()
|
||
|
|
||
|
container.override(overriding_container1)
|
||
|
container.override(overriding_container2)
|
||
|
container.reset_last_overriding()
|
||
|
|
||
|
assert container.overridden == (overriding_container1,)
|
||
|
assert container.p11.overridden, (overriding_container1.p11,)
|
||
|
|
||
|
|
||
|
def test_reset_last_overriding_when_not_overridden():
|
||
|
container = Container()
|
||
|
with raises(errors.Error):
|
||
|
container.reset_last_overriding()
|
||
|
|
||
|
|
||
|
def test_reset_override():
|
||
|
class _Container(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer1(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
|
||
|
class _OverridingContainer2(containers.DeclarativeContainer):
|
||
|
p11 = providers.Provider()
|
||
|
p12 = providers.Provider()
|
||
|
|
||
|
container = _Container()
|
||
|
overriding_container1 = _OverridingContainer1()
|
||
|
overriding_container2 = _OverridingContainer2()
|
||
|
|
||
|
container.override(overriding_container1)
|
||
|
container.override(overriding_container2)
|
||
|
container.reset_override()
|
||
|
|
||
|
assert container.overridden == tuple()
|
||
|
assert container.p11.overridden == tuple()
|
||
|
|
||
|
|
||
|
def test_init_and_shutdown_resources_ordering():
|
||
|
"""Test init and shutdown resources.
|
||
|
|
||
|
Methods .init_resources() and .shutdown_resources() should respect resources dependencies.
|
||
|
Initialization should first initialize resources without dependencies and then provide
|
||
|
these resources to other resources. Resources shutdown should follow the same rule: first
|
||
|
shutdown resources without initialized dependencies and then continue correspondingly
|
||
|
until all resources are shutdown.
|
||
|
"""
|
||
|
initialized_resources = []
|
||
|
shutdown_resources = []
|
||
|
|
||
|
def _resource(name, **_):
|
||
|
initialized_resources.append(name)
|
||
|
yield name
|
||
|
shutdown_resources.append(name)
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
resource1 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r1",
|
||
|
)
|
||
|
resource2 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r2",
|
||
|
r1=resource1,
|
||
|
)
|
||
|
resource3 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r3",
|
||
|
r2=resource2,
|
||
|
)
|
||
|
|
||
|
container = Container()
|
||
|
|
||
|
container.init_resources()
|
||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||
|
assert shutdown_resources == []
|
||
|
|
||
|
container.shutdown_resources()
|
||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||
|
|
||
|
container.init_resources()
|
||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||
|
|
||
|
container.shutdown_resources()
|
||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||
|
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
||
|
|
||
|
|
||
|
def test_shutdown_resources_circular_dependencies_breaker():
|
||
|
def _resource(name, **_):
|
||
|
yield name
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
resource1 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r1",
|
||
|
)
|
||
|
resource2 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r2",
|
||
|
r1=resource1,
|
||
|
)
|
||
|
resource3 = providers.Resource(
|
||
|
_resource,
|
||
|
name="r3",
|
||
|
r2=resource2,
|
||
|
)
|
||
|
|
||
|
container = Container()
|
||
|
container.init_resources()
|
||
|
|
||
|
# Create circular dependency after initialization (r3 -> r2 -> r1 -> r3 -> ...)
|
||
|
container.resource1.add_kwargs(r3=container.resource3)
|
||
|
|
||
|
with raises(RuntimeError, match="Unable to resolve resources shutdown order"):
|
||
|
container.shutdown_resources()
|
||
|
|
||
|
|
||
|
def test_init_shutdown_nested_resources():
|
||
|
def _init1():
|
||
|
_init1.init_counter += 1
|
||
|
yield
|
||
|
_init1.shutdown_counter += 1
|
||
|
|
||
|
_init1.init_counter = 0
|
||
|
_init1.shutdown_counter = 0
|
||
|
|
||
|
def _init2():
|
||
|
_init2.init_counter += 1
|
||
|
yield
|
||
|
_init2.shutdown_counter += 1
|
||
|
|
||
|
_init2.init_counter = 0
|
||
|
_init2.shutdown_counter = 0
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
|
||
|
service = providers.Factory(
|
||
|
dict,
|
||
|
resource1=providers.Resource(_init1),
|
||
|
resource2=providers.Resource(_init2),
|
||
|
)
|
||
|
|
||
|
container = Container()
|
||
|
assert _init1.init_counter == 0
|
||
|
assert _init1.shutdown_counter == 0
|
||
|
assert _init2.init_counter == 0
|
||
|
assert _init2.shutdown_counter == 0
|
||
|
|
||
|
container.init_resources()
|
||
|
assert _init1.init_counter == 1
|
||
|
assert _init1.shutdown_counter == 0
|
||
|
assert _init2.init_counter == 1
|
||
|
assert _init2.shutdown_counter == 0
|
||
|
|
||
|
container.shutdown_resources()
|
||
|
assert _init1.init_counter == 1
|
||
|
assert _init1.shutdown_counter == 1
|
||
|
assert _init2.init_counter == 1
|
||
|
assert _init2.shutdown_counter == 1
|
||
|
|
||
|
container.init_resources()
|
||
|
container.shutdown_resources()
|
||
|
assert _init1.init_counter == 2
|
||
|
assert _init1.shutdown_counter == 2
|
||
|
assert _init2.init_counter == 2
|
||
|
assert _init2.shutdown_counter == 2
|
||
|
|
||
|
|
||
|
def test_reset_singletons():
|
||
|
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()
|
||
|
|
||
|
assert obj11 is obj21
|
||
|
assert obj12 is obj22
|
||
|
assert obj13 is 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()
|
||
|
|
||
|
assert obj11 is not obj31
|
||
|
assert obj12 is not obj32
|
||
|
assert obj13 is not obj33
|
||
|
|
||
|
assert obj21 is not obj31
|
||
|
assert obj22 is not obj32
|
||
|
assert obj23 is not obj33
|
||
|
|
||
|
assert obj31 is obj41
|
||
|
assert obj32 is obj42
|
||
|
assert obj33 is obj43
|
||
|
|
||
|
|
||
|
def test_reset_singletons_context_manager():
|
||
|
class Item:
|
||
|
def __init__(self, dependency):
|
||
|
self.dependency = dependency
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
dependent = providers.Singleton(object)
|
||
|
singleton = providers.Singleton(Item, dependency=dependent)
|
||
|
|
||
|
container = Container()
|
||
|
|
||
|
instance1 = container.singleton()
|
||
|
with container.reset_singletons():
|
||
|
instance2 = container.singleton()
|
||
|
instance3 = container.singleton()
|
||
|
|
||
|
assert len({instance1, instance2, instance3}) == 3
|
||
|
assert len({instance1.dependency, instance2.dependency, instance3.dependency}) == 3
|
||
|
|
||
|
|
||
|
def test_reset_singletons_context_manager_as_attribute():
|
||
|
container = containers.DeclarativeContainer()
|
||
|
with container.reset_singletons() as alias:
|
||
|
pass
|
||
|
assert container is alias
|
||
|
|
||
|
|
||
|
def test_check_dependencies():
|
||
|
class SubContainer(containers.DeclarativeContainer):
|
||
|
dependency = providers.Dependency()
|
||
|
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
dependency = providers.Dependency()
|
||
|
dependencies_container = providers.DependenciesContainer()
|
||
|
provider = providers.List(dependencies_container.dependency)
|
||
|
sub_container = providers.Container(SubContainer)
|
||
|
|
||
|
container = Container()
|
||
|
|
||
|
with raises(errors.Error) as exception_info:
|
||
|
container.check_dependencies()
|
||
|
|
||
|
assert "Container \"Container\" has undefined dependencies:" in str(exception_info.value)
|
||
|
assert "\"Container.dependency\"" in str(exception_info.value)
|
||
|
assert "\"Container.dependencies_container.dependency\"" in str(exception_info.value)
|
||
|
assert "\"Container.sub_container.dependency\"" in str(exception_info.value)
|
||
|
|
||
|
|
||
|
def test_check_dependencies_all_defined():
|
||
|
class Container(containers.DeclarativeContainer):
|
||
|
dependency = providers.Dependency()
|
||
|
|
||
|
container = Container(dependency="provided")
|
||
|
result = container.check_dependencies()
|
||
|
|
||
|
assert result is None
|
||
|
|
||
|
|
||
|
def test_assign_parent():
|
||
|
parent = providers.DependenciesContainer()
|
||
|
container = Container()
|
||
|
|
||
|
container.assign_parent(parent)
|
||
|
|
||
|
assert container.parent is parent
|
||
|
|
||
|
|
||
|
def test_parent_name_declarative_parent():
|
||
|
container = Container()
|
||
|
assert container.parent_name == "Container"
|
||
|
|
||
|
|
||
|
def test_parent_name():
|
||
|
container = Container()
|
||
|
assert container.parent_name == "Container"
|
||
|
|
||
|
|
||
|
def test_parent_name_with_deep_parenting():
|
||
|
class Container2(containers.DeclarativeContainer):
|
||
|
name = providers.Container(Container)
|
||
|
|
||
|
class Container1(containers.DeclarativeContainer):
|
||
|
container = providers.Container(Container2)
|
||
|
|
||
|
container = Container1()
|
||
|
assert container.container().name.parent_name == "Container1.container.name"
|
||
|
|
||
|
|
||
|
def test_parent_name_is_none():
|
||
|
container = containers.DynamicContainer()
|
||
|
assert container.parent_name is None
|
||
|
|
||
|
|
||
|
def test_parent_deepcopy():
|
||
|
class ParentContainer(containers.DeclarativeContainer):
|
||
|
child = providers.Container(Container)
|
||
|
|
||
|
container = ParentContainer()
|
||
|
copied = providers.deepcopy(container)
|
||
|
|
||
|
assert container.child.parent is container
|
||
|
assert copied.child.parent is copied
|
||
|
|
||
|
assert container is not copied
|
||
|
assert container.child is not copied.child
|
||
|
assert container.child.parent is not copied.child.parent
|
||
|
|
||
|
|
||
|
def test_resolve_provider_name():
|
||
|
container = Container()
|
||
|
assert container.resolve_provider_name(container.p11) == "p11"
|
||
|
|
||
|
|
||
|
def test_resolve_provider_name_no_provider():
|
||
|
container = Container()
|
||
|
with raises(errors.Error):
|
||
|
container.resolve_provider_name(providers.Provider())
|