python / python/mypy

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

未关闭
#17,601 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

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. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

先使用 mypy 1.14.0 开始最小的 Python 3.11 复现,并比较 BaseDataClass 和 DerivedDataClass 的 reveal_type 结果。调查带有 descriptor 属性的冻结 dataclass 继承;当派生类在类上报告 FloatParameter、在实例上报告 float | None,且与基类一致时,即视为完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
devtools
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
描述清楚
新手友好度
45/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。