diff --git a/tests/unit/samples/wiring/module_annotated.py b/tests/unit/samples/wiring/module_annotated.py new file mode 100644 index 00000000..624ac41d --- /dev/null +++ b/tests/unit/samples/wiring/module_annotated.py @@ -0,0 +1,120 @@ +"""Test module for wiring with Annotated.""" + +from decimal import Decimal +from typing import Callable, Annotated + +from dependency_injector import providers +from dependency_injector.wiring import inject, Provide, Provider + +from .container import Container, SubContainer +from .service import Service + +service: Annotated[Service, Provide[Container.service]] +service_provider: Annotated[Callable[..., Service], Provider[Container.service]] +undefined: Annotated[Callable, Provide[providers.Provider()]] + +class TestClass: + service: Annotated[Service, Provide[Container.service]] + service_provider: Annotated[Callable[..., Service], Provider[Container.service]] + undefined: Annotated[Callable, Provide[providers.Provider()]] + + @inject + def __init__(self, service: Annotated[Service, Provide[Container.service]]): + self.service = service + + @inject + def method(self, service: Annotated[Service, Provide[Container.service]]): + return service + + @classmethod + @inject + def class_method(cls, service: Annotated[Service, Provide[Container.service]]): + return service + + @staticmethod + @inject + def static_method(service: Annotated[Service, Provide[Container.service]]): + return service + +@inject +def test_function(service: Annotated[Service, Provide[Container.service]]): + return service + +@inject +def test_function_provider(service_provider: Annotated[Callable[..., Service], Provider[Container.service]]): + service = service_provider() + return service + +@inject +def test_config_value( + value_int: Annotated[int, Provide[Container.config.a.b.c.as_int()]], + value_float: Annotated[float, Provide[Container.config.a.b.c.as_float()]], + value_str: Annotated[str, Provide[Container.config.a.b.c.as_(str)]], + value_decimal: Annotated[Decimal, Provide[Container.config.a.b.c.as_(Decimal)]], + value_required: Annotated[str, Provide[Container.config.a.b.c.required()]], + value_required_int: Annotated[int, Provide[Container.config.a.b.c.required().as_int()]], + value_required_float: Annotated[float, Provide[Container.config.a.b.c.required().as_float()]], + value_required_str: Annotated[str, Provide[Container.config.a.b.c.required().as_(str)]], + value_required_decimal: Annotated[str, Provide[Container.config.a.b.c.required().as_(Decimal)]], +): + return ( + value_int, + value_float, + value_str, + value_decimal, + value_required, + value_required_int, + value_required_float, + value_required_str, + value_required_decimal, + ) + +@inject +def test_config_value_required_undefined( + value_required: Annotated[int, Provide[Container.config.a.b.c.required()]], +): + return value_required + +@inject +def test_provide_provider(service_provider: Annotated[Callable[..., Service], Provide[Container.service.provider]]): + service = service_provider() + return service + +@inject +def test_provider_provider(service_provider: Annotated[Callable[..., Service], Provider[Container.service.provider]]): + service = service_provider() + return service + +@inject +def test_provided_instance(some_value: Annotated[int, Provide[Container.service.provided.foo["bar"].call()]]): + return some_value + +@inject +def test_subcontainer_provider(some_value: Annotated[int, Provide[Container.sub.int_object]]): + return some_value + +@inject +def test_config_invariant(some_value: Annotated[int, Provide[Container.config.option[Container.config.switch]]]): + return some_value + +@inject +def test_provide_from_different_containers( + service: Annotated[Service, Provide[Container.service]], + some_value: Annotated[int, Provide[SubContainer.int_object]], +): + return service, some_value + +class ClassDecorator: + def __init__(self, fn): + self._fn = fn + + def __call__(self, *args, **kwargs): + return self._fn(*args, **kwargs) + +@ClassDecorator +@inject +def test_class_decorator(service: Annotated[Service, Provide[Container.service]]): + return service + +def test_container(container: Annotated[Container, Provide[Container]]): + return container.service() \ No newline at end of file diff --git a/tests/unit/wiring/provider_ids/test_main_annotated_py36.py b/tests/unit/wiring/provider_ids/test_main_annotated_py36.py new file mode 100644 index 00000000..517f3283 --- /dev/null +++ b/tests/unit/wiring/provider_ids/test_main_annotated_py36.py @@ -0,0 +1,176 @@ +"""Main wiring tests for Annotated attribute and argument injection.""" + +from decimal import Decimal +import typing + +from dependency_injector import errors +from dependency_injector.wiring import Closing, Provide, Provider, wire +from pytest import fixture, mark, raises + +from samples.wiring import module_annotated as module, package, resourceclosing +from samples.wiring.service import Service +from samples.wiring.container import Container, SubContainer + +@fixture(autouse=True) +def container(): + container = Container(config={"a": {"b": {"c": 10}}}) + container.wire( + modules=[module], + packages=[package], + ) + yield container + container.unwire() + +@fixture +def subcontainer(): + container = SubContainer() + container.wire( + modules=[module], + packages=[package], + ) + yield container + container.unwire() + +@fixture +def resourceclosing_container(): + container = resourceclosing.Container() + container.wire(modules=[resourceclosing]) + yield container + container.unwire() + +def test_module_attributes_wiring(): + assert isinstance(module.service, Service) + assert isinstance(module.service_provider(), Service) + assert isinstance(module.__annotations__['undefined'], typing._AnnotatedAlias) + +def test_class_wiring(): + test_class_object = module.TestClass() + assert isinstance(test_class_object.service, Service) + +def test_class_wiring_context_arg(container: Container): + test_service = container.service() + test_class_object = module.TestClass(service=test_service) + assert test_class_object.service is test_service + +def test_class_method_wiring(): + test_class_object = module.TestClass() + service = test_class_object.method() + assert isinstance(service, Service) + +def test_class_classmethod_wiring(): + service = module.TestClass.class_method() + assert isinstance(service, Service) + +def test_instance_classmethod_wiring(): + instance = module.TestClass() + service = instance.class_method() + assert isinstance(service, Service) + +def test_class_staticmethod_wiring(): + service = module.TestClass.static_method() + assert isinstance(service, Service) + +def test_instance_staticmethod_wiring(): + instance = module.TestClass() + service = instance.static_method() + assert isinstance(service, Service) + +def test_class_attribute_wiring(): + assert isinstance(module.TestClass.service, Service) + assert isinstance(module.TestClass.service_provider(), Service) + assert isinstance(module.TestClass.__annotations__['undefined'], typing._AnnotatedAlias) + +def test_function_wiring(): + service = module.test_function() + assert isinstance(service, Service) + +def test_function_wiring_context_arg(container: Container): + test_service = container.service() + service = module.test_function(service=test_service) + assert service is test_service + +def test_function_wiring_provider(): + service = module.test_function_provider() + assert isinstance(service, Service) + +def test_function_wiring_provider_context_arg(container: Container): + test_service = container.service() + service = module.test_function_provider(service_provider=lambda: test_service) + assert service is test_service + +def test_configuration_option(): + ( + value_int, + value_float, + value_str, + value_decimal, + value_required, + value_required_int, + value_required_float, + value_required_str, + value_required_decimal, + ) = module.test_config_value() + + assert value_int == 10 + assert value_float == 10.0 + assert value_str == "10" + assert value_decimal == Decimal(10) + assert value_required == 10 + assert value_required_int == 10 + assert value_required_float == 10.0 + assert value_required_str == "10" + assert value_required_decimal == Decimal(10) + +def test_configuration_option_required_undefined(container: Container): + container.config.reset_override() + with raises(errors.Error, match="Undefined configuration option \"config.a.b.c\""): + module.test_config_value_required_undefined() + +def test_provide_provider(): + service = module.test_provide_provider() + assert isinstance(service, Service) + +def test_provider_provider(): + service = module.test_provider_provider() + assert isinstance(service, Service) + +def test_provided_instance(container: Container): + class TestService: + foo = {"bar": lambda: 10} + + with container.service.override(TestService()): + some_value = module.test_provided_instance() + assert some_value == 10 + +def test_subcontainer(): + some_value = module.test_subcontainer_provider() + assert some_value == 1 + +def test_config_invariant(container: Container): + config = { + "option": { + "a": 1, + "b": 2, + }, + "switch": "a", + } + container.config.from_dict(config) + + value_default = module.test_config_invariant() + assert value_default == 1 + + with container.config.switch.override("a"): + value_a = module.test_config_invariant() + assert value_a == 1 + + with container.config.switch.override("b"): + value_b = module.test_config_invariant() + assert value_b == 2 + +def test_class_decorator(): + service = module.test_class_decorator() + assert isinstance(service, Service) + +def test_container(): + service = module.test_container() + assert isinstance(service, Service) \ No newline at end of file