MyPy fails type narrowing on `not issubclass(X, type[T]): raise`
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 20.6k
- フォーク
- 3.3k
- PR マージ指標
- PR 指標を取得中
説明
Bug Report
MyPy fails to narrow types using issubclass, and expects an isinstance check which is wrong.
MyPy fails type narrowing on not issubclass(X, type[T]): raise and complains about returning X() later for the function marked as returning T.
To Reproduce
[Mypy Playground example](https://gist.github.com/mypy-play/dcd17343b289523c197a31f850eca90f)
```python from collections.abc import Iterator from typing import TYPE_CHECKING
class MySentinel:
pass
# Works as expected
class OptionalSentinel:
def __init__(self, value: object) -> None:
self._value = value
def get_value[T](self, expected: type[T]) -> Iterator[T]:
if not isinstance(self._value, expected):
raise TypeError
yield self._value
# Reveals bug
class MagicIterator:
def raw[T](self, expected: type[T]) -> Iterator[T]:
"""Get a list of repositories which accept a specific lint to run."""
if not issubclass(MySentinel, expected):
raise ValueError
# if TYPE_CHECKING:
# # an ugly workaround to make mypy happy
# if not isinstance(expected, MySentinel):
# raise ValueError
for project in [MySentinel(), MySentinel()]:
yield project # error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
```
Expected Behavior
I expected mypy to infer that after an if not issubclass(T, X): raise block X must be subclass T and it should be ok to return X in place of T.
Since I pass a type and want to compare it to a type an isinstance check is not appropriate for MagicIterator, neiter o I think the issubclass check is the most appropriate (I would prefer to check using expected is not T to guarantee that expected is actually exactly T but ìs`i not listed in the list of supported narrowing expressions.
Actual Behavior
Mypy is only happy if I add an isinstance check, which I deem wrong since I dont pass an instance but a type to the function. Thus, if I do not put the incorrect code in a if TYPE_CHECKCING block my program crashes
src/typeTest.py:31: error: Incompatible types in "yield" (actual type "MySentinel", expected type "T") [misc]
Found 1 error in 1 file (checked 1 source file)
Your Environment
- Mypy version used: mypy 1.18.2 (compiled: yes)
- Mypy command-line flags: none (
mypy src) - Mypy configuration options from
pyproject.toml(and other config files): - Python version used: 3.13.7 (uv)
** Possibly related **:
- https://github.com/python/mypy/issues/10680 (I think these go hand in hand)
- https://github.com/python/mypy/issues/19529
- https://github.com/python/mypy/issues/9003 (might be related, not sure)
- https://github.com/python/mypy/issues/17728 (similar but seems different)
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
リンクされている MyPy Playground の例から始め、失敗する issubclass の narrowing パスと、動作する isinstance の例を比較してください。関連する issue #10680、#19529、#9003、#17728 を読んでから、該当する narrowing テストと実装を見つけてください。例が誤ったランタイム workaround なしで型チェックを通れば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- devtools
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 35/100