Files
ruff/crates/ty_python_semantic/resources/mdtest/async.md
Douglas Creager c02bd11b93 [ty] Infer typevar specializations for Callable types (#21551)
This is a first stab at solving
https://github.com/astral-sh/ty/issues/500, at least in part, with the
old solver. We add a new `TypeRelation` that lets us opt into using
constraint sets to describe when a typevar is assignability to some
type, and then use that to calculate a constraint set that describes
when two callable types are assignable. If the callable types contain
typevars, that constraint set will describe their valid specializations.
We can then walk through all of the ways the constraint set can be
satisfied, and record a type mapping in the old solver for each one.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-12-16 09:16:49 -08:00

2.2 KiB

async / await

Basic

async def retrieve() -> int:
    return 42

async def main():
    result = await retrieve()

    reveal_type(result)  # revealed: int

Generic async functions

from typing import TypeVar

T = TypeVar("T")

async def persist(x: T) -> T:
    return x

async def f(x: int):
    result = await persist(x)

    reveal_type(result)  # revealed: int

Use cases

Future

import asyncio
import concurrent.futures

def blocking_function() -> int:
    return 42

async def main():
    loop = asyncio.get_event_loop()
    with concurrent.futures.ThreadPoolExecutor() as pool:
        result = await loop.run_in_executor(pool, blocking_function)
        reveal_type(result)  # revealed: int

asyncio.Task

import asyncio

async def f() -> int:
    return 1

async def main():
    task = asyncio.create_task(f())

    result = await task

    reveal_type(result)  # revealed: int

asyncio.gather

import asyncio

async def task(name: str) -> int:
    return len(name)

async def main():
    (a, b) = await asyncio.gather(
        task("A"),
        task("B"),
    )

    reveal_type(a)  # revealed: int
    reveal_type(b)  # revealed: int

Under the hood

[environment]
python-version = "3.12"  # Use 3.12 to be able to use PEP 695 generics

Let's look at the example from the beginning again:

async def retrieve() -> int:
    return 42

When we look at the signature of this function, we see that it actually returns a CoroutineType:

reveal_type(retrieve)  # revealed: def retrieve() -> CoroutineType[Any, Any, int]

The expression await retrieve() desugars into a call to the __await__ dunder method on the CoroutineType object, followed by a yield from. Let's first see the return type of __await__:

reveal_type(retrieve().__await__())  # revealed: Generator[Any, None, int]

We can see that this returns a Generator that yields Any, and eventually returns int. For the final type of the await expression, we retrieve that third argument of the Generator type:

from typing import Generator

def _():
    result = yield from retrieve().__await__()
    reveal_type(result)  # revealed: int