Return types for Overloads with "Optional[TypeVar]" arguments are deducted as just "object"
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 20.6k
- フォーク
- 3.3k
- PR マージ指標
- PR 指標を取得中
説明
Bug Report
Please see the following mypy playground: https://mypy-play.net/?mypy=latest&python=3.12&gist=207fe333eb8e226d36b84bf34be5ca65
I personally encountered this on mypy 1.7.1 and Python 3.8, but the mypy playground link uses the latest version of everything.
Consider the following helper function:
import datetime
from typing import Any, Optional, Sized, Tuple, Type, TypeVar, Union
from typing_extensions import Literal, overload, TypeAlias
_T = TypeVar("_T")
class TypeGuardError(Exception):
pass
_THasLen = TypeVar("_THasLen", bound=Sized)
@overload
def required(
value: Optional[_T],
*,
message: Optional[str] = None,
property_name: Optional[str] = None,
) -> _T:
pass
@overload
def required(
value: Any,
*,
instance_of: Type[_T],
instance_of_message: Optional[str] = None,
message: Optional[str] = None,
property_name: Optional[str] = None,
) -> _T:
pass
def required(
value: Optional[Any],
*,
instance_of: Optional[Any] = None,
message: Optional[str] = None,
property_name: Optional[str] = None,
instance_of_message: Optional[str] = None,
) -> Any:
if property_name is not None:
message_prefix = f"{property_name}: "
else:
message_prefix = ""
if value is None:
raise TypeGuardError(message or f"{message_prefix}Value cannot be `None`.")
if instance_of and not isinstance(value, instance_of):
raise TypeGuardError(
instance_of_message
or message
or f'{message_prefix}Expected value of type "{instance_of.__name__}", got value of type "{type(value).__name__}" instead.'
)
return value
required here just checks if the value is not None, optionally verifying its type as well. If any validation fails, it's able to generate an error, with either a message you define, or a parameterizable auto-generated one.
The returned value is just the input value, but cast to its "pure type" through overloading (removing None from the type by type-matching it to Optional). But when passing the instance_of argument, the return type is whatever type you pass to that argument instead. That's the intention.
However, mypy fails my expectations. For example:
TimeDeltaLike: TypeAlias = Union[int, float, str, datetime.timedelta]
default_lock_time: Optional[TimeDeltaLike] = None
locked_for = required(
default_lock_time,
message=f"Entity does not specify a default lock time.",
)
# QUESTION: Why is the revealed type `object`?
reveal_type(locked_for)
In the last line, the revealed type is object.
When analyzing the same code through Pyright, the revealed type is int | float | str | datetime.timedelta, as I would expect.
To Reproduce
# Ideally, a small sample program that demonstrates the problem.
# Or even better, a reproducible playground link https://mypy-play.net/ (use the "Gist" button)
See explanation above.
Expected Behavior
See explanation above.
Actual Behavior
See explanation above.
Your Environment
- Mypy version used: 1.7.1
- Mypy command-line flags:
- Mypy configuration options from
mypy.ini(and other config files): - Python version used: 3.8
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
リンク先の mypy playground と Optional[TypeVar] を使用する required オーバーロードから始め、reveal_type の前に default_lock_time を指定した呼び出しがどのように推論されるかを調べます。instance_of オーバーロードの動作を維持したまま、表示される戻り値の型が object ではなく int | float | str | datetime.timedelta になれば作業完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- devtools
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 42/100