python / python/mypy

Incorrect specialisation of inferred lambda types based on early binding assumption (sometimes)

Aperta
#9,658 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

If a lambda is defined in a partially specialised context, its type implicitly captures the specialisation of the context, even though the specialisation may no longer hold at the point it gets executed. This changes unexpectedly if a type hint is given.

For example

def example_1(p1: Optional[int]):
    if p1 is not None:
        late_p1 = (lambda: p1)
        reveal_type(late_p1)

Revealed type is 'def () -> builtins.int'

However, if we give it a type annotation on the assignment line, it complains.

def example_2(p1: Optional[int]):
    if p1 is not None:
        late_p1: Callable[[], int] = (lambda: p1)
        reveal_type(late_p1)

This time it abandons the local context that p1 has been checked against None, and considers that the broadest type of p1 is Optional. And even though that signature is exactly what it otherwise infers, it now raises two errors on the same assignment line:

error: Incompatible types in assignment (expression has type "Callable[[], Optional[int]]", variable has type "Callable[[], int]")
error: Incompatible return value type (got "Optional[int]", expected "int")

The behaviour in example_1 is buggy: because of late binding it is possible for the former behaviour to leak wider types.

def example_3(p1: Optional[int]):
    if p1 is not None:
        late_p1 = (lambda: p1)
    p1 = None
    inc = late_p1() + 1 # This will crash, but MyPy is perfectly confident that `late_p1()` is an integer.

Example_2 considered in isolation exhibits expected behaviour.
It is a bit odd that type a type hint that matches the original inferred type changes and indeed is suddenly incompatible with the inferred type.

Seen on this StackOverflow question: https://stackoverflow.com/q/64203221/1688786

  • Mypy version used: mypy 0.790
  • Python version used: 3.8.2
  • Operating system and version: Windows 10

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Iniziate riproducendo gli esempi 1–3 con la versione di mypy indicata e confrontate l’inferenza di lambda con e senza l’annotazione Callable. Tracciate il percorso del controllo dei tipi per le variabili ristrette catturate dalle lambda; il lavoro è completato quando le catture late-bound non mantengono un restringimento non valido e il caso annotato si comporta in modo coerente, senza errori duplicati o contraddittori.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
compilers, devtools
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
38/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.