Skip to content

Improve support of @overload for await expressions #9837

Closed
@rmk135

Description

@rmk135

Feature

The feature is to support @overload'ing for await expressions by return type only. Here is an example:

from typing import Callable, Any, TypeVar, Generic, Awaitable, overload


T = TypeVar('T')


class Provider(Generic[T]):
    def __init__(self, provides: Callable[..., T], *args: Any, **kwargs: Any) -> None: ...
    @overload
    def __call__(self, *args: Any, **kwargs: Any) -> T: ...
    @overload
    def __call__(self, *args: Any, **kwargs: Any) -> Awaitable[T]: ...


class Animal:
    ...


class Cat(Animal):
    ...


# Test 1: to check the return type
provider = Provider(Cat)
animal: Animal = provider(1, 2, 3)


# Test 2: to check the return type with "await"
async def _get_animal_async() -> None:
    animal: Animal = await provider(1, 2, 3)

With mypy 0.790 it produces next errors:

mypy_async.py:12: error: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader
mypy_async.py:30: error: Incompatible types in "await" (actual type "Cat", expected type "Awaitable[Any]")

Pitch

I'm looking for this feature for the Dependency Injector.

The Provider class here is a generic wrapper. It returns actual instance or awaitable of this instance. The return type depends on the dependencies of the provider. If any of the underlaying providers returns awaitable it turns all the providers in the chain to the async mode:

provider1()  [async mode enabled]
│
├──> provider2()
│
├──> provider3()  [async mode enabled]
│    │
│    └──> provider4()  [returned Awaitable[T]]
│
└──> provider5()
     │
     └──> provider6()

result = await provider1()  # always returns awaitable in async mode

When async mode is enabled provider will always return awaitable.

I see this as a specific case of using @overload. It is intended to specify different return types depending on how the callable is called. While "how the callable is called" now is defined by the number of parameters, the use of callable in the await expression is just another kind of "how the callable is called".

PS: PyCharm already supports this example:

Screenshot 2020-12-25 at 12 45 53

Appreciate community feedback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions