python / python/mypy

Generic type inferred wrong; curious difference betweer `aiter(f(x))` and `c = f(x); aiter(c)`

Open
#16,376 13 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-type-context
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

Consider this code (playground):

from typing import TypeVar, Generic, Callable, Coroutine, Any, AsyncIterator

_T = TypeVar("_T")

class StartedToken:
    pass

class ProducerContext(Generic[_T]):
    pass

async def coro_to_aiter(
    coroutine: Callable[[ProducerContext[_T]], Coroutine[Any, Any, None]]
) -> AsyncIterator[_T | StartedToken]:
    yield StartedToken()

async def coro(ctx: ProducerContext[int]) -> None:
    pass

# Split into two lines, no error:
c = coro_to_aiter(coro)  # no error
it = aiter(c)  # no error

# The same on one line -> mypy error:
it2 = aiter(coro_to_aiter(coro))  # error: Argument 1 to "coro_to_aiter" has incompatible type ...

Expected Behavior

I would expect mypy to not error on the it2 definition line. Moreover, regardless of whether mypy can handle these definitions, I would expect to see the same behavior (error or absence of error) in the "Split into two lines" case and the "on one line" case.

Actual Behavior

I get this error on the it2 line, and no errors on other lines:

main.py:24: error: Argument 1 to "coro_to_aiter" has incompatible type "Callable[[ProducerContext[int]], Coroutine[Any, Any, None]]"; expected "Callable[[ProducerContext[int | StartedToken]], Coroutine[Any, Any, None]]" [arg-type]

Your Environment

  • Mypy version used: 1.6.1 (playground), current master
  • Python version used: 3.11, 3.12

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the main.py reproducer from the issue, using the listed Python and mypy versions, and compare the split and nested aiter(coro_to_aiter(coro)) cases. Trace how generic type inference handles each call and add or update a focused regression test for the discrepancy. Done means both forms receive consistent, expected diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.