Merge branch 'release/4.48.3'

This commit is contained in:
ZipFile 2025-12-04 18:31:06 +00:00
commit 964d932673
11 changed files with 313 additions and 155 deletions

View File

@ -7,6 +7,13 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_
4.48.3
------
- Allow annotated marker to be anywhere in the annotation list. Thanks to `@BrianPugh <https://github.com/BrianPugh>`_ for `#939 <https://github.com/ets-labs/python-dependency-injector/issues/939>`_.
- Fix FastDepends v3 compatibility. Thanks to `@AndrianEquestrian <https://github.com/AndrianEquestrian>`_ for `#933 <https://github.com/ets-labs/python-dependency-injector/issues/933>`_.
- Various type annotation improvements for providers. Thanks to `@leonarduschen <https://github.com/leonarduschen>`_ for `#927 <https://github.com/ets-labs/python-dependency-injector/pull/927>`_, `#932 <https://github.com/ets-labs/python-dependency-injector/pull/932>`_ and `#935 <https://github.com/ets-labs/python-dependency-injector/pull/935>`_.
4.48.2
------

View File

@ -55,7 +55,8 @@ dynamic = ["version"]
dependencies = [
# typing.Annotated since v3.9
# typing.Self and typing.assert_never since v3.11
"typing-extensions; python_version<'3.11'",
# typing.TypeVar default since v3.13
"typing-extensions; python_version<'3.13'",
]
[project.optional-dependencies]

View File

@ -1,6 +1,6 @@
"""Top-level package."""
__version__ = "4.48.2"
__version__ = "4.48.3"
"""Version number.
:type: str

View File

@ -1,26 +1,29 @@
from __future__ import annotations
from contextlib import AbstractAsyncContextManager, AbstractContextManager
from pathlib import Path
from typing import (
Awaitable,
TypeVar,
Generic,
Type,
Callable as _Callable,
Any,
Tuple,
List as _List,
Dict as _Dict,
Optional,
Union,
AsyncIterator as _AsyncIterator,
Awaitable,
Callable as _Callable,
Coroutine as _Coroutine,
Dict as _Dict,
Generator as _Generator,
Generic,
Iterable as _Iterable,
Iterator as _Iterator,
AsyncIterator as _AsyncIterator,
Generator as _Generator,
List as _List,
Mapping,
Optional,
Tuple,
Type,
Union,
overload,
)
from typing_extensions import Self as _Self, TypeVar
try:
import yaml
except ImportError:
@ -37,6 +40,7 @@ Injection = Any
ProviderParent = Union["Provider", Any]
T = TypeVar("T")
TT = TypeVar("TT")
T_Any = TypeVar("T_Any", default=Any)
P = TypeVar("P", bound="Provider")
BS = TypeVar("BS", bound="BaseSingleton")
@ -65,7 +69,7 @@ class Provider(Generic[T]):
@property
def provider(self) -> Provider[T]: ...
@property
def provided(self) -> ProvidedInstance[T]: ...
def provided(self) -> ProvidedInstance: ...
def enable_async_mode(self) -> None: ...
def disable_async_mode(self) -> None: ...
def reset_async_mode(self) -> None: ...
@ -85,7 +89,7 @@ class Object(Provider[T]):
def __init__(self, provides: Optional[T] = None) -> None: ...
@property
def provides(self) -> Optional[T]: ...
def set_provides(self, provides: Optional[T]) -> Object: ...
def set_provides(self, provides: Optional[T]) -> _Self: ...
class Self(Provider[T]):
def __init__(self, container: Optional[T] = None) -> None: ...
@ -98,12 +102,12 @@ class Delegate(Provider[Provider]):
def __init__(self, provides: Optional[Provider] = None) -> None: ...
@property
def provides(self) -> Optional[Provider]: ...
def set_provides(self, provides: Optional[Provider]) -> Delegate: ...
def set_provides(self, provides: Optional[Provider]) -> _Self: ...
class Aggregate(Provider[T]):
def __init__(
self,
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
provider_dict: Optional[Mapping[Any, Provider[T]]] = None,
**provider_kwargs: Provider[T],
): ...
def __getattr__(self, provider_name: Any) -> Provider[T]: ...
@ -122,9 +126,9 @@ class Aggregate(Provider[T]):
def providers(self) -> _Dict[Any, Provider[T]]: ...
def set_providers(
self,
provider_dict: Optional[_Dict[Any, Provider[T]]] = None,
provider_dict: Optional[Mapping[Any, Provider[T]]] = None,
**provider_kwargs: Provider[T],
) -> Aggregate[T]: ...
) -> _Self: ...
class Dependency(Provider[T]):
def __init__(
@ -135,10 +139,10 @@ class Dependency(Provider[T]):
def __getattr__(self, name: str) -> Any: ...
@property
def instance_of(self) -> Type[T]: ...
def set_instance_of(self, instance_of: Type[T]) -> Dependency[T]: ...
def set_instance_of(self, instance_of: Type[T]) -> _Self: ...
@property
def default(self) -> Provider[T]: ...
def set_default(self, default: Optional[Union[Provider, Any]]) -> Dependency[T]: ...
def set_default(self, default: Optional[Union[Provider, Any]]) -> _Self: ...
@property
def is_defined(self) -> bool: ...
def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
@ -162,28 +166,28 @@ class DependenciesContainer(Object):
def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ...
class Callable(Provider[T]):
class Callable(Provider[T_Any]):
def __init__(
self,
provides: Optional[Union[_Callable[..., T], str]] = None,
provides: Optional[Union[_Callable[..., T_Any], str]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@property
def provides(self) -> Optional[_Callable[..., T]]: ...
def provides(self) -> Optional[_Callable[..., T_Any]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> Callable[T]: ...
self, provides: Optional[Union[_Callable[..., T_Any], str]]
) -> _Self: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Callable[T]: ...
def set_args(self, *args: Injection) -> Callable[T]: ...
def clear_args(self) -> Callable[T]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
def clear_kwargs(self) -> Callable[T]: ...
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
class DelegatedCallable(Callable[T]): ...
@ -205,7 +209,7 @@ class CoroutineDelegate(Delegate):
class ConfigurationOption(Provider[Any]):
UNDEFINED: object
def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
def __enter__(self) -> ConfigurationOption: ...
def __enter__(self) -> _Self: ...
def __exit__(self, *exc_info: Any) -> None: ...
def __getattr__(self, item: str) -> ConfigurationOption: ...
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
@ -270,30 +274,26 @@ class Configuration(Object[Any]):
json_files: Optional[_Iterable[Union[Path, str]]] = None,
pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
) -> None: ...
def __enter__(self) -> Configuration: ...
def __enter__(self) -> _Self: ...
def __exit__(self, *exc_info: Any) -> None: ...
def __getattr__(self, item: str) -> ConfigurationOption: ...
def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
def get_name(self) -> str: ...
def set_name(self, name: str) -> Configuration: ...
def set_name(self, name: str) -> _Self: ...
def get_default(self) -> _Dict[Any, Any]: ...
def set_default(self, default: _Dict[Any, Any]): ...
def set_default(self, default: _Dict[Any, Any]) -> _Self: ...
def get_strict(self) -> bool: ...
def set_strict(self, strict: bool) -> Configuration: ...
def set_strict(self, strict: bool) -> _Self: ...
def get_children(self) -> _Dict[str, ConfigurationOption]: ...
def set_children(
self, children: _Dict[str, ConfigurationOption]
) -> Configuration: ...
def set_children(self, children: _Dict[str, ConfigurationOption]) -> _Self: ...
def get_ini_files(self) -> _List[Union[Path, str]]: ...
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def get_yaml_files(self) -> _List[Union[Path, str]]: ...
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def get_json_files(self) -> _List[Union[Path, str]]: ...
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> Configuration: ...
def set_json_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
def set_pydantic_settings(
self, settings: _Iterable[PydanticSettings]
) -> Configuration: ...
def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> _Self: ...
def load(self, required: bool = False, envs_required: bool = False) -> None: ...
def get(self, selector: str) -> Any: ...
def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
@ -345,22 +345,22 @@ class Factory(Provider[T]):
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> Factory[T]: ...
) -> _Self: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Factory[T]: ...
def set_args(self, *args: Injection) -> Factory[T]: ...
def clear_args(self) -> Factory[T]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
def clear_kwargs(self) -> Factory[T]: ...
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
@property
def attributes(self) -> _Dict[Any, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
def clear_attributes(self) -> Factory[T]: ...
def attributes(self) -> _Dict[str, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> _Self: ...
def set_attributes(self, **kwargs: Injection) -> _Self: ...
def clear_attributes(self) -> _Self: ...
class DelegatedFactory(Factory[T]): ...
@ -376,7 +376,7 @@ class FactoryAggregate(Aggregate[T]):
def factories(self) -> _Dict[Any, Factory[T]]: ...
def set_factories(
self,
provider_dict: Optional[_Dict[Any, Factory[T]]] = None,
provider_dict: Optional[Mapping[Any, Factory[T]]] = None,
**provider_kwargs: Factory[T],
) -> FactoryAggregate[T]: ...
@ -394,22 +394,22 @@ class BaseSingleton(Provider[T]):
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(
self, provides: Optional[Union[_Callable[..., T], str]]
) -> BaseSingleton[T]: ...
) -> _Self: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> BaseSingleton[T]: ...
def set_args(self, *args: Injection) -> BaseSingleton[T]: ...
def clear_args(self) -> BaseSingleton[T]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def set_kwargs(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def clear_kwargs(self) -> BaseSingleton[T]: ...
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
@property
def attributes(self) -> _Dict[Any, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def set_attributes(self, **kwargs: Injection) -> BaseSingleton[T]: ...
def clear_attributes(self) -> BaseSingleton[T]: ...
def attributes(self) -> _Dict[str, Injection]: ...
def add_attributes(self, **kwargs: Injection) -> _Self: ...
def set_attributes(self, **kwargs: Injection) -> _Self: ...
def clear_attributes(self) -> _Self: ...
def reset(self) -> SingletonResetContext[BS]: ...
def full_reset(self) -> SingletonFullResetContext[BS]: ...
@ -431,23 +431,23 @@ class List(Provider[_List]):
def __init__(self, *args: Injection): ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> List[T]: ...
def set_args(self, *args: Injection) -> List[T]: ...
def clear_args(self) -> List[T]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
class Dict(Provider[_Dict]):
def __init__(
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
): ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
) -> Dict: ...
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
) -> _Self: ...
def set_kwargs(
self, dict_: Optional[_Dict[Any, Injection]] = None, **kwargs: Injection
) -> Dict: ...
def clear_kwargs(self) -> Dict: ...
self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
class Resource(Provider[T]):
@overload
@ -465,6 +465,20 @@ class Resource(Provider[T]):
**kwargs: Injection,
) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., AbstractContextManager[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., AbstractAsyncContextManager[T]]] = None,
*args: Injection,
**kwargs: Injection,
) -> None: ...
@overload
def __init__(
self,
provides: Optional[_Callable[..., _Iterator[T]]] = None,
@ -494,17 +508,17 @@ class Resource(Provider[T]):
) -> None: ...
@property
def provides(self) -> Optional[_Callable[..., Any]]: ...
def set_provides(self, provides: Optional[Any]) -> Resource[T]: ...
def set_provides(self, provides: Optional[Any]) -> _Self: ...
@property
def args(self) -> Tuple[Injection]: ...
def add_args(self, *args: Injection) -> Resource[T]: ...
def set_args(self, *args: Injection) -> Resource[T]: ...
def clear_args(self) -> Resource[T]: ...
def add_args(self, *args: Injection) -> _Self: ...
def set_args(self, *args: Injection) -> _Self: ...
def clear_args(self) -> _Self: ...
@property
def kwargs(self) -> _Dict[Any, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
def set_kwargs(self, **kwargs: Injection) -> Resource[T]: ...
def clear_kwargs(self) -> Resource[T]: ...
def kwargs(self) -> _Dict[str, Injection]: ...
def add_kwargs(self, **kwargs: Injection) -> _Self: ...
def set_kwargs(self, **kwargs: Injection) -> _Self: ...
def clear_kwargs(self) -> _Self: ...
@property
def initialized(self) -> bool: ...
def init(self) -> Optional[Awaitable[T]]: ...
@ -527,17 +541,17 @@ class Container(Provider[T]):
def parent_name(self) -> Optional[str]: ...
def assign_parent(self, parent: ProviderParent) -> None: ...
class Selector(Provider[Any]):
class Selector(Provider[T_Any]):
def __init__(
self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
): ...
def __getattr__(self, name: str) -> Provider: ...
def __getattr__(self, name: str) -> Provider[T_Any]: ...
@property
def selector(self) -> Optional[_Callable[..., Any]]: ...
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ...
def set_selector(self, selector: Optional[_Callable[..., Any]]) -> _Self: ...
@property
def providers(self) -> _Dict[str, Provider]: ...
def set_providers(self, **providers: Provider) -> Selector: ...
def providers(self) -> _Dict[str, Provider[T_Any]]: ...
def set_providers(self, **providers: Provider) -> _Self: ...
class ProvidedInstanceFluentInterface:
def __getattr__(self, item: Any) -> AttributeGetter: ...
@ -545,9 +559,7 @@ class ProvidedInstanceFluentInterface:
def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
@property
def provides(self) -> Optional[Provider]: ...
def set_provides(
self, provides: Optional[Provider]
) -> ProvidedInstanceFluentInterface: ...
def set_provides(self, provides: Optional[Provider]) -> _Self: ...
class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
def __init__(self, provides: Optional[Provider] = None) -> None: ...
@ -558,7 +570,7 @@ class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
) -> None: ...
@property
def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
def set_name(self, name: Optional[str]) -> _Self: ...
class ItemGetter(Provider, ProvidedInstanceFluentInterface):
def __init__(
@ -566,7 +578,7 @@ class ItemGetter(Provider, ProvidedInstanceFluentInterface):
) -> None: ...
@property
def name(self) -> Optional[str]: ...
def set_name(self, name: Optional[str]) -> ProvidedInstanceFluentInterface: ...
def set_name(self, name: Optional[str]) -> _Self: ...
class MethodCaller(Provider, ProvidedInstanceFluentInterface):
def __init__(

View File

@ -76,8 +76,18 @@ with suppress(ImportError):
MARKER_EXTRACTORS.append(extract_marker_from_fastapi)
with suppress(ImportError):
from fast_depends.dependencies import Depends as FastDepends
with suppress(ImportError): # fast_depends >=3.0.0
from fast_depends.dependencies.model import Dependant as FastDependant # type: ignore[attr-defined]
def extract_marker_from_dependant_fast_depends(param: Any) -> Any:
if isinstance(param, FastDependant):
return param.dependency
return None
MARKER_EXTRACTORS.append(extract_marker_from_dependant_fast_depends)
with suppress(ImportError): # fast_depends <3.0.0
from fast_depends.dependencies import Depends as FastDepends # type: ignore[attr-defined]
def extract_marker_from_fast_depends(param: Any) -> Any:
if isinstance(param, FastDepends):
@ -672,23 +682,18 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
if get_origin(parameter.annotation) is Annotated:
args = get_args(parameter.annotation)
if len(args) > 1:
marker = args[1]
else:
marker = None
candidates = get_args(parameter.annotation)[1:]
else:
marker = parameter.default
candidates = (parameter.default,)
for marker_extractor in MARKER_EXTRACTORS:
if _marker := marker_extractor(marker):
marker = _marker
break
if not isinstance(marker, _Marker):
return None
return marker
for marker in candidates:
for marker_extractor in MARKER_EXTRACTORS:
if _marker := marker_extractor(marker):
marker = _marker
break
if _is_marker(marker):
return marker
return None
@cache
@ -1213,9 +1218,11 @@ def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
for annotation_name, annotation in annotations.items():
if get_origin(annotation) is Annotated:
args = get_args(annotation)
if len(args) > 1:
member = args[1]
members.append((annotation_name, member))
# Search through all metadata items (args[1:]) for a DI marker
for arg in args[1:]:
if _is_marker(arg):
members.append((annotation_name, arg))
break
return members

View File

@ -1,4 +1,5 @@
from dependency_injector import providers
from typing_extensions import assert_type, Any
class Animal: ...
@ -8,29 +9,35 @@ class Cat(Animal): ...
# Test 1: to check Aggregate provider
provider1: providers.Aggregate[str] = providers.Aggregate(
provider1 = providers.Aggregate(
a=providers.Object("str1"),
b=providers.Object("str2"),
)
provider_a_1: providers.Provider[str] = provider1.a
provider_a_1 = provider1.a
provider_b_1: providers.Provider[str] = provider1.b
val1: str = provider1("a")
val1 = provider1("a")
assert_type(provider1, providers.Aggregate[str])
assert_type(provider_a_1, providers.Provider[str])
assert_type(provider_b_1, providers.Provider[str])
assert_type(val1, str)
provider1_set_non_string_keys: providers.Aggregate[str] = providers.Aggregate()
provider1_set_non_string_keys = providers.Aggregate[str]()
provider1_set_non_string_keys.set_providers({Cat: providers.Object("str")})
provider_set_non_string_1: providers.Provider[str] = (
provider1_set_non_string_keys.providers[Cat]
)
provider_set_non_string_1 = provider1_set_non_string_keys.providers[Cat]
assert_type(provider_set_non_string_1, providers.Provider[str])
provider1_new_non_string_keys: providers.Aggregate[str] = providers.Aggregate(
provider1_new_non_string_keys = providers.Aggregate(
{Cat: providers.Object("str")},
)
factory_new_non_string_1: providers.Provider[str] = (
provider1_new_non_string_keys.providers[Cat]
)
factory_new_non_string_1 = provider1_new_non_string_keys.providers[Cat]
assert_type(provider1_new_non_string_keys, providers.Aggregate[str])
assert_type(factory_new_non_string_1, providers.Provider[str])
provider1_no_explicit_typing = providers.Aggregate(a=providers.Object("str"))
provider1_no_explicit_typing_factory: providers.Provider[str] = (
provider1_no_explicit_typing.providers["a"]
)
provider1_no_explicit_typing_object: str = provider1_no_explicit_typing("a")
provider1_no_explicit_typing_factory = provider1_no_explicit_typing.providers["a"]
provider1_no_explicit_typing_object = provider1_no_explicit_typing("a")
assert_type(provider1_no_explicit_typing_factory, providers.Provider[str])
assert_type(provider1_no_explicit_typing_object, str)

View File

@ -1,4 +1,5 @@
from typing import Any, Callable, Dict, Optional, Tuple, Type
from typing import Any, Callable, Dict, Optional, Tuple
from typing_extensions import assert_type
from dependency_injector import providers
@ -7,7 +8,6 @@ class Animal: ...
class Cat(Animal):
@classmethod
def create(cls) -> Animal:
return cls()
@ -15,11 +15,13 @@ class Cat(Animal):
# Test 1: to check the return type (class)
provider1 = providers.Callable(Cat)
animal1: Animal = provider1(1, 2, 3, b="1", c=2, e=0.0)
cat1 = provider1(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat1, Cat)
# Test 2: to check the return type (class factory method)
provider2 = providers.Callable(Cat.create)
animal2: Animal = provider2()
animal2 = provider2()
assert_type(animal2, Animal)
# Test 3: to check the .override() method
provider3 = providers.Callable(Animal)
@ -28,24 +30,32 @@ with provider3.override(providers.Callable(Cat)):
# Test 4: to check the .args & .kwargs attributes
provider4 = providers.Callable(Animal)
args4: Tuple[Any] = provider4.args
kwargs4: Dict[str, Any] = provider4.kwargs
args4 = provider4.args
kwargs4 = provider4.kwargs
assert_type(args4, Tuple[Any])
assert_type(kwargs4, Dict[str, Any])
# Test 5: to check the provided instance interface
provider5 = providers.Callable(Animal)
provided5: Animal = provider5.provided()
attr_getter5: providers.AttributeGetter = provider5.provided.attr
item_getter5: providers.ItemGetter = provider5.provided["item"]
method_caller: providers.MethodCaller = provider5.provided.method.call(123, arg=324)
provided_val5 = provider5.provided()
attr_getter5 = provider5.provided.attr
item_getter5 = provider5.provided["item"]
method_caller5 = provider5.provided.method.call(123, arg=324)
assert_type(provided_val5, Any)
assert_type(attr_getter5, providers.AttributeGetter)
assert_type(item_getter5, providers.ItemGetter)
assert_type(method_caller5, providers.MethodCaller)
# Test 6: to check the DelegatedCallable
provider6 = providers.DelegatedCallable(Cat)
animal6: Animal = provider6(1, 2, 3, b="1", c=2, e=0.0)
cat6 = provider6(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat6, Cat)
# Test 7: to check the AbstractCallable
provider7 = providers.AbstractCallable(Animal)
provider7.override(providers.Callable(Cat))
animal7: Animal = provider7(1, 2, 3, b="1", c=2, e=0.0)
animal7 = provider7(1, 2, 3, b="1", c=2, e=0.0)
assert_type(animal7, Animal)
# Test 8: to check the CallableDelegate __init__
provider8 = providers.CallableDelegate(providers.Callable(lambda: None))
@ -55,20 +65,22 @@ provider9 = providers.Callable(Cat)
async def _async9() -> None:
animal1: Animal = await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore
animal2: Animal = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0)
await provider9(1, 2, 3, b="1", c=2, e=0.0) # type: ignore[misc]
cat9 = await provider9.async_(1, 2, 3, b="1", c=2, e=0.0)
assert_type(cat9, Cat)
# Test 10: to check the .provides
provider10 = providers.Callable(Cat)
provides10: Optional[Callable[..., Cat]] = provider10.provides
assert provides10 is Cat
provides10 = provider10.provides
assert_type(provides10, Optional[Callable[..., Cat]])
# Test 11: to check the .provides for explicit typevar
provider11 = providers.Callable[Animal](Cat)
provides11: Optional[Callable[..., Animal]] = provider11.provides
assert provides11 is Cat
provides11 = provider11.provides
assert_type(provides11, Optional[Callable[..., Animal]])
# Test 12: to check string imports
provider12: providers.Callable[Dict[Any, Any]] = providers.Callable("builtins.dict")
provider12 = providers.Callable("builtins.dict")
provider12.set_provides("builtins.dict")

View File

@ -1,3 +1,4 @@
from contextlib import contextmanager, asynccontextmanager
from typing import (
Any,
AsyncGenerator,
@ -7,6 +8,7 @@ from typing import (
Iterator,
List,
Optional,
Self,
)
from dependency_injector import providers, resources
@ -109,3 +111,62 @@ async def _provide8() -> None:
# Test 9: to check string imports
provider9: providers.Resource[Dict[Any, Any]] = providers.Resource("builtins.dict")
provider9.set_provides("builtins.dict")
# Test 10: to check the return type with classes implementing AbstractContextManager protocol
class MyResource10:
def __init__(self) -> None:
pass
def __enter__(self) -> Self:
return self
def __exit__(self, *args: Any, **kwargs: Any) -> None:
return None
provider10 = providers.Resource(MyResource10)
var10: MyResource10 = provider10()
# Test 11: to check the return type with functions decorated with contextlib.contextmanager
@contextmanager
def init11() -> Iterator[int]:
yield 1
provider11 = providers.Resource(init11)
var11: int = provider11()
# Test 12: to check the return type with classes implementing AbstractAsyncContextManager protocol
class MyResource12:
def __init__(self) -> None:
pass
async def __aenter__(self) -> Self:
return self
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
return None
provider12 = providers.Resource(MyResource12)
async def _provide12() -> None:
var1: MyResource12 = await provider12() # type: ignore
var2: MyResource12 = await provider12.async_()
# Test 13: to check the return type with functions decorated with contextlib.asynccontextmanager
@asynccontextmanager
async def init13() -> AsyncIterator[int]:
yield 1
provider13 = providers.Resource(init13)
async def _provide13() -> None:
var1: int = await provider13() # type: ignore
var2: int = await provider13.async_()

View File

@ -1,4 +1,4 @@
from typing import Any
from typing import Any, Callable, Optional, Dict
from dependency_injector import providers
@ -40,3 +40,36 @@ provider4 = providers.Selector(
async def _async4() -> None:
var1: Any = await provider4()
var2: Any = await provider4.async_()
# Test 5: to check selector getter and setter
provider5 = providers.Selector(
lambda: "a",
a=providers.Factory(object),
b=providers.Factory(object),
)
selector5: Optional[Callable[..., Any]] = provider5.selector
provider5_after_set_selector: providers.Selector[Any] = provider5.set_selector(lambda: "a")
# Test 6: to check providers getter and setter
provider6 = providers.Selector(
lambda: "a",
a=providers.Factory(object),
b=providers.Factory(object),
)
providers6: Dict[str, providers.Provider[Any]] = provider6.providers
provider6_after_set_providers: providers.Selector[Any] = provider6.set_providers(c=providers.Factory(object))
# Test 7: to check explicit typing: return type, getattr, getter/setter of providers and selectors
provider7 = providers.Selector[bool](lambda: "a", a=providers.Factory(bool), b=providers.Factory(int))
var7: bool = provider7()
attr7: providers.Provider[bool] = provider7.a
selector7: Optional[Callable[..., Any]] = provider7.selector
provider7_after_set_selector: providers.Selector[bool] = provider7.set_selector(lambda: "a")
providers7: Dict[str, providers.Provider[bool]] = provider7.providers
provider7_after_set_providers: providers.Selector[bool] = provider7.set_providers(
c=providers.Factory(str)
) # We don't require Provider of subclass of bool yet since Provider is invariant

View File

@ -124,3 +124,10 @@ def test_class_decorator(service: Annotated[Service, Provide[Container.service]]
def test_container(container: Annotated[Container, Provide[Container]]):
return container.service()
@inject
def test_annotated_with_non_di_metadata_first(
service: Annotated[Service, "some other annotated value", Provide[Container.service]],
):
return service

View File

@ -174,3 +174,14 @@ def test_class_decorator():
def test_container():
service = module.test_container()
assert isinstance(service, Service)
def test_annotated_with_non_di_metadata_first():
"""Test that Annotated works when DI marker is not the first metadata item.
This tests the case where Annotated has other metadata (like docstrings or
other annotations) before the Provide marker, e.g.:
Annotated[Service, "some doc", Provide[Container.service]]
"""
service = module.test_annotated_with_non_di_metadata_first()
assert isinstance(service, Service)