diff --git a/mypy/messages.py b/mypy/messages.py index 51bb0b7ee9be..4bde8e4686b8 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -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) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 66d7a95eb425..9bf284e1e2b3 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -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) @@ -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) else: is_compat = is_proper_subtype(subtype, supertype) if not is_compat: @@ -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 diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index a51445ef0b5b..21548f6b2f8b 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -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 @@ -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: ... @@ -3403,7 +3408,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) -> int \ + # N: def foo(Any, /) -> int \ # N: Got: \ # N: def foo(self: C) -> str @@ -3411,7 +3416,7 @@ test(C) # E: Argument 1 to "test" has incompatible type "type[C]"; expected "P" 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: ... @@ -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 @@ -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 @@ -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 \ @@ -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]): @@ -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] @@ -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: ... @@ -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 @@ -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: ... @@ -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 @@ -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: ... @@ -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 @@ -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: ... @@ -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: ... @@ -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 @@ -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]