python / python/mypy

mypy infer incorrect types in frozen dataclass with descriptors as attributes.

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

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

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

説明

I am having issues with frozen dataclass that inherits from a frozen dataclass with descriptors as attributes. mypy infer incorrect datatypes of the attributes from the base class. If the dataclasses are not frozen, then mypy infer correct datatypes in inherited class.

Below is the minimal code to reproduce the issue.
python=3.11
mypy=1.14.0

mypy-play.net

To Reproduce


from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
from typing import Self, SupportsFloat, TypeVar, overload, Type, Optional, Generic, Any

T = TypeVar("T")
V = TypeVar("V")


class Base(Generic[T, V], metaclass=ABCMeta):
    def __set_name__(self, owner: type, name: str) -> None:
        self.public_name = name
        self.private_name = "_" + name

    @overload
    def __get__(self, instance: None, owner: Optional[Type[Any]] = None) -> Self:...

    @overload
    def __get__(self, instance: Any, owner: Optional[Type[Any]] = None) -> V | None:...

    def __get__(self, instance: Any, owner: Optional[Type[Any]] = None) ->  Self | V | None:
        if instance is None:
            return self
        return getattr(instance, self.private_name, None)

    def __set__(self, instance: Any, value: Optional[T]) -> None:
        if value is self or value is None:
            return
        converted = self._converter(value)
        object.__setattr__(instance, self.private_name, converted)

    @abstractmethod
    def _converter(self, value: T) -> V:
        """
        Hook to implement conversion logic
        """

class FloatParameter(Base[SupportsFloat, float]):
    def _converter(self, value: SupportsFloat) -> float:
        """Can have more logic here to check if value can be converted to float"""
        return float(value)
        

@dataclass(frozen=True, kw_only=True)
class BaseDataClass:
    float_param: FloatParameter = FloatParameter()


@dataclass(frozen=True, kw_only=True)
class DerivedDataClass(BaseDataClass):
    boolean: bool = True


reveal_type(BaseDataClass.float_param) # correct FloatParameter
reveal_type(DerivedDataClass.float_param) # Incorrect Union[typing.SupportsFloat, None]

reveal_type(BaseDataClass().float_param) # Correct "Union[builtins.float, None]"
reveal_type(DerivedDataClass().float_param) # Incorrect Union[typing.SupportsFloat, None]

NOTE: Pylance on the other hand on VSCode shows correct datatypes.

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

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

はじめの一歩

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

調査の方向性

mypy 1.14.0 を使用した最小限の Python 3.11 再現から始め、BaseDataClass と DerivedDataClass の reveal_type の結果を比較します。descriptor 属性を持つ frozen dataclass の継承を調査します。派生クラスがクラス上では FloatParameter、インスタンス上では float | None を報告し、基底クラスと一致すれば完了です。

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

評価

技術スタック
python
領域
devtools
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
45/100

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

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