python / python/mypy

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

オープン
#14,726 コメント 0 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

feature
主要言語
Python
スター
20.6k
フォーク
3.3k
PR マージ指標
PR 指標を取得中

説明

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?

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、提供された Base/Sub の例を mypy 1.0.0 で再現し、foo と goo の診断を意図された Never の動作と比較します。引数を取らないサブクラスメソッドを type: ignore なしで呼び出し不可能として宣言でき、goo のオーバーライドが Liskov 置換原則に一貫して従って処理されれば、作業は完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
compilers
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
30/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。