python / python/mypy

false positive: classmethod constructor on generic class

Aperta
#9,201 3 commenti 1 reazione 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

needs discussion topic-type-variables
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

I'm inclined to think this is a bug, but could be argued it's a missing feature.

How to reproduce

# makes_bug.py

from typing import Generic, Type, TypeVar

T = TypeVar('T')

class Foo(Generic[T]):
    def __init__(self, value: T) -> None: ...
    @classmethod
    def construct(cls) -> Foo[str]:
        return cls('bar')

Expected outcome

This code should pass type checks.

Actual outcome

mypy, to my great surprise, fails:

$ python --version
Python 3.8.1
$ mypy --version
mypy 0.790+dev.65186ae1e23fd44f1d7e6aa2c4458bdf55640742
$ mypy src/makes_bug.py
makes_bug.py:11: error: Incompatible return value type (got "Foo[T]", expected "Foo[str]")
makes_bug.py:11: error: Argument "value" to "Foo" has incompatible type "str"; expected "T"
Found 2 errors in 1 file (checked 1 source file)

Workaround

Changing line 10 to def construct(cls: Type[Foo]) -> Foo[str]: fixes it; or, probably more or less equivalently, def construct(cls: Type[Foo[Any]]) -> Foo[str]:.

Investigation / suggested fix

Adding reveal_type(cls) indicates that cls is inferred to have type Foo[T], which cannot return a Foo[str].

It's not clear to me why classmethods should have the lead argument inferred to a bound type. I'd propose that my workaround be made a default: class methods should infer the type of the lead argument type as the unbound class type, or bound with all Anys.

I'd imagine the change would go here: https://github.com/python/mypy/blob/65186ae1e23fd44f1d7e6aa2c4458bdf55640742/mypy/semanal.py#L609,L610
or (more likely) in the referenced method class_type():
https://github.com/python/mypy/blob/65186ae1e23fd44f1d7e6aa2c4458bdf55640742/mypy/semanal.py#L4790,L4791

Happy to take a stab at it, but wanted to solicit some feedback first. This is a complex system and it's possible I'm missing some potential consequence here.

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

Inizia con la riproduzione generica di classmethod in makes_bug.py e ispeziona mypy/semanal.py alle righe indicate, in particolare class_type(). Usa reveal_type(cls) ed esegui mypy sull’esempio per confermare il tipo dedotto. È completato quando l’esempio supera il controllo dei tipi senza il workaround Type[Foo], mantenendo il comportamento generico esistente.

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

Valutazione

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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.