Skip to content

Fix type annotations for .provides #491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/dependency_injector/providers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class Provider(Generic[T]):

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: ...

Expand Down Expand Up @@ -144,7 +145,7 @@ class DependenciesContainer(Object):
class Callable(Provider[T]):
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@property
def provides(self) -> Optional[T]: ...
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Callable[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
Expand Down Expand Up @@ -249,9 +250,9 @@ class Factory(Provider[T]):
provided_type: Optional[Type]
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@property
def cls(self) -> T: ...
def cls(self) -> Type[T]: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the same time, Factory provider still has a strong use case when it is used with a class. When you need to access factory class, attribute .cls is better from the readability and understandability perspectives. Even despite .cls and .provides are now aliases and have the same implementation, I think we should treat them differently. With that said, I think that's ok if we type .cls -> Type[T], and .provides -> _Callable[..., T].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okidoki 👍

once we resolve the conversation at https://github.com/ets-labs/python-dependency-injector/pull/491/files#r692507266, I'll do the changes 🙏

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done 47cf4c2

@property
def provides(self) -> T: ...
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[_Callable[..., T]]) -> Factory[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
Expand Down Expand Up @@ -300,9 +301,9 @@ class BaseSingleton(Provider[T]):
provided_type = Optional[Type]
def __init__(self, provides: Optional[_Callable[..., T]] = None, *args: Injection, **kwargs: Injection) -> None: ...
@property
def cls(self) -> T: ...
def cls(self) -> Type[T]: ...
@property
def provides(self) -> T: ...
def provides(self) -> Optional[_Callable[..., T]]: ...
def set_provides(self, provides: Optional[_Callable[..., T]]) -> BaseSingleton[T]: ...
@property
def args(self) -> Tuple[Injection]: ...
Expand Down
12 changes: 11 additions & 1 deletion tests/typing/callable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple, Any, Dict
from typing import Callable, Optional, Tuple, Any, Dict, Type

from dependency_injector import providers

Expand Down Expand Up @@ -56,3 +56,13 @@ def create(cls) -> Animal:
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)

# Test 10: to check the .provides
provider10 = providers.Callable(Cat)
provides10: Optional[Callable[..., Cat]] = provider10.provides
assert provides10 is 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
7 changes: 6 additions & 1 deletion tests/typing/delegate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dependency_injector import providers
from typing import Optional

from dependency_injector import providers

# Test 1: to check the return type
provider1 = providers.Delegate(providers.Provider())
Expand All @@ -10,3 +11,7 @@
async def _async2() -> None:
var1: providers.Provider = await provider2() # type: ignore
var2: providers.Provider = await provider2.async_()

# Test 3: to check class type from provider
provider3 = providers.Delegate(providers.Provider())
provided_provides: Optional[providers.Provider] = provider3.provides
16 changes: 15 additions & 1 deletion tests/typing/factory.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple, Any, Dict
from typing import Callable, Optional, Tuple, Any, Dict, Type

from dependency_injector import providers

Expand Down Expand Up @@ -72,3 +72,17 @@ def create(cls) -> Animal:
async def _async11() -> None:
animal1: Animal = await provider11(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
animal2: Animal = await provider11.async_(1, 2, 3, b='1', c=2, e=0.0)

# Test 12: to check class type from .provides
provider12 = providers.Factory(Cat)
provided_cls12: Type[Animal] = provider12.cls
assert issubclass(provided_cls12, Animal)
provided_provides12: Optional[Callable[..., Animal]] = provider12.provides
assert provided_provides12 is not None and provided_provides12() == Cat()

# Test 13: to check class from .provides with explicit typevar
provider13 = providers.Factory[Animal](Cat)
provided_cls13: Type[Animal] = provider13.cls
assert issubclass(provided_cls13, Animal)
provided_provides13: Optional[Callable[..., Animal]] = provider13.provides
assert provided_provides13 is not None and provided_provides13() == Cat()
6 changes: 6 additions & 0 deletions tests/typing/object.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Type, Optional

from dependency_injector import providers


Expand All @@ -17,3 +19,7 @@
async def _async3() -> None:
var1: int = await provider3() # type: ignore
var2: int = await provider3.async_()

# Test 4: to check class type from provider
provider4 = providers.Object(int('1'))
provided_provides: Optional[int] = provider4.provides
16 changes: 15 additions & 1 deletion tests/typing/singleton.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple, Any, Dict
from typing import Callable, Optional, Tuple, Any, Dict, Type

from dependency_injector import providers

Expand Down Expand Up @@ -75,3 +75,17 @@ def create(cls) -> Animal:
async def _async13() -> None:
animal1: Animal = await provider13(1, 2, 3, b='1', c=2, e=0.0) # type: ignore
animal2: Animal = await provider13.async_(1, 2, 3, b='1', c=2, e=0.0)

# Test 14: to check class from .provides
provider14 = providers.Singleton(Cat)
provided_cls14: Type[Cat] = provider14.cls
assert issubclass(provided_cls14, Cat)
provided_provides14: Optional[Callable[..., Cat]] = provider14.provides
assert provided_provides14 is not None and provided_provides14() == Cat()

# Test 15: to check class from .provides with explicit typevar
provider15 = providers.Singleton[Animal](Cat)
provided_cls15: Type[Animal] = provider15.cls
assert issubclass(provided_cls15, Animal)
provided_provides15: Optional[Callable[..., Animal]] = provider15.provides
assert provided_provides15 is not None and provided_provides15() == Cat()