python / python/typeshed

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

Đang mở
#14,283 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Ngôn ngữ chính
Python
Star
5.1k
Fork
2.1k
Merge trung bình
1 ngày 19 giờ
Pull request đã merge (30 ngày)
82

Mô tả

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__.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với các overload của list.add và dict.or, sau đó xem xét các PR #14282 và #14284 về đề xuất đơn giản hóa. Tái hiện các ví dụ về list và dict bằng mypy và pyright, đồng thời sử dụng mypy-primer để xác minh rằng các thay đổi stub được chọn loại bỏ các lỗi wrapper khác biệt.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
tooling
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.