python / python/mypy

How to use typing.Never to indicate a subclass method with zero arguments cannot be called?

Aperta
#14,726 0 commenti 1 reazione 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

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

Descrizione

Feature

If you have a subclass that has a method defined in the superclass, but in the subclass should never get called, you should be able to declare self to have type Never.

Pitch

Let's suppose I have a base class Base with a method foo(), and a subclass Sub that raises an Exception if foo() is called. I'd like it so that the type checker flags an error on Sub.foo(), but not Base.foo(), so I tried this:

from typing_extensions import Never


class Base:
    def __init__(self):
        pass

    def foo(self) -> None:
        print("foo")
        
    def goo(self, x: int) -> None;
        print("goo ", x)


class Sub(Base):
    def foo(self: Never) -> None:
        raise Exception("never")
    
    def goo(self, x: Never) -> None:
        raise Exception("never")


x = Sub()
x.foo()
x.goo(3)

With mypy 1.0.0 and python 3.10, I get the following:

neversub.py:16: error: The erased type of self "<nothing>" is not a supertype of its class "neversub.Sub"  [misc]
neversub.py:19: error: Argument 1 of "goo" is incompatible with supertype "Base"; supertype defines the argument type as "int"  [override]
neversub.py:19: note: This violates the Liskov substitution principle
neversub.py:19: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
neversub.py:24: error: Invalid self argument "Sub" to attribute function "foo" with type "Callable[[NoReturn], None]"  [misc]
neversub.py:25: error: Argument 1 to "goo" of "Sub" has incompatible type "int"; expected "NoReturn"  [arg-type]

The last two errors are exactly what I want. But while the first error makes sense, aside from using a # type: ignore, I'm not sure how this should be declared to avoid having to use # type: ignore. In other words, what is the right way to indicate that a method with zero parameters is not valid to call in a subclass? Secondly, shouldn't the declaration of goo be considered as not violating the Liskov substitution principle based on how typing.Never is supposed to work?

I should note that with the method goo(), since it has a required argument, I can use the Never declaration to show that this is invalid. What's not clear is how do you indicate that a method with no arguments is invalid without having to use # type: ignore ?

I think this is a valid use of Never, and Eric Traut from pyright agrees: https://github.com/microsoft/pyright/issues/4653#issuecomment-1434851320

Could mypy do the same?

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 riproducendo l'esempio Base/Sub fornito con mypy 1.0.0 e confronta le diagnosi per foo e goo con il comportamento previsto di Never. Il lavoro è completo quando un metodo di sottoclasse senza argomenti può essere dichiarato non chiamabile senza un type: ignore e l'override di goo viene gestito in modo coerente con la regola di sostituzione di Liskov.

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

Valutazione

Stack tecnologico
python
Ambito
compilers
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
30/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.