polymorphic overloads on `list.__add__` and `dict.__or__` lead to divergences in type checkers.

未關閉
#14,283 1 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
35/100
Issue 類型
缺陷
描述清晰度
基本清楚
活躍度
停滯
技術堆疊
python
領域
tooling

研究方向

從 list.add 和 dict.or 的多載開始,然後檢視 PRs #14282 和 #14284,以了解提議的簡化方案。使用 mypy 和 pyright 重現 list 和 dict 範例,並使用 mypy-primer 驗證所選的 stub 變更能移除不一致的 wrapper 錯誤。

由索引模型根據 Issue 內容生成。

描述

The overloads on list.__add__ lead to divergent behavior between mypy and pyright when doing something as simple as a list concatenation

    # Overloading looks unnecessary, but is needed to work around complex mypy problems
    @overload
    def __add__(self, value: list[_T], /) -> list[_T]: ...
    @overload
    def __add__(self, value: list[_S], /) -> list[_S | _T]: ...

Code sample in pyright playground, https://mypy-play.net/?mypy=latest&python=3.12&gist=abf6a8834020af17a16bd8cfb44b2f10

from typing import Any, overload

class ListA[T]:  # emulates builtins list
    @overload
    def __add__(self, other: "ListA[T]", /) -> "ListA[T]": return ListA()
    @overload
    def __add__[S](self, other: "ListA[S]", /) -> "ListA[T | S]": return ListA()
    
    
class ListB[T]:  # without overloads
    def __add__[S](self, other: "ListB[S]", /) -> "ListB[T | S]": return ListB()

                                            # mypy              | pyright                             
reveal_type( list[str]() + list[str]() )    # list[str]         | list[str]          ✅️
reveal_type( list[str]() + list[int]() )    # list[str | int]   | list[str | int]    ✅️
reveal_type( list[str]() + list[Any]() )    # list[Any]         | list[str]          ❌️

reveal_type( ListA[str]() + ListA[str]() )  # ListA[str]        | ListA[str]         ✅️
reveal_type( ListA[str]() + ListA[int]() )  # ListA[str | int]  | ListA[str | int]   ✅️
reveal_type( ListA[str]() + ListA[Any]() )  # ListA[Any]        | ListA[str]         ❌️

reveal_type( ListB[str]() + ListB[str]() )  # ListB[str]        | ListB[str]         ✅️
reveal_type( ListB[str]() + ListB[int]() )  # ListB[str | int]  | ListB[str | int]   ✅️
reveal_type( ListB[str]() + ListB[Any]() )  # ListB[str | Any]  | ListB[str | Any]   ✅️

This ultimately causes some very annoying type errors when checking wrapper functions in pyright.

Code sample in pyright playground

from typing import Mapping, Any

# function with 2 optional arguments
def foo(arg: object, /, *, opt1: str = ..., opt2: int = ...) -> None: ...

# wrapper that forwards args via dict
def foo_wrapper(arg: object, foo_kwargs: Mapping[str, Any]) -> None:
    # apply new defaults
    foo_kwargs = {"opt1": "new_default"} | dict(foo_kwargs)
    foo(arg, **foo_kwargs)  # "str" cannot be assigned to parameter "opt2"

PR #14282 and #14284 show mypy-primer results of simplifying the overloads away from list.__add__ and dict.__or__.

主要語言
Python
星號
5.1k
分支
2.1k
平均合併
2 天 5 小時
30 天內合併 PR
122

環境準備

  • 沒有 Dockerfile 或 Docker Compose 檔案
  • 沒有 Pull Request 範本
  • 閱讀貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

python/typeshed 的其他 Issue

查看 python/typeshed 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。