Wrapped methods are not properly resolved until after the class definition
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 20.6k
- フォーク
- 3.3k
- PR マージ指標
- PR 指標を取得中
説明
Context & setup
Consider the following simple protocol:
class Proto(Protocol):
@property
def f(self) -> int:
...
One can implement this protocol wrongly as follows:
T = TypeVar("T")
P = ParamSpec("P")
def wrapper(f: Callable[P, T]) -> Callable[P, str]:
def wrapped(*args: P.args, **kwargs: P.kwargs) -> str:
return 0
return wrapped
class Impl:
@wrapper # converts return type from `bool` to `str`, while the protocol expects `int`
def f(self) -> bool:
return False
# error: Incompatible return value type (got "Impl", expected "Proto") [return-value]
# note: Following member(s) of "Impl" have conflicts:
# note: Expected:
# note: def f(self) -> int
# note: Got:
# note: def f(self) -> str
def b() -> Proto:
return Impl()
The bug (false negative)
When this same function b is placed before class Impl, mypy does not report an error. Adding reveal_type(Impl.f) before and after the class definition hints at what is going on:
main.py:21: error: Expression has type "Any" [misc]
main.py:21: error: Cannot determine type of "f" [has-type]
main.py:21: note: Revealed type is "Any"
main.py:21: error: Name "Impl" is used before definition [used-before-def]
main.py:28: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
If f is not decorated but explicitly returns str, one gets
main.py:21: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
main.py:21: error: Name "Impl" is used before definition [used-before-def]
main.py:27: note: Revealed type is "def (self: __main__.Impl) -> builtins.str"
as expected. This indicates that the wrapped decorator is only properly resolved for code after the class definition.
False positive variant
If f is decorated with @property, this can also produce false positives. This is how I encountered the issue in the first place. Playground.
Additional info
Python: 3.11
Mypy: 371219347a6d17e16924bbabf3e693c6874e7138
Flags: --strict --disallow-any-*
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
リンクされた mypy-play の例を使って main.py の偽陰性と偽陽性を再現し、関数の配置、@wrapper、@property を比較する。class Impl の前後で decorator とラップされたメソッドの型がどのように解決されるかを追跡する。両方のバリエーションで、期待される protocol 互換性の診断が一貫して報告されれば完了とする。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- tooling
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 35/100