Skip to content

Inconsistent errors reported for protocol matching #13850

Closed

Description

@erictraut

I ran into some strange behavior in mypy when investigating a bug reported against pyright.

Here is a sample that demonstrates the issue:

from typing import Generic, Protocol, TypeVar, overload

_T_co = TypeVar("_T_co", covariant=True)

class _NestedSequence(Protocol[_T_co]):
    @overload
    def __getitem__(self, index: int) -> _T_co | "_NestedSequence[_T_co]":
        ...

    @overload
    def __getitem__(self, index: slice) -> "_NestedSequence[_T_co]":
        ...

    # def __contains__(self, __s: object, /) -> bool:
    #     ...

class MySequence(Generic[_T_co]):
    @overload
    def __getitem__(self, index: int) -> _T_co:
        ...

    @overload
    def __getitem__(self, index: slice) -> "MySequence[_T_co]":
        ...

    def __getitem__(self, index: slice | int) -> "MySequence[_T_co]" | _T_co:
        ...

    def __contains__(self, __s: object, /) -> bool:
        ...

class MySequenceCopy(Generic[_T_co]):
    @overload
    def __getitem__(self, index: int) -> _T_co:
        ...

    @overload
    def __getitem__(self, index: slice) -> "MySequenceCopy[_T_co]":
        ...

    def __getitem__(self, index: slice | int) -> "MySequenceCopy[_T_co]" | _T_co:
        ...

    def __contains__(self, __s: object, /) -> bool:
        ...

def func1_1(x: MySequence[int]):
    _: _NestedSequence[int] = x  # (no error)

def func1_1_copy(x: MySequence[int]):
    _: _NestedSequence[int] = x  # error: Incompatible types in assignment

def func1_2(x: MySequence[MySequence[int]]):
    _: _NestedSequence[int] = x  # (no error)

def func2_1(x: MySequenceCopy[int]):
    _: _NestedSequence[int] = x  # error: Incompatible types in assignment

def func2_1_copy(x: MySequenceCopy[int]):
    _: _NestedSequence[int] = x  # error: Incompatible types in assignment

def func2_2(x: MySequenceCopy[MySequenceCopy[int]]):
    _: _NestedSequence[int] = x  # error: Incompatible types in assignment

a: _NestedSequence[int] = [[1, 2, 3]]  # error: List item 0/1/2 has incompatible type "int"; expected "_NestedSequence[int]"

Strange behavior 1: Function func1_1_copy is an exact copy of func1_1, yet mypy reports an error in func1_1_copy but not in func1_1.

Strange behavior 2: The protocol class MySequenceCopy is an exact copy of MySequence, so I would expect them to behave the same from a type checking perspective. func2_2 is a copy of func2_1 with MySequence replaced with MySequenceCopy, so I would expect the two to produce the same type checking results. Mypy emits no error for func1_2 but emits an error for func2_2.

Strange behavior 3: The protocol class _NestedSequence contains a commented-out __contains__ method. If this method is added back, I would expect there to be no change to the type checking results. Yet, when __contains__ is added here, all of the errors previously reported by mypy disappear. This includes the errors in the final line of the sample. I would not expect the addition of __contains__ to have any impact on the results.

I'm not sure if these three issues are related, but I suspect they are.

I'm using the latest published version of mypy (0.982).

Activity

  1. ilevkivskyi commented on Jan 25, 2025

    @ilevkivskyi
    Member

    On a recent master I see no errors (with or without the __contains__ in the initial protocol).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions