python / python/mypy

stubgen generates stubs for `@asynccontextmanager` functions that mypy itself rejects

Offen
#21,869 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug topic-stubgen
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug Report

stubgen copies @asynccontextmanager and the async def keyword verbatim into the generated .pyi. Because a stub body has no yield, mypy then classifies the stubbed function as a coroutine function returning AsyncIterator[T] rather than as an async generator function, and rejects the decorator application.

The result is that stubgen emits a stub which mypy — the same version, with default settings — reports an error on. Source that type-checks cleanly produces a stub that does not.

To Reproduce

# cm.py
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager


@asynccontextmanager
async def ctx() -> AsyncIterator[int]:
    yield 1
$ mypy cm.py
Success: no issues found in 1 source file

$ stubgen -o out cm.py
Processed 1 modules
Generated out/cm.pyi

$ cat out/cm.pyi
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager

@asynccontextmanager
async def ctx() -> AsyncIterator[int]: ...

$ mypy out/cm.pyi
out/cm.pyi:4: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[], Coroutine[Any, Any, AsyncIterator[int]]]"; expected "Callable[[], AsyncIterator[Never]]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

The same happens for methods, and with AsyncGenerator[T, None] in place of AsyncIterator[T].

Expected Behavior

stubgen output should type-check under the mypy version that produced it. For an @asynccontextmanager-decorated async generator, that means emitting one of the two spellings the typing docs sanction for stubs — either dropping async:

@asynccontextmanager
def ctx() -> AsyncIterator[int]: ...

or dropping the decorator and declaring the decorated result, as typeshed does:

from contextlib import AbstractAsyncContextManager

def ctx() -> AbstractAsyncContextManager[int]: ...

Both of these are accepted by mypy 2.3.0.

Actual Behavior

out/cm.pyi:4: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[], Coroutine[Any, Any, AsyncIterator[int]]]"; expected "Callable[[], AsyncIterator[Never]]"  [arg-type]

Your Environment

  • Mypy version used: 2.3.0 (compiled: yes)
  • Mypy command-line flags: none (mypy out/cm.pyi); stub produced with stubgen -o out cm.py
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.11.15

Additional notes

This is a change in behaviour from 1.15.0. That version's stubgen omitted the decorator entirely:

# stubgen 1.15.0
async def ctx() -> AsyncIterator[int]: ...

which is also lossy, but happens to type-check, so the problem only becomes visible on 2.3.0 once the decorator is preserved.

The synchronous case is unaffected — @contextmanager over def f() -> Iterator[T] round-trips through stubgen and type-checks on 2.3.0, because there is no async keyword to change how the return type is interpreted.

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit dem stubgen-Einstiegspunkt und reproduziere das Problem mit cm.py, dem @asynccontextmanager-Beispiel aus dem Bericht. Vergleiche die generierte .pyi-Ausgabe für asynchrone Generatorfunktionen und -methoden und führe anschließend mypy für das generierte Stub aus. Erledigt ist die Aufgabe, wenn das generierte Stub für diesen Fall ohne Fehler typgeprüft wird und dabei die beabsichtigte Context-Manager-Typisierung erhalten bleibt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
cli, tooling
Issue-Typ
Bug
Schwierigkeit
3/5
Geschätzter Aufwand
1-2 Tage
Aktivitätsstatus
Ruhig
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
72/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.