python / python/mypy

Incorrect type inference with PEP 646 Unpack and self-type annotations

Offen
#20,651 11 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug topic-pep-646 topic-type-variables
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug Report

When a generic class uses TypeVarTuple (PEP 646) and a method has a self-type annotation that "reshapes" the type parameters, mypy incorrectly infers the return type when the class is instantiated with Unpack[Tuple[Any, ...]].

Specifically, mypy infers the return type as None instead of Any | None, leading to false "unreachable code" errors.

To Reproduce

from typing import Generic, TypeVar, Optional, Iterator, Any, Tuple
from typing_extensions import TypeVarTuple, Unpack

_T = TypeVar("_T", bound=Any)
_Ts = TypeVarTuple("_Ts")


class Result(Generic[Unpack[_Ts]]):
    """Generic result class that takes variadic type parameters.

    This mimics SQLAlchemy's Result class which is generic over Unpack[_Ts].
    """

    def scalar(self: "Result[_T, Unpack[Tuple[Any, ...]]]") -> Optional[_T]:
        return None




def test_function() -> Iterator[Any]:
    result: Result[Unpack[Tuple[Any, ...]]] = Result()
    reveal_type(result)
    data = result.scalar()
    reveal_type(data)
    if data is None:
        return
    yield from iter(data)

Expected Behavior

If we run pyright, the type of "data" is "Any | None" and the yield from is reachable:

$ pyright mypy_bug_repro.py 
/home/classic/dev/sqlalchemy/mypy_bug_repro.py
  /home/classic/dev/sqlalchemy/mypy_bug_repro.py:22:17 - information: Type of "result" is "Result[*tuple[Any, ...]]"
  /home/classic/dev/sqlalchemy/mypy_bug_repro.py:24:17 - information: Type of "data" is "Any | None"
0 errors, 0 warnings, 2 informations

Actual Behavior

Mypy sees "data" as None in all cases

$ mypy --warn-unreachable mypy_bug_repro.py 
mypy_bug_repro.py:22: note: Revealed type is "mypy_bug_repro.Result[Unpack[builtins.tuple[Any, ...]]]"
mypy_bug_repro.py:24: note: Revealed type is "None"
mypy_bug_repro.py:27: error: Statement is unreachable  [unreachable]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version: mypy 1.19.1 (compiled: yes)
  • Python version: 3.14
  • typing_extensions version: latest (for TypeVarTuple, Unpack)
  • Pyright version (for comparison): 1.1.408

notes

So I used Claude to refactor SQLAlchemy's structures into standalone features. Additional commentary from Claude which may or may not be helpful:

The bug appears to be in mypy's type parameter matching logic. When trying to match:

  • Instance type: Result[Unpack[Tuple[Any, ...]]]
  • Against self-type: Result[_T, Unpack[Tuple[Any, ...]]]

Mypy should extract _T = Any from the first element of the unpacked tuple, but instead appears to fail at this extraction, possibly binding _T to Never or treating it as non-existent, resulting in Optional[Never] = None.

downstream SQLAlchemy issues is at https://github.com/sqlalchemy/sqlalchemy/discussions/13089 which is local to the new 2.1.0beta1 release that uses pep-646

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 im Issue beschriebenen eigenständigen Reproducer und führe ihn mit mypy --warn-unreachable aus, um den aufgelösten Typ und den Fehler wegen unerreichbaren Codes zu bestätigen. Verfolge mypy's Abgleich der Typparameter für Result[Unpack[Tuple[Any, ...]]] mit der self-Typannotation; abgeschlossen ist die Aufgabe, wenn scalar() als Any | None inferiert wird und der yield-Pfad nicht mehr als unerreichbar gemeldet wird.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
compilers
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

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