polymorphic overloads on `list.__add__` and `dict.__or__` lead to divergences in type checkers.
未关闭
还没有人认领这个 Issue。
- 主要语言
- Python
- 星标
- 5.1k
- 派生
- 2.1k
- 平均合并
- 1 天 19 小时
- 30 天内合并 PR
- 82
描述
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__.
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 list.add 和 dict.or 的重载开始,然后查看 PRs #14282 和 #14284,了解所提议的简化方案。使用 mypy 和 pyright 重现 list 和 dict 示例,并使用 mypy-primer 验证所选的 stub 更改能够移除不一致的 wrapper 错误。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- tooling
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100