From 10552f928499befec53d3aafdbf0cbdc0ff995d5 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Thu, 23 Oct 2025 14:52:28 +0700 Subject: [PATCH 1/9] Use TypeVar with default Any for Selector --- src/dependency_injector/providers.pyi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index 8f9b525a..960a85c0 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -4,7 +4,6 @@ from contextlib import AbstractContextManager, AbstractAsyncContextManager from pathlib import Path from typing import ( Awaitable, - TypeVar, Generic, Type, Callable as _Callable, @@ -22,6 +21,8 @@ from typing import ( overload, ) +from typing_extensions import TypeVar + try: import yaml except ImportError: @@ -38,6 +39,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") @@ -542,17 +544,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: ... @property def selector(self) -> Optional[_Callable[..., Any]]: ... - def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector: ... + def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector[T_Any]: ... @property def providers(self) -> _Dict[str, Provider]: ... - def set_providers(self, **providers: Provider) -> Selector: ... + def set_providers(self, **providers: Provider) -> Selector[TT]: ... class ProvidedInstanceFluentInterface: def __getattr__(self, item: Any) -> AttributeGetter: ... From 673ae085b346bb8f0ca6a7aea651abb5cc68907c Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Thu, 23 Oct 2025 14:52:34 +0700 Subject: [PATCH 2/9] Add tests --- tests/typing/selector.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/typing/selector.py b/tests/typing/selector.py index 5d89ec66..59da7492 100644 --- a/tests/typing/selector.py +++ b/tests/typing/selector.py @@ -40,3 +40,14 @@ async def _async4() -> None: var1: Any = await provider4() var2: Any = await provider4.async_() + + +# Test 5: to check explicit typing + +provider5 = providers.Selector[bool](lambda: "a", a=providers.Factory(bool), b=providers.Factory(int)) +var5: bool = provider5() +attr5: providers.Provider[Any] = provider5.a +provider5_after_set_selector: providers.Selector[bool] = provider5.set_selector(lambda: "a") +provider5_after_set_providers: providers.Selector[Any] = provider5.set_providers( + c=providers.Factory(str) +) # Selector[Any] for now since Provider is invariant From a97c98f07749fb99a5497a2ee0da49a9291d2555 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 09:45:38 +0700 Subject: [PATCH 3/9] Bump minimal Python version for typing_extensions to v3.13 --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fe89efaa..fc8b36c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] From 7e366bee8ce784aff6d8ef1ccd9e21b3927470a2 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 09:46:34 +0700 Subject: [PATCH 4/9] Fix accidental return type change on Selector.set_providers --- src/dependency_injector/providers.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index 960a85c0..e0a49949 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -554,7 +554,7 @@ class Selector(Provider[T_Any]): def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector[T_Any]: ... @property def providers(self) -> _Dict[str, Provider]: ... - def set_providers(self, **providers: Provider) -> Selector[TT]: ... + def set_providers(self, **providers: Provider) -> Selector: ... class ProvidedInstanceFluentInterface: def __getattr__(self, item: Any) -> AttributeGetter: ... From d74402c46df9cf753a82bbffa7a50bcd6d1374d3 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 09:55:56 +0700 Subject: [PATCH 5/9] Change Selector.set_provider to return Selector[T_Any] --- src/dependency_injector/providers.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index e0a49949..91d3e9ed 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -554,7 +554,7 @@ class Selector(Provider[T_Any]): def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector[T_Any]: ... @property def providers(self) -> _Dict[str, Provider]: ... - def set_providers(self, **providers: Provider) -> Selector: ... + def set_providers(self, **providers: Provider) -> Selector[T_Any]: ... class ProvidedInstanceFluentInterface: def __getattr__(self, item: Any) -> AttributeGetter: ... From e6c5f255198ccdc29c56d8d884ac928cca785489 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 10:09:10 +0700 Subject: [PATCH 6/9] Update tests --- tests/typing/selector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/typing/selector.py b/tests/typing/selector.py index 59da7492..c923f39a 100644 --- a/tests/typing/selector.py +++ b/tests/typing/selector.py @@ -48,6 +48,6 @@ async def _async4() -> None: var5: bool = provider5() attr5: providers.Provider[Any] = provider5.a provider5_after_set_selector: providers.Selector[bool] = provider5.set_selector(lambda: "a") -provider5_after_set_providers: providers.Selector[Any] = provider5.set_providers( +provider5_after_set_providers: providers.Selector[bool] = provider5.set_providers( c=providers.Factory(str) -) # Selector[Any] for now since Provider is invariant +) # We don't require Provider of subclass of bool yet since Provider is invariant \ No newline at end of file From 58adabaf751e18c5eec212f1d680f7540da66747 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 11:07:42 +0700 Subject: [PATCH 7/9] Specify Provider type for Selector.__getattr__ and Selector.providers --- src/dependency_injector/providers.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index 91d3e9ed..f145a57e 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -548,12 +548,12 @@ 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[T_Any]: ... @property - def providers(self) -> _Dict[str, Provider]: ... + def providers(self) -> _Dict[str, Provider[T_Any]]: ... def set_providers(self, **providers: Provider) -> Selector[T_Any]: ... class ProvidedInstanceFluentInterface: From 48a51f914cacceeb6148520d5f713fe7c7b8d0a4 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 11:07:59 +0700 Subject: [PATCH 8/9] Add more tests --- tests/typing/selector.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/typing/selector.py b/tests/typing/selector.py index c923f39a..6297baf1 100644 --- a/tests/typing/selector.py +++ b/tests/typing/selector.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Callable, Optional, Dict from dependency_injector import providers @@ -42,12 +42,34 @@ async def _async4() -> None: var2: Any = await provider4.async_() -# Test 5: to check explicit typing +# 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") -provider5 = providers.Selector[bool](lambda: "a", a=providers.Factory(bool), b=providers.Factory(int)) -var5: bool = provider5() -attr5: providers.Provider[Any] = provider5.a -provider5_after_set_selector: providers.Selector[bool] = provider5.set_selector(lambda: "a") -provider5_after_set_providers: providers.Selector[bool] = provider5.set_providers( +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 \ No newline at end of file +) # We don't require Provider of subclass of bool yet since Provider is invariant From 8e6f6dfcff51e5803f20d3bb16de482e70f38805 Mon Sep 17 00:00:00 2001 From: Leonardus Chen Date: Fri, 24 Oct 2025 18:28:28 +0700 Subject: [PATCH 9/9] Use typing_extensions.Self on set_selector and set_providers --- src/dependency_injector/providers.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index f145a57e..a4443107 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -21,7 +21,7 @@ from typing import ( overload, ) -from typing_extensions import TypeVar +from typing_extensions import Self as _Self, TypeVar try: import yaml @@ -551,10 +551,10 @@ class Selector(Provider[T_Any]): def __getattr__(self, name: str) -> Provider[T_Any]: ... @property def selector(self) -> Optional[_Callable[..., Any]]: ... - def set_selector(self, selector: Optional[_Callable[..., Any]]) -> Selector[T_Any]: ... + def set_selector(self, selector: Optional[_Callable[..., Any]]) -> _Self: ... @property def providers(self) -> _Dict[str, Provider[T_Any]]: ... - def set_providers(self, **providers: Provider) -> Selector[T_Any]: ... + def set_providers(self, **providers: Provider) -> _Self: ... class ProvidedInstanceFluentInterface: def __getattr__(self, item: Any) -> AttributeGetter: ...