Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,7 @@ def get_conflict_protocol_types(
subtype = mypy.typeops.get_protocol_member(left, member, class_obj)
if not subtype:
continue
is_compat = is_subtype(subtype, supertype, ignore_pos_arg_names=True, options=options)
is_compat = is_subtype(subtype, supertype, options=options)
if not is_compat:
conflicts.append((member, subtype, supertype, False))
superflags = get_member_flags(member, right)
Expand Down
13 changes: 2 additions & 11 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,6 @@ def f(self) -> A: ...
for member in right.type.protocol_members:
if member in members_not_to_check:
continue
ignore_names = member != "__call__" # __call__ can be passed kwargs
# The third argument below indicates to what self type is bound.
# We always bind self to the subtype. (Similarly to nominal types).
supertype = find_member(member, right, left)
Expand All @@ -1234,9 +1233,7 @@ def f(self) -> A: ...
# Nominal check currently ignores arg names
# NOTE: If we ever change this, be sure to also change the call to
# SubtypeVisitor.build_subtype_kind(...) down below.
is_compat = is_subtype(
subtype, supertype, ignore_pos_arg_names=ignore_names, options=options
)
is_compat = is_subtype(subtype, supertype, options=options)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to also remove it from SubtypeContext() ~50 lines below.

else:
is_compat = is_proper_subtype(subtype, supertype)
if not is_compat:
Expand Down Expand Up @@ -1280,14 +1277,8 @@ def f(self) -> A: ...
if IS_CLASS_OR_STATIC in superflags and IS_CLASS_OR_STATIC not in subflags:
return False

if not proper_subtype:
# Nominal check currently ignores arg names, but __call__ is special for protocols
ignore_names = right.type.protocol_members != ["__call__"]
else:
ignore_names = False
subtype_kind = SubtypeVisitor.build_subtype_kind(
subtype_context=SubtypeContext(ignore_pos_arg_names=ignore_names),
proper_subtype=proper_subtype,
subtype_context=SubtypeContext(), proper_subtype=proper_subtype
)
type_state.record_subtype_cache_entry(subtype_kind, left, right)
return True
Expand Down
82 changes: 63 additions & 19 deletions test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -2640,8 +2640,13 @@ b: Bad

func(a)
func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" \
# N: Following member(s) of "Bad" have conflicts: \
# N: Expected: \
# N: def __call__(self, x: str) -> None \
# N: Got: \
# N: def __call__(self, zzz: str) -> None \
# N: "One.__call__" has type "def __call__(self, x: str) -> None"
[out]


[case testJoinProtocolCallback]
from typing import Protocol, Callable
Expand Down Expand Up @@ -3391,7 +3396,7 @@ test(D) # OK
from typing import Any, Protocol

class P(Protocol):
def foo(self, obj: Any) -> int: ...
def foo(self, obj: Any, /) -> int: ...

class B:
def foo(self) -> int: ...
Expand All @@ -3403,15 +3408,15 @@ test(B) # OK
test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def foo(obj: Any) -> int \
# N: def foo(Any, /) -> int \
# N: Got: \
# N: def foo(self: C) -> str

[case testProtocolClassObjectInstanceMethodArg]
from typing import Any, Protocol

class P(Protocol):
def foo(self, obj: B) -> int: ...
def foo(self, obj: B, /) -> int: ...

class B:
def foo(self) -> int: ...
Expand All @@ -3423,7 +3428,7 @@ test(B) # OK
test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def foo(obj: B) -> int \
# N: def foo(B, /) -> int \
# N: Got: \
# N: def foo(self: C) -> int

Expand All @@ -3432,9 +3437,9 @@ from typing import Any, Protocol, overload

class P(Protocol):
@overload
def foo(self, obj: Any, arg: int) -> int: ...
def foo(self, obj: Any, /, arg: int) -> int: ...
@overload
def foo(self, obj: Any, arg: str) -> str: ...
def foo(self, obj: Any, /, arg: str) -> str: ...

class B:
@overload
Expand All @@ -3458,9 +3463,9 @@ test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P"
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: @overload \
# N: def foo(obj: Any, arg: int) -> int \
# N: def foo(Any, /, arg: int) -> int \
# N: @overload \
# N: def foo(obj: Any, arg: str) -> str \
# N: def foo(Any, /, arg: str) -> str \
# N: Got: \
# N: @overload \
# N: def foo(self: C, arg: int) -> int \
Expand Down Expand Up @@ -3517,7 +3522,7 @@ test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P"
from typing import Any, Protocol, Generic, List, TypeVar

class P(Protocol):
def foo(self, obj: Any) -> List[int]: ...
def foo(self, obj: Any, /) -> List[int]: ...

T = TypeVar("T")
class A(Generic[T]):
Expand All @@ -3532,7 +3537,7 @@ test(B) # OK
test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def foo(obj: Any) -> list[int] \
# N: def foo(Any, /) -> list[int] \
# N: Got: \
# N: def foo(self: A[list[str]]) -> list[str]
[builtins fixtures/list.pyi]
Expand Down Expand Up @@ -3567,7 +3572,7 @@ from typing import Protocol, TypeVar, Union

T = TypeVar("T")
class P(Protocol):
def foo(self, arg: T) -> T: ...
def foo(self, arg: T, /) -> T: ...

class B:
def foo(self: T) -> T: ...
Expand All @@ -3579,7 +3584,7 @@ test(B) # OK
test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def [T] foo(arg: T) -> T \
# N: def [T] foo(T, /) -> T \
# N: Got: \
# N: def [T] foo(self: T) -> T | int

Expand Down Expand Up @@ -3711,7 +3716,7 @@ test(d) # E: Argument 1 to "test" has incompatible type "type[D]"; expected "P"
from typing import Any, Protocol, Type

class P(Protocol):
def foo(self, cls: Any) -> int: ...
def foo(self, cls: Any, /) -> int: ...

class B:
def foo(self) -> int: ...
Expand All @@ -3725,7 +3730,7 @@ test(b) # OK
test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def foo(cls: Any) -> int \
# N: def foo(Any, /) -> int \
# N: Got: \
# N: def foo(self: C) -> str

Expand Down Expand Up @@ -3759,7 +3764,7 @@ from typing import Protocol, Type, TypeVar, Union

T = TypeVar("T")
class P(Protocol):
def foo(self, arg: T) -> T: ...
def foo(self, arg: T, /) -> T: ...

class B:
def foo(self: T) -> T: ...
Expand All @@ -3773,7 +3778,7 @@ test(b) # OK
test(c) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" \
# N: Following member(s) of "C" have conflicts: \
# N: Expected: \
# N: def [T] foo(arg: T) -> T \
# N: def [T] foo(T, /) -> T \
# N: Got: \
# N: def [T] foo(self: T) -> T | int

Expand All @@ -3782,7 +3787,7 @@ from typing import Any, Protocol, TypeVar

T = TypeVar("T", contravariant=True)
class P(Protocol[T]):
def foo(self, obj: T) -> int: ...
def foo(self, obj: T, /) -> int: ...

class B:
def foo(self) -> int: ...
Expand All @@ -3796,7 +3801,7 @@ from typing import Any, Protocol, TypeVar, Type

T = TypeVar("T", contravariant=True)
class P(Protocol[T]):
def foo(self, obj: T) -> int: ...
def foo(self, obj: T, /) -> int: ...

class B:
def foo(self) -> int: ...
Expand All @@ -3806,6 +3811,7 @@ def test(arg: P[S]) -> S: ...
b: Type[B]
reveal_type(test(b)) # N: Revealed type is "__main__.B"


[case testTypeAliasInProtocolBody]
from typing import Protocol, List

Expand Down Expand Up @@ -4746,3 +4752,41 @@ tmp/a.py:8: note: Expected:
tmp/a.py:8: note: def f(self) -> PNested
tmp/a.py:8: note: Got:
tmp/a.py:8: note: def f(self) -> CNested

[case testProtocolArgNames]
from typing import Protocol

class P1(Protocol):
def foo(self, a: int) -> None: ...

class C1:
def foo(self, b: int) -> None: pass

x1: P1 = C1() # E: Incompatible types in assignment (expression has type "C1", variable has type "P1") \
# N: Following member(s) of "C1" have conflicts: \
# N: Expected: \
# N: def foo(self, a: int) -> None \
# N: Got: \
# N: def foo(self, b: int) -> None

class P2(Protocol):
def foo(self, a: int) -> None: ...

class C2:
def foo(self, *args: int) -> None: pass

x2: P2 = C2() # E: Incompatible types in assignment (expression has type "C2", variable has type "P2") \
# N: Following member(s) of "C2" have conflicts: \
# N: Expected: \
# N: def foo(self, a: int) -> None \
# N: Got: \
# N: def foo(self, *args: int) -> None

class P3(Protocol):
def foo(self, a: int, /) -> None: ...

class C3:
def foo(self, *args: int) -> None: pass

okay3: P3 = C3()
[builtins fixtures/tuple.pyi]
Loading