The MRE below is taken from this mypy issue. That issue and POC mypy implementation can provide more context into this question.
Consider the following:
from contextlib import asynccontextmanager, contextmanager from typing import AsyncIterator, Iterator, overload @overload @asynccontextmanager # Error here async def test_async() -> AsyncIterator[str]: ... @overload @asynccontextmanager # Error here async def test_async(val: int) -> AsyncIterator[int]: ... @asynccontextmanager async def test_async(val: int = 1) -> AsyncIterator[int | str]: yield val if val != 1 else 'a' @overload @contextmanager def test_sync() -> Iterator[str]: ... @overload @contextmanager def test_sync(val: int) -> Iterator[int]: ... @contextmanager def test_sync(val: int = 1) -> Iterator[int | str]: yield val if val != 1 else 'a' This happens because async functions return types are wrapped with Coroutine unless these functions are generators, and this condition is detected by yield presence in the body. overload definitions have usually only trivial bodies (ellipsis or pass) and do not contain yield. I’d argue that “generator-ness” should be propagated from the implementation to the overloads, marking overloads as async generators if the implementation is, because other ways to make this (correct) snippet pass are counterintuitive (either removing the decorator and constructing the type manually or adding yield to overloads bodies, I don’t see any other reasonable approach)
Type checkers survey:
mypy: wraps withCoroutine, error.Argument 1 to "asynccontextmanager" has incompatible type "Callable[[], Coroutine[Any, Any, AsyncIterator[int]]]"; expected "Callable[[], AsyncIterator[Never]]" [arg-type]pyright: same asmypyArgument of type "() -> Coroutine[Any, Any, AsyncIterator[str]]" cannot be assigned to parameter "func" of type "(**_P@asynccontextmanager) -> AsyncIterator[_T_co@asynccontextmanager]" in function "asynccontextmanager"pyre: accepts, produces expected reveal_typepytype: rejects, but also rejects trivial bodies oftest_sync.Function contextlib.asynccontextmanager was called with the wrong arguments [wrong-arg-types]
This issue is not currently addressed in typing specification. @hauntsaninja advised to start a topic here to discuss this potential change and bring it to sync with other typecheckers if agreed upon.