Skip to content
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

"Awaitable[Any]" is incompatible with "Coroutine[Any, Any, Any]" #184

Closed
iwoloschin opened this issue Jul 29, 2020 · 6 comments
Closed

"Awaitable[Any]" is incompatible with "Coroutine[Any, Any, Any]" #184

iwoloschin opened this issue Jul 29, 2020 · 6 comments
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@iwoloschin
Copy link

Environment data

  • Language Server version: 2020.7.3 (pyright 5004efe5)
  • OS and version: macOS 10.15.6
  • Python version (& distribution if applicable, e.g. Anaconda): 3.8.5 via Homebrew

Expected behaviour

Awaitable[Any] should be compatible with Coroutine[Any, Any, Any]. Or, potentially, instead of being an Awaitable[Any] an un-awaited coroutine should be Coroutine[Any, Any, Any]? For comparison, I do not believe mypy views the below code snippet as an error, though it is possible I'm using mypy incorrectly.

Details from mypy are available here: https://mypy.readthedocs.io/en/stable/more_types.html#async-and-await

Actual behaviour

Pylance throws an error:

Argument of type "Awaitable[str]" cannot be assigned to parameter "coro" of type "Coroutine[Any, Any, Any]" in function "inspector"
  "Awaitable[str]" is incompatible with "Coroutine[Any, Any, Any]"Pylance (reportGeneralTypeIssues)

Logs

I don't think logs are necessary for this issue, can provide if requested.

Code Snippet / Additional information

Here's a silly example that just does some basic introspection of a passed coroutine. It runs as-is if copied into ipython, but pylance flags with the error from above on the last line, inner(1, "test"). I don't have much experience with mypy but I do not believe it is flagging the same line as an error.

import asyncio
from typing import Any, Coroutine

async def inspector(coro: Coroutine[Any, Any, Any]):
    print(coro.cr_frame.f_locals)
    return await coro

async def inner(sleep: int, message: str) -> str:
    await asyncio.sleep(sleep)
    print(message)
    return message

async def outer():
    await inspector(inner(1, "test"))
@erictraut
Copy link
Contributor

Thanks for the bug report. Currently, Pylance infers the type Awaitable[T] for an async function's return type. Mypy appears to infer the type Coroutine[Any, Any, T]. Both are arguably correct because Coroutine derives from Awaitable, but I think it's reasonable to change the behavior to match mypy in this case. This change will be in the next version of Pylance.

@erictraut erictraut added bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version labels Jul 29, 2020
@jakebailey
Copy link
Member

This didn't quite make it into 2020.7.4, but will be in the next release.

@jakebailey
Copy link
Member

This issue has been fixed in version 2020.8.0, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/master/CHANGELOG.md#202080-5-august-2020

@NixBiks
Copy link

NixBiks commented Nov 7, 2022

Shouldn't this PR make the following valid?

import typing as t
import asyncio
import functools
from concurrent.futures import Executor
from typing_extensions import ParamSpec


T = t.TypeVar("T")
P = ParamSpec("P")


def to_async(executor: t.Optional[Executor] = None):
    def decorator(f: t.Callable[P, T]) -> t.Callable[P, t.Awaitable[T]]:
        @functools.wraps(f)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> t.Awaitable[T]:
            loop = asyncio.get_event_loop()
            return loop.run_in_executor(executor, functools.partial(f, *args, **kwargs))

        return wrapper

    return decorator


@to_async()
def sync(name: str) -> int:
    return 2


async def main():
    return await sync("test")


asyncio.run(main())
asyncio.run(sync("test"))  # <-- "Awaitable[int]" is incompatible with "Coroutine[Any, Any, _T@run]"

@erictraut
Copy link
Contributor

@nixfixbix, no, your code sample should result in a type violation error. Coroutine derives from (i.e. is a subclass of) Awaitable. That means you can pass a Coroutine to a function that expects an Awaitable, but not vice versa. The asyncio.run method specifically requires a Couroutine, and you're attempting to pass it an Awaitable.

The title of this issue is misleading because it doesn't describe the underlying cause of the problem the OP was seeing. The fix was to infer a return type of Coroutine rather than Awaitable for an async function.

@NixBiks
Copy link

NixBiks commented Nov 7, 2022

@nixfixbix, no, your code sample should result in a type violation error. Coroutine derives from (i.e. is a subclass of) Awaitable. That means you can pass a Coroutine to a function that expects an Awaitable, but not vice versa. The asyncio.run method specifically requires a Couroutine, and you're attempting to pass it an Awaitable.

The title of this issue is misleading because it doesn't describe the underlying cause of the problem the OP was seeing. The fix was to infer a return type of Coroutine rather than Awaitable for an async function.

Thanks @erictraut - I don't understand how you always are able to reply so quickly and great answers too. Great job! 👏🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version
Projects
None yet
Development

No branches or pull requests

4 participants