Skip to content

Document how to type hint for an async function used as a callback #424

Closed
python/cpython
#20386

Description

@brettcannon

I have (roughly) the following code:

async def foo(x: int): pass
callback = foo  # What does this type to?

The problem is I don't know what to type callback to. It isn't a Callable as mypy says that "Function does not return a value" when I call await callback(42). It isn't an AsyncIterable, AsyncIterator, or an AsyncGenerator as it simply isn't. It isn't a Coroutine as mypy says that they can't be called.

So am I missing something on how to type this, or does there need to be an AsyncCallable added to typing?

Activity

  1. JelleZijlstra commented on May 7, 2017

    @JelleZijlstra
    Member

    I think you can use Callable[[int], Awaitable[None]].

  2. ilevkivskyi commented on May 7, 2017

    @ilevkivskyi
    Member

    Yes, in general case it should be like this:

    async def bar(x: int) -> str:
        return str(x)
    
    cbar: Callable[[int], Awaitable[str]] = bar

    So that in your case it should probably be:

    async def foo(x: int): pass
    callback: Callable[[int], Awaitable[None]] = foo

    Also note that without an explicit return annotation for foo mypy infers Callable[[int], Awaitable[Any]], according to PEP 484 all missing annotations are treated as Any.

    What are the exact steps to get Function does not return a value error? Are you using --strict-optional, a similar (spurious) error was reported recently in mypy, it can be quite misleading.

  3. changed the title [-]type hint for an async function used as a callback[/-] [+]Document how to type hint for an async function used as a callback[/+] on May 7, 2017
  4. brettcannon commented on May 7, 2017

    @brettcannon
    MemberAuthor

    I've changed the title of this issue to make a documentation thing so that an example in the Callable docs and show how to do this.

    @ilevkivskyi As for triggering the error, if I type hint the callback as Callable[[Any], None] it causes that error message.

  5. terrisgit commented on Jul 3, 2018

    @terrisgit

    From a newbie's point of view, it seems like Coroutine was added as a short-hand way to declare this (eg #251) but I can't figure out how to use it in Python 3.6. Maybe I need 3.7.

  6. JelleZijlstra commented on Jul 3, 2018

    @JelleZijlstra
    Member

    I don't think 3.7 makes a difference here, and you more likely want Awaitable instead of Coroutine. What issues are you running into?

  7. terrisgit commented on Jul 3, 2018

    @terrisgit

    Maybe a better question is: is Coroutine documented somewhere with an example? I will use Callable[[int], Awaitable[None]] but I do not understand the generic Coroutine and it merely bugs me, nothing more. I can ignore it. Thank you.

  8. JelleZijlstra commented on Jul 3, 2018

    @JelleZijlstra
    Member

    Not sure, but there are very few contexts where you'd actually need it. To declare an async callback, you would use Awaitable like in Ivan's and my messages further up in this thread.

  9. terrisgit commented on Jul 5, 2018

    @terrisgit

    I see the return value of an async function is now Coroutine instead of Awaitable. At any rate, this explains why Callable should be used instead of Coroutine. python/mypy@6519eb6

  10. vladap commented on Oct 31, 2019

    @vladap

    Callable[[int], Awaitable[None]] is as well satisfied by any def without async keyword which returns asyncio Future, Task or coroutine object. Type Coroutine specifies awaitables which support send() and throw() methods and the complete type is Coroutine[send_type, throw_type, return_type], each parameter is covariant.

    Coroutine[Any, Any, None] is the inferred type which is returned by your async def taking int and returning None when executed without await.

    Future/Task means you allow to schedule/execute tasks before a callback function starts or returns. Unlike when returning coroutine object which is scheduled at the code line where you await async def. If Future/Task should be disallowed then the type of the callback would have to be Callable[[int], Coroutine[Any, Any, None]]. It still supports plain def which returns coroutine object but it should not matter.

    And a closing note because of the similarity, Generator[send_type, throw_type, return_type] is an unrelated type to Coroutine[send_type, throw_type, return_type] hence they are not allowed as a callback nor async functions defined via @asyncio.coroutine annotation.

    Welcome to simplicity of typing:)

  11. added a commit that references this issue on May 25, 2020
  12. added and removed on Nov 4, 2021
  13. dimaqq commented on Nov 25, 2021

    @dimaqq

    I wonder if perhaps typing got any simpler in the last 2~4 years?

    I'd love to write something simpler than Callable[[], Awaitable[None]] for simple cases like this:

    async def startup():
        ...
    
    app.add_middleware(MyThing, startup=startup)
    
    class MyThing:
        def __init__(self, app: ..., startup: Callable[[], Awaitable[None]])

    Perhaps an alias, like AsyncFunction[] or AsyncCallable[] would be easier on the eyes and newcomers?

  14. gvanrossum commented on Nov 25, 2021

    @gvanrossum
    Member

    There’s a proposal on typing-sig that would allow writing ‘async () -> None’ for that case.

    https://mail.python.org/archives/list/typing-sig@python.org/message/JZLYRAXJV34WAV5TKEOMA32V7ZLPOBFC/

  15. added a commit that references this issue on May 1, 2022
    9588f88
  16. added 3 commits that reference this issue on May 1, 2022
    266e960
    e7de543
    fcbff77
  17. added a commit that references this issue on Jun 2, 2022
    c69fa62
  18. added a commit that references this issue on May 20, 2024
    508f13a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions