-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Coroutine functions should return Coroutine, not Awaitable #3569
Comments
Would you like to see if you can submit a PR to fix this? |
This would be simple to fix, by just changing the return type in defn.type = defn.type.copy_modified(
ret_type = self.named_type_or_none('typing.Awaitable',
[defn.type.ret_type])) To something like |
Thanks @ilevkivskyi! I think I can do the PR, with the test & doc changes. One thing I am not sure about is whether the return type of defs annotated with |
@jpochyla @types.coroutine
def test():
yield 1 You can't do |
Hi, just seeing if any progress was being made on this (as I stumbled on it today). |
@scottbelden |
I started trying to make an attempt to resolve this, but I'm afraid that without some help I don't think I'll be able to do it and might just have to wait for someone else to get to it. I started with making the changes in assert ret_type is not None, "Internal error: typing.Coroutine not found"
AssertionError: Internal error: typing.Coroutine not found I'm not sure why |
You may have to add it to one of the stub test files (typing.pyi somewhere in the test-data/ directory). |
I also hit this issue yesteday. Here is one case where having from asyncio import (
AbstractEventLoop,
get_event_loop,
sleep,
Task,
)
from typing import Coroutine, Optional
# NOTE: `loop.create_task()` only accepts coroutine objects. It cannot accept
# futures, nor tasks so we should not use `typing.Awaitable`.
def spawn(coro: Coroutine,
loop: Optional[AbstractEventLoop]=None) -> Task:
loop = loop or get_event_loop()
return loop.create_task(coro)
# Any regular coroutine :-)
#
# I would expect `mypy` to deduce `Coroutine[Any, Any, Any]`, but it detects
# `Awaitable[None]`.
async def background_work() -> None:
await sleep(5.0)
print('DONE!')
# Demonstrate the type conflict :-)
async def main() -> None:
task = spawn(background_work()) # error: Argument 1 to "spawn" has incompatible type "Awaitable[None]"; expected "Coroutine[Any, Any, Any]"
print('waiting...')
await task
if __name__ == '__main__':
get_event_loop().run_until_complete(main()) |
As a reminder to myself:
|
* Change the return type of coroutine functions to coroutine object Previously, the return type was `Awaitable`, which is correct, but not specific enough for some use cases. For example, if you have a function parameter that should be a coroutine object (but not a `Future`, `Task` or other awaitable), then `mypy` is unable to detect incorrect invocations of the function. This change is (deliberately) imcomplete as is. It seems like this breaks quite a few tests in `mypy`. The first symptom is that coroutine objects are now (incorrectly) detected as generators.. * Prevent coroutine functions from being classified as generators This change removes the undesired "return type of a generator function should be `Generator` or one of its subtypes for coroutine function definitions. However, it introduces a new error where the return type of coroutine functions is expected to be `Coroutine[T, Any, Any]` instead of the desired `T`. It looks like we were hijacking a generator-specific path for checking the return type of coroutine functinos. I added an explicit path for coroutine functions, allowing our test to pass. However, lots of tests now fail. A few of them were simply places that were incidentally relying on coroutine functions to have type `Awaitable`. I fix them. The remaining failures all seem to be about coroutine functions with return type `None` without an explicit return statement. Seems like this is also something for which we were relying on implicit classification as generators. * Allow implicit return for coroutine functions that return `None` Most of the tests are fixed, but two tests still fail. One about not detecting invalid `yield from` on `AwaitableGenerator`. The other about types being erased in call to `asyncio.gather()`. * Fix return type for coroutine functions decorated with @coroutine * Fix detection of await expression on direct coroutine function call Changing the return type of coroutine functions to `Coroutine` introduced a regression in the expression checks. * Fix regression after change of coroutine function return type * Fix position of return type in `Coroutine` This fixes the type inference logic that was causing the last failing test to fail. Build should now be green :-) * Fix issues raised in code review Fixes #3569. Fixes #4460. Special thanks to @ilevkivskyi and @gvanrossum for their time and their infinite patience with all my questions :-)
Summary: Came across this because we were wrapping functions returning `AsyncGenerator` inside an `Awaitable`, which was throwing false positive errors later on when we tried to call `__aiter__` on it, for instance. Turns out functions prefixed with `async` wrap the return type in a `Coroutine`, not an `Awaitable` (see: python/mypy#3569), and functions that are actually generators (contain a yield) just take the return annotation of `AsyncGenerator` at face value - otherwise, the function signature is understood as asynchronously returning a generator object just like any other async function (see: python/mypy#5070) Reviewed By: dkgi Differential Revision: D13864544 fbshipit-source-id: 0d201735252b77688a5491428cfb5818d000754b
c
above has all fields oftyping.Coroutine
, you cansend
andthrow
on it, but the type doesn't reflect that.The text was updated successfully, but these errors were encountered: