| -- Simple protocol types |
| -- --------------------- |
| |
| [case testCannotInstantiateProtocol] |
| from typing import Protocol |
| |
| class P(Protocol): |
| def meth(self) -> None: |
| pass |
| |
| P() # E: Cannot instantiate protocol class "P" |
| |
| [case testSimpleProtocolOneMethod] |
| from typing import Protocol |
| |
| class P(Protocol): |
| def meth(self) -> None: |
| pass |
| |
| class B: pass |
| class C: |
| def meth(self) -> None: |
| pass |
| |
| x: P |
| def fun(x: P) -> None: |
| x.meth() |
| x.meth(x) # E: Too many arguments for "meth" of "P" |
| x.bad # E: "P" has no attribute "bad" |
| |
| x = C() |
| x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") |
| |
| fun(C()) |
| fun(B()) # E: Argument 1 to "fun" has incompatible type "B"; expected "P" |
| |
| def fun2() -> P: |
| return C() |
| def fun3() -> P: |
| return B() # E: Incompatible return value type (got "B", expected "P") |
| |
| [case testProtocolAttrAccessDecoratedGetAttrDunder] |
| from typing import Any, Protocol, Callable |
| |
| def typed_decorator(fun: Callable) -> Callable[[Any, str], str]: |
| pass |
| |
| def untyped_decorator(fun): |
| pass |
| |
| class P(Protocol): |
| @property |
| def x(self) -> int: |
| pass |
| |
| class A: |
| @untyped_decorator |
| def __getattr__(self, key: str) -> int: |
| pass |
| |
| class B: |
| @typed_decorator |
| def __getattr__(self, key: str) -> int: |
| pass |
| |
| class C: |
| def __getattr__(self, key: str) -> int: |
| pass |
| |
| def fun(x: P) -> None: |
| pass |
| |
| a: A |
| reveal_type(a.x) |
| fun(a) |
| |
| b: B |
| reveal_type(b.x) |
| fun(b) |
| |
| c: C |
| reveal_type(c.x) |
| fun(c) |
| [out] |
| main:32: note: Revealed type is "Any" |
| main:36: note: Revealed type is "builtins.str" |
| main:37: error: Argument 1 to "fun" has incompatible type "B"; expected "P" |
| main:37: note: Following member(s) of "B" have conflicts: |
| main:37: note: x: expected "int", got "str" |
| main:40: note: Revealed type is "builtins.int" |
| [builtins fixtures/bool.pyi] |
| |
| [case testSimpleProtocolOneAbstractMethod] |
| from typing import Protocol |
| from abc import abstractmethod |
| |
| class P(Protocol): |
| @abstractmethod |
| def meth(self) -> None: |
| pass |
| |
| class B: pass |
| class C: |
| def meth(self) -> None: |
| pass |
| class D(B): |
| def meth(self) -> None: |
| pass |
| |
| x: P |
| def fun(x: P) -> None: |
| x.meth() |
| x.meth(x) # E: Too many arguments for "meth" of "P" |
| x.bad # E: "P" has no attribute "bad" |
| |
| x = C() |
| x = D() |
| x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") |
| fun(C()) |
| fun(D()) |
| fun(B()) # E: Argument 1 to "fun" has incompatible type "B"; expected "P" |
| fun(x) |
| |
| [case testProtocolMethodBodies] |
| from typing import Protocol, List |
| |
| class P(Protocol): |
| def meth(self) -> int: |
| return 'no way' # E: Incompatible return value type (got "str", expected "int") |
| |
| # explicit ellipsis is OK in protocol methods |
| class P2(Protocol): |
| def meth2(self) -> List[int]: |
| ... |
| [builtins fixtures/list.pyi] |
| |
| [case testSimpleProtocolOneMethodOverride] |
| from typing import Protocol, Union |
| |
| class P(Protocol): |
| def meth(self) -> Union[int, str]: |
| pass |
| class SubP(P, Protocol): |
| def meth(self) -> int: |
| pass |
| |
| class B: pass |
| class C: |
| def meth(self) -> int: |
| pass |
| z: P |
| x: SubP |
| def fun(x: SubP) -> str: |
| x.bad # E: "SubP" has no attribute "bad" |
| return x.meth() # E: Incompatible return value type (got "int", expected "str") |
| |
| z = x |
| x = C() |
| x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "SubP") |
| |
| reveal_type(fun(C())) # N: Revealed type is "builtins.str" |
| fun(B()) # E: Argument 1 to "fun" has incompatible type "B"; expected "SubP" |
| |
| [case testSimpleProtocolTwoMethodsMerge] |
| from typing import Protocol |
| |
| class P1(Protocol): |
| def meth1(self) -> int: |
| pass |
| class P2(Protocol): |
| def meth2(self) -> str: |
| pass |
| class P(P1, P2, Protocol): pass |
| |
| class B: pass |
| class C1: |
| def meth1(self) -> int: |
| pass |
| class C2(C1): |
| def meth2(self) -> str: |
| pass |
| class C: |
| def meth1(self) -> int: |
| pass |
| def meth2(self) -> str: |
| pass |
| |
| class AnotherP(Protocol): |
| def meth1(self) -> int: |
| pass |
| def meth2(self) -> str: |
| pass |
| |
| x: P |
| reveal_type(x.meth1()) # N: Revealed type is "builtins.int" |
| reveal_type(x.meth2()) # N: Revealed type is "builtins.str" |
| |
| c: C |
| c1: C1 |
| c2: C2 |
| y: AnotherP |
| |
| if int(): |
| x = c |
| if int(): |
| x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") |
| if int(): |
| x = c1 # E: Incompatible types in assignment (expression has type "C1", variable has type "P") \ |
| # N: "C1" is missing following "P" protocol member: \ |
| # N: meth2 |
| if int(): |
| x = c2 |
| if int(): |
| x = y |
| if int(): |
| y = x |
| |
| [case testSimpleProtocolTwoMethodsExtend] |
| from typing import Protocol |
| |
| class P1(Protocol): |
| def meth1(self) -> int: |
| pass |
| class P2(P1, Protocol): |
| def meth2(self) -> str: |
| pass |
| |
| class Cbad: |
| def meth1(self) -> int: |
| pass |
| |
| class C: |
| def meth1(self) -> int: |
| pass |
| def meth2(self) -> str: |
| pass |
| |
| x: P2 |
| reveal_type(x.meth1()) # N: Revealed type is "builtins.int" |
| reveal_type(x.meth2()) # N: Revealed type is "builtins.str" |
| |
| if int(): |
| x = C() # OK |
| if int(): |
| x = Cbad() # E: Incompatible types in assignment (expression has type "Cbad", variable has type "P2") \ |
| # N: "Cbad" is missing following "P2" protocol member: \ |
| # N: meth2 |
| |
| [case testProtocolMethodVsAttributeErrors] |
| from typing import Protocol |
| |
| class P(Protocol): |
| def meth(self) -> int: |
| pass |
| class C: |
| meth: int |
| x: P = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P") \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: meth: expected "Callable[[], int]", got "int" |
| |
| [case testProtocolMethodVsAttributeErrors2] |
| from typing import Protocol |
| |
| class P(Protocol): |
| @property |
| def meth(self) -> int: |
| pass |
| class C: |
| def meth(self) -> int: |
| pass |
| x: P = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P") \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: meth: expected "int", got "Callable[[], int]" |
| [builtins fixtures/property.pyi] |
| |
| [case testCannotAssignNormalToProtocol] |
| from typing import Protocol |
| |
| class P(Protocol): |
| def meth(self) -> int: |
| pass |
| class C: |
| def meth(self) -> int: |
| pass |
| |
| x: C |
| y: P |
| x = y # E: Incompatible types in assignment (expression has type "P", variable has type "C") |
| |
| [case testIndependentProtocolSubtyping] |
| from typing import Protocol |
| |
| class P1(Protocol): |
| def meth(self) -> int: |
| pass |
| class P2(Protocol): |
| def meth(self) -> int: |
| pass |
| |
| x1: P1 |
| x2: P2 |
| |
| x1 = x2 |
| x2 = x1 |
| |
| def f1(x: P1) -> None: pass |
| def f2(x: P2) -> None: pass |
| |
| f1(x2) |
| f2(x1) |
| |
| [case testNoneDisablesProtocolImplementation] |
| from typing import Protocol |
| |
| class MyHashable(Protocol): |
| def __my_hash__(self) -> int: |
| return 0 |
| |
| class C: |
| __my_hash__ = None |
| |
| var: MyHashable = C() # E: Incompatible types in assignment (expression has type "C", variable has type "MyHashable") |
| |
| [case testNoneDisablesProtocolSubclassingWithStrictOptional] |
| # flags: --strict-optional |
| from typing import Protocol |
| |
| class MyHashable(Protocol): |
| def __my_hash__(self) -> int: |
| return 0 |
| |
| class C(MyHashable): |
| __my_hash__ = None # E: Incompatible types in assignment \ |
| (expression has type "None", base class "MyHashable" defined the type as "Callable[[MyHashable], int]") |
| |
| [case testProtocolsWithNoneAndStrictOptional] |
| # flags: --strict-optional |
| from typing import Protocol |
| class P(Protocol): |
| x = 0 # type: int |
| |
| class C: |
| x = None |
| |
| x: P = C() # Error! |
| def f(x: P) -> None: pass |
| f(C()) # Error! |
| [out] |
| main:9: error: Incompatible types in assignment (expression has type "C", variable has type "P") |
| main:9: note: Following member(s) of "C" have conflicts: |
| main:9: note: x: expected "int", got "None" |
| main:11: error: Argument 1 to "f" has incompatible type "C"; expected "P" |
| main:11: note: Following member(s) of "C" have conflicts: |
| main:11: note: x: expected "int", got "None" |
| |
| -- Semanal errors in protocol types |
| -- -------------------------------- |
| |
| [case testBasicSemanalErrorsInProtocols] |
| |
| from typing import Protocol, Generic, TypeVar, Iterable |
| |
| T = TypeVar('T', covariant=True) |
| S = TypeVar('S', covariant=True) |
| |
| class P1(Protocol[T, T]): # E: Duplicate type variables in Generic[...] or Protocol[...] |
| def meth(self) -> T: |
| pass |
| |
| class P2(Protocol[T], Protocol[S]): # E: Only single Generic[...] or Protocol[...] can be in bases |
| def meth(self) -> T: |
| pass |
| |
| class P3(Protocol[T], Generic[S]): # E: Only single Generic[...] or Protocol[...] can be in bases |
| def meth(self) -> T: |
| pass |
| |
| class P4(Protocol[T]): |
| attr: Iterable[S] # E: Type variable "__main__.S" is unbound \ |
| # N: (Hint: Use "Generic[S]" or "Protocol[S]" base class to bind "S" inside a class) \ |
| # N: (Hint: Use "S" in function signature to bind "S" inside a function) |
| |
| class P5(Iterable[S], Protocol[T]): # E: If Generic[...] or Protocol[...] is present it should list all type variables |
| def meth(self) -> T: |
| pass |
| |
| [case testProhibitSelfDefinitionInProtocols] |
| from typing import Protocol |
| |
| class P(Protocol): |
| def __init__(self, a: int) -> None: |
| self.a = a # E: Protocol members cannot be defined via assignment to self \ |
| # E: "P" has no attribute "a" |
| |
| class B: pass |
| class C: |
| def __init__(self, a: int) -> None: |
| pass |
| |
| x: P |
| x = B() |
| # The above has an incompatible __init__, but mypy ignores this for nominal subtypes? |
| x = C(1) |
| |
| class P2(Protocol): |
| a: int |
| def __init__(self) -> None: |
| self.a = 1 |
| |
| class B2(P2): |
| a: int |
| |
| x2: P2 = B2() # OK |
| |
| [case testProtocolAndRuntimeAreDefinedAlsoInTypingExtensions] |
| from typing_extensions import Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class P(Protocol): |
| def meth(self) -> int: |
| pass |
| |
| x: object |
| if isinstance(x, P): |
| reveal_type(x) # N: Revealed type is "__main__.P" |
| reveal_type(x.meth()) # N: Revealed type is "builtins.int" |
| |
| class C: |
| def meth(self) -> int: |
| pass |
| |
| z: P = C() |
| [builtins fixtures/dict.pyi] |
| |
| [case testProtocolsCannotInheritFromNormal] |
| from typing import Protocol |
| |
| class C: pass |
| class D: pass |
| |
| class P(C, Protocol): # E: All bases of a protocol must be protocols |
| attr: int |
| |
| class P2(P, D, Protocol): # E: All bases of a protocol must be protocols |
| pass |
| |
| P2() # E: Cannot instantiate abstract class "P2" with abstract attribute "attr" |
| p: P2 |
| reveal_type(p.attr) # N: Revealed type is "builtins.int" |
| |
| -- Generic protocol types |
| -- ---------------------- |
| |
| [case testGenericMethodWithProtocol] |
| from typing import Protocol, TypeVar |
| T = TypeVar('T') |
| |
| class P(Protocol): |
| def meth(self, x: int) -> int: |
| return x |
| class C: |
| def meth(self, x: T) -> T: |
| return x |
| |
| x: P = C() |
| |
| [case testGenericMethodWithProtocol2] |
| from typing import Protocol, TypeVar |
| T = TypeVar('T') |
| |
| class P(Protocol): |
| def meth(self, x: T) -> T: |
| return x |
| class C: |
| def meth(self, x: int) -> int: |
| return x |
| |
| x: P = C() |
| [out] |
| main:11: error: Incompatible types in assignment (expression has type "C", variable has type "P") |
| main:11: note: Following member(s) of "C" have conflicts: |
| main:11: note: Expected: |
| main:11: note: def [T] meth(self, x: T) -> T |
| main:11: note: Got: |
| main:11: note: def meth(self, x: int) -> int |
| |
| [case testAutomaticProtocolVariance] |
| from typing import TypeVar, Protocol |
| |
| T = TypeVar('T') |
| |
| # In case of these errors we proceed with declared variance. |
| class Pco(Protocol[T]): # E: Invariant type variable "T" used in protocol where covariant one is expected |
| def meth(self) -> T: |
| pass |
| class Pcontra(Protocol[T]): # E: Invariant type variable "T" used in protocol where contravariant one is expected |
| def meth(self, x: T) -> None: |
| pass |
| class Pinv(Protocol[T]): |
| attr: T |
| |
| class A: pass |
| class B(A): pass |
| |
| x1: Pco[B] |
| y1: Pco[A] |
| if int(): |
| x1 = y1 # E: Incompatible types in assignment (expression has type "Pco[A]", variable has type "Pco[B]") |
| if int(): |
| y1 = x1 # E: Incompatible types in assignment (expression has type "Pco[B]", variable has type "Pco[A]") |
| |
| x2: Pcontra[B] |
| y2: Pcontra[A] |
| if int(): |
| y2 = x2 # E: Incompatible types in assignment (expression has type "Pcontra[B]", variable has type "Pcontra[A]") |
| if int(): |
| x2 = y2 # E: Incompatible types in assignment (expression has type "Pcontra[A]", variable has type "Pcontra[B]") |
| |
| x3: Pinv[B] |
| y3: Pinv[A] |
| if int(): |
| y3 = x3 # E: Incompatible types in assignment (expression has type "Pinv[B]", variable has type "Pinv[A]") |
| if int(): |
| x3 = y3 # E: Incompatible types in assignment (expression has type "Pinv[A]", variable has type "Pinv[B]") |
| |
| [case testProtocolVarianceWithCallableAndList] |
| from typing import Protocol, TypeVar, Callable, List |
| T = TypeVar('T') |
| S = TypeVar('S') |
| T_co = TypeVar('T_co', covariant=True) |
| |
| class P(Protocol[T, S]): # E: Invariant type variable "T" used in protocol where covariant one is expected \ |
| # E: Invariant type variable "S" used in protocol where contravariant one is expected |
| def fun(self, callback: Callable[[T], S]) -> None: pass |
| |
| class P2(Protocol[T_co]): # E: Covariant type variable "T_co" used in protocol where invariant one is expected |
| lst: List[T_co] |
| [builtins fixtures/list.pyi] |
| |
| |
| [case testProtocolConstraintsUnsolvableWithSelfAnnotation1] |
| # https://github.com/python/mypy/issues/11020 |
| from typing import overload, Protocol, TypeVar |
| |
| I = TypeVar('I', covariant=True) |
| V_contra = TypeVar('V_contra', contravariant=True) |
| |
| class C(Protocol[I]): |
| def __abs__(self: 'C[V_contra]') -> 'C[V_contra]': |
| ... |
| |
| @overload |
| def f(self: 'C', q: int) -> int: |
| ... |
| @overload |
| def f(self: 'C[float]', q: float) -> 'C[float]': |
| ... |
| [builtins fixtures/bool.pyi] |
| |
| |
| [case testProtocolConstraintsUnsolvableWithSelfAnnotation2] |
| # https://github.com/python/mypy/issues/11020 |
| from typing import Protocol, TypeVar |
| |
| I = TypeVar('I', covariant=True) |
| V = TypeVar('V') |
| |
| class C(Protocol[I]): |
| def g(self: 'C[V]') -> 'C[V]': |
| ... |
| |
| class D: |
| pass |
| |
| x: C = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C[Any]") |
| [builtins fixtures/bool.pyi] |
| |
| |
| [case testProtocolConstraintsUnsolvableWithSelfAnnotation3] |
| # https://github.com/python/mypy/issues/11020 |
| from typing import Protocol, TypeVar |
| |
| I = TypeVar('I', covariant=True) |
| V = TypeVar('V') |
| |
| class C(Protocol[I]): |
| def g(self: 'C[V]') -> 'C[V]': |
| ... |
| |
| class D: |
| def g(self) -> D: |
| ... |
| |
| x: C = D() |
| [builtins fixtures/bool.pyi] |
| |
| |
| [case testProtocolVarianceWithUnusedVariable] |
| from typing import Protocol, TypeVar |
| T = TypeVar('T') |
| |
| class P(Protocol[T]): # E: Invariant type variable "T" used in protocol where covariant one is expected |
| attr: int |
| |
| [case testGenericProtocolsInference1] |
| from typing import Protocol, Sequence, TypeVar |
| |
| T = TypeVar('T', covariant=True) |
| |
| class Closeable(Protocol[T]): |
| def close(self) -> T: |
| pass |
| |
| class F: |
| def close(self) -> int: |
| return 0 |
| |
| def close(arg: Closeable[T]) -> T: |
| return arg.close() |
| |
| def close_all(args: Sequence[Closeable[T]]) -> T: |
| for arg in args: |
| arg.close() |
| return args[0].close() |
| |
| arg: Closeable[int] |
| |
| reveal_type(close(F())) # N: Revealed type is "builtins.int" |
| reveal_type(close(arg)) # N: Revealed type is "builtins.int" |
| reveal_type(close_all([F()])) # N: Revealed type is "builtins.int" |
| reveal_type(close_all([arg])) # N: Revealed type is "builtins.int" |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testProtocolGenericInference2] |
| from typing import Generic, TypeVar, Protocol |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class P(Protocol[T, S]): |
| x: T |
| y: S |
| |
| class C: |
| x: int |
| y: int |
| |
| def fun3(x: P[T, T]) -> T: |
| pass |
| reveal_type(fun3(C())) # N: Revealed type is "builtins.int" |
| |
| [case testProtocolGenericInferenceCovariant] |
| from typing import Generic, TypeVar, Protocol |
| T = TypeVar('T', covariant=True) |
| S = TypeVar('S', covariant=True) |
| U = TypeVar('U') |
| |
| class P(Protocol[T, S]): |
| def x(self) -> T: pass |
| def y(self) -> S: pass |
| |
| class C: |
| def x(self) -> int: pass |
| def y(self) -> int: pass |
| |
| def fun4(x: U, y: P[U, U]) -> U: |
| pass |
| reveal_type(fun4('a', C())) # N: Revealed type is "builtins.object" |
| |
| [case testUnrealtedGenericProtolsEquivalent] |
| from typing import TypeVar, Protocol |
| T = TypeVar('T') |
| |
| class PA(Protocol[T]): |
| attr: int |
| def meth(self) -> T: pass |
| def other(self, arg: T) -> None: pass |
| class PB(Protocol[T]): # exactly the same as above |
| attr: int |
| def meth(self) -> T: pass |
| def other(self, arg: T) -> None: pass |
| |
| def fun(x: PA[T]) -> PA[T]: |
| y: PB[T] = x |
| z: PB[T] |
| return z |
| |
| x: PA |
| y: PB |
| x = y |
| y = x |
| |
| xi: PA[int] |
| yi: PB[int] |
| xi = yi |
| yi = xi |
| |
| [case testGenericSubProtocols] |
| from typing import TypeVar, Protocol, Tuple, Generic |
| |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class P1(Protocol[T]): |
| attr1: T |
| class P2(P1[T], Protocol[T, S]): |
| attr2: Tuple[T, S] |
| |
| class C: |
| def __init__(self, a1: int, a2: Tuple[int, int]) -> None: |
| self.attr1 = a1 |
| self.attr2 = a2 |
| |
| c: C |
| var: P2[int, int] = c |
| var2: P2[int, str] = c # E: Incompatible types in assignment (expression has type "C", variable has type "P2[int, str]") \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: attr2: expected "Tuple[int, str]", got "Tuple[int, int]" |
| |
| class D(Generic[T]): |
| attr1: T |
| class E(D[T]): |
| attr2: Tuple[T, T] |
| |
| def f(x: T) -> T: |
| z: P2[T, T] = E[T]() |
| y: P2[T, T] = D[T]() # E: Incompatible types in assignment (expression has type "D[T]", variable has type "P2[T, T]") \ |
| # N: "D" is missing following "P2" protocol member: \ |
| # N: attr2 |
| return x |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testGenericSubProtocolsExtensionInvariant] |
| from typing import TypeVar, Protocol, Union |
| |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class P1(Protocol[T]): |
| attr1: T |
| class P2(Protocol[T]): |
| attr2: T |
| class P(P1[T], P2[S], Protocol): |
| pass |
| |
| class C: |
| attr1: int |
| attr2: str |
| |
| class A: |
| attr1: A |
| class B: |
| attr2: B |
| class D(A, B): pass |
| |
| x: P = D() # Same as P[Any, Any] |
| |
| var: P[Union[int, P], Union[P, str]] = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P[Union[int, P[Any, Any]], Union[P[Any, Any], str]]") \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: attr1: expected "Union[int, P[Any, Any]]", got "int" \ |
| # N: attr2: expected "Union[P[Any, Any], str]", got "str" |
| |
| [case testGenericSubProtocolsExtensionCovariant] |
| from typing import TypeVar, Protocol, Union |
| |
| T = TypeVar('T', covariant=True) |
| S = TypeVar('S', covariant=True) |
| |
| class P1(Protocol[T]): |
| def attr1(self) -> T: pass |
| class P2(Protocol[T]): |
| def attr2(self) -> T: pass |
| class P(P1[T], P2[S], Protocol): |
| pass |
| |
| class C: |
| def attr1(self) -> int: pass |
| def attr2(self) -> str: pass |
| |
| var: P[Union[int, P], Union[P, str]] = C() # OK for covariant |
| var2: P[Union[str, P], Union[P, int]] = C() |
| [out] |
| main:18: error: Incompatible types in assignment (expression has type "C", variable has type "P[Union[str, P[Any, Any]], Union[P[Any, Any], int]]") |
| main:18: note: Following member(s) of "C" have conflicts: |
| main:18: note: Expected: |
| main:18: note: def attr1(self) -> Union[str, P[Any, Any]] |
| main:18: note: Got: |
| main:18: note: def attr1(self) -> int |
| main:18: note: Expected: |
| main:18: note: def attr2(self) -> Union[P[Any, Any], int] |
| main:18: note: Got: |
| main:18: note: def attr2(self) -> str |
| |
| [case testSelfTypesWithProtocolsBehaveAsWithNominal] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T', bound=Shape) |
| class Shape(Protocol): |
| def combine(self: T, other: T) -> T: |
| pass |
| |
| class NonProtoShape: |
| def combine(self: T, other: T) -> T: |
| pass |
| class Circle: |
| def combine(self: T, other: Shape) -> T: |
| pass |
| class Triangle: |
| def combine(self, other: Shape) -> Shape: |
| pass |
| class Bad: |
| def combine(self, other: int) -> str: |
| pass |
| |
| def f(s: Shape) -> None: pass |
| f(NonProtoShape()) |
| f(Circle()) |
| s: Shape |
| if int(): |
| s = Triangle() |
| s = Bad() |
| |
| n2: NonProtoShape = s |
| [out] |
| main:26: error: Incompatible types in assignment (expression has type "Triangle", variable has type "Shape") |
| main:26: note: Following member(s) of "Triangle" have conflicts: |
| main:26: note: Expected: |
| main:26: note: def combine(self, other: Triangle) -> Triangle |
| main:26: note: Got: |
| main:26: note: def combine(self, other: Shape) -> Shape |
| main:27: error: Incompatible types in assignment (expression has type "Bad", variable has type "Shape") |
| main:27: note: Following member(s) of "Bad" have conflicts: |
| main:27: note: Expected: |
| main:27: note: def combine(self, other: Bad) -> Bad |
| main:27: note: Got: |
| main:27: note: def combine(self, other: int) -> str |
| main:29: error: Incompatible types in assignment (expression has type "Shape", variable has type "NonProtoShape") |
| |
| [case testBadVarianceInProtocols] |
| from typing import Protocol, TypeVar |
| |
| T_co = TypeVar('T_co', covariant=True) |
| T_contra = TypeVar('T_contra', contravariant=True) |
| |
| class Proto(Protocol[T_co, T_contra]): # type: ignore |
| def one(self, x: T_co) -> None: # E: Cannot use a covariant type variable as a parameter |
| pass |
| def other(self) -> T_contra: # E: Cannot use a contravariant type variable as return type |
| pass |
| |
| # Check that we respect user overrides of variance after the errors are reported |
| x: Proto[int, float] |
| y: Proto[float, int] |
| y = x # OK |
| [builtins fixtures/list.pyi] |
| |
| [case testSubtleBadVarianceInProtocols] |
| from typing import Protocol, TypeVar, Iterable, Sequence |
| |
| T_co = TypeVar('T_co', covariant=True) |
| T_contra = TypeVar('T_contra', contravariant=True) |
| |
| class Proto(Protocol[T_co, T_contra]): # E: Covariant type variable "T_co" used in protocol where contravariant one is expected \ |
| # E: Contravariant type variable "T_contra" used in protocol where covariant one is expected |
| def one(self, x: Iterable[T_co]) -> None: |
| pass |
| def other(self) -> Sequence[T_contra]: |
| pass |
| |
| # Check that we respect user overrides of variance after the errors are reported |
| x: Proto[int, float] |
| y: Proto[float, int] |
| y = x # OK |
| [builtins fixtures/list.pyi] |
| |
| -- Recursive protocol types |
| -- ------------------------ |
| |
| [case testRecursiveProtocols1] |
| from typing import Protocol, Sequence, List, Generic, TypeVar |
| |
| T = TypeVar('T') |
| |
| class Traversable(Protocol): |
| @property |
| def leaves(self) -> Sequence[Traversable]: pass |
| |
| class C: pass |
| |
| class D(Generic[T]): |
| leaves: List[D[T]] |
| |
| t: Traversable |
| t = D[int]() # OK |
| if int(): |
| t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "Traversable") |
| [builtins fixtures/list.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testRecursiveProtocols2] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| class Linked(Protocol[T]): |
| val: T |
| def next(self) -> Linked[T]: pass |
| |
| class L: |
| val: int |
| def next(self) -> L: pass |
| |
| def last(seq: Linked[T]) -> T: |
| pass |
| |
| reveal_type(last(L())) # N: Revealed type is "builtins.int" |
| [builtins fixtures/list.pyi] |
| |
| [case testRecursiveProtocolSubtleMismatch] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| class Linked(Protocol[T]): |
| val: T |
| def next(self) -> Linked[T]: pass |
| class L: |
| val: int |
| def next(self) -> int: pass |
| |
| def last(seq: Linked[T]) -> T: |
| pass |
| last(L()) # E: Argument 1 to "last" has incompatible type "L"; expected "Linked[<nothing>]" |
| |
| [case testMutuallyRecursiveProtocols] |
| from typing import Protocol, Sequence, List |
| |
| class P1(Protocol): |
| @property |
| def attr1(self) -> Sequence[P2]: pass |
| class P2(Protocol): |
| @property |
| def attr2(self) -> Sequence[P1]: pass |
| |
| class C: pass |
| class A: |
| attr1: List[B] |
| class B: |
| attr2: List[A] |
| |
| t: P1 |
| t = A() # OK |
| if int(): |
| t = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P1") |
| t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P1") |
| [builtins fixtures/list.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testMutuallyRecursiveProtocolsTypesWithSubteMismatch] |
| from typing import Protocol, Sequence, List |
| |
| class P1(Protocol): |
| @property |
| def attr1(self) -> Sequence[P2]: pass |
| class P2(Protocol): |
| @property |
| def attr2(self) -> Sequence[P1]: pass |
| |
| class C: pass |
| class A: |
| attr1: List[B] |
| class B: |
| attr2: List[C] |
| |
| t: P1 |
| t = A() # E: Incompatible types in assignment (expression has type "A", variable has type "P1") \ |
| # N: Following member(s) of "A" have conflicts: \ |
| # N: attr1: expected "Sequence[P2]", got "List[B]" |
| [builtins fixtures/list.pyi] |
| |
| [case testMutuallyRecursiveProtocolsTypesWithSubteMismatchWriteable] |
| from typing import Protocol |
| |
| class P1(Protocol): |
| @property |
| def attr1(self) -> P2: pass |
| class P2(Protocol): |
| attr2: P1 |
| |
| class A: |
| attr1: B |
| class B: |
| attr2: A |
| |
| x: P1 = A() # E: Incompatible types in assignment (expression has type "A", variable has type "P1") \ |
| # N: Following member(s) of "A" have conflicts: \ |
| # N: attr1: expected "P2", got "B" |
| [builtins fixtures/property.pyi] |
| |
| [case testTwoUncomfortablyIncompatibleProtocolsWithoutRunningInIssue9771] |
| from typing import cast, Protocol, TypeVar, Union |
| |
| T1 = TypeVar("T1", covariant=True) |
| T2 = TypeVar("T2") |
| |
| class P1(Protocol[T1]): |
| def b(self) -> int: ... |
| def a(self, other: "P1[T2]") -> T1: ... |
| |
| class P2(Protocol[T1]): |
| def a(self, other: Union[P1[T2], "P2[T2]"]) -> T1: ... |
| |
| p11: P1 = cast(P1, 1) |
| p12: P1 = cast(P2, 1) # E |
| p21: P2 = cast(P1, 1) |
| p22: P2 = cast(P2, 1) # E |
| [out] |
| main:14: error: Incompatible types in assignment (expression has type "P2[Any]", variable has type "P1[Any]") |
| main:14: note: "P2" is missing following "P1" protocol member: |
| main:14: note: b |
| main:15: error: Incompatible types in assignment (expression has type "P1[Any]", variable has type "P2[Any]") |
| main:15: note: Following member(s) of "P1[Any]" have conflicts: |
| main:15: note: Expected: |
| main:15: note: def [T2] a(self, other: Union[P1[T2], P2[T2]]) -> Any |
| main:15: note: Got: |
| main:15: note: def [T2] a(self, other: P1[T2]) -> Any |
| |
| [case testHashable] |
| |
| from typing import Hashable, Iterable |
| |
| def f(x: Hashable) -> None: |
| pass |
| |
| def g(x: Iterable[str]) -> None: |
| f(x) # E: Argument 1 to "f" has incompatible type "Iterable[str]"; expected "Hashable" |
| |
| [builtins fixtures/object_hashable.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| -- FIXME: things like this should work |
| [case testWeirdRecursiveInferenceForProtocols-skip] |
| from typing import Protocol, TypeVar, Generic |
| T_co = TypeVar('T_co', covariant=True) |
| T = TypeVar('T') |
| |
| class P(Protocol[T_co]): |
| def meth(self) -> P[T_co]: pass |
| |
| class C(Generic[T]): |
| def meth(self) -> C[T]: pass |
| |
| x: C[int] |
| def f(arg: P[T]) -> T: pass |
| reveal_type(f(x)) #E: Revealed type is "builtins.int" |
| |
| -- @property, @classmethod and @staticmethod in protocol types |
| -- ----------------------------------------------------------- |
| |
| [case testCannotInstantiateAbstractMethodExplicitProtocolSubtypes] |
| from typing import Protocol |
| from abc import abstractmethod |
| |
| class P(Protocol): |
| @abstractmethod |
| def meth(self) -> int: |
| pass |
| |
| class A(P): |
| pass |
| |
| A() # E: Cannot instantiate abstract class "A" with abstract attribute "meth" |
| |
| class C(A): |
| def meth(self) -> int: |
| pass |
| class C2(P): |
| def meth(self) -> int: |
| pass |
| |
| C() |
| C2() |
| |
| [case testCannotInstantiateAbstractVariableExplicitProtocolSubtypes] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr: int |
| |
| class A(P): |
| pass |
| |
| A() # E: Cannot instantiate abstract class "A" with abstract attribute "attr" |
| |
| class C(A): |
| attr: int |
| class C2(P): |
| def __init__(self) -> None: |
| self.attr = 1 |
| |
| C() |
| C2() |
| |
| class P2(Protocol): |
| attr: int = 1 |
| |
| class B(P2): pass |
| B() # OK, attr is not abstract |
| |
| [case testClassVarsInProtocols] |
| from typing import Protocol, ClassVar |
| |
| class PInst(Protocol): |
| v: int |
| |
| class PClass(Protocol): |
| v: ClassVar[int] |
| |
| class CInst: |
| v: int |
| |
| class CClass: |
| v: ClassVar[int] |
| |
| x: PInst |
| y: PClass |
| |
| x = CInst() |
| if int(): |
| x = CClass() # E: Incompatible types in assignment (expression has type "CClass", variable has type "PInst") \ |
| # N: Protocol member PInst.v expected instance variable, got class variable |
| y = CClass() |
| if int(): |
| y = CInst() # E: Incompatible types in assignment (expression has type "CInst", variable has type "PClass") \ |
| # N: Protocol member PClass.v expected class variable, got instance variable |
| |
| [case testPropertyInProtocols] |
| from typing import Protocol |
| |
| class PP(Protocol): |
| @property |
| def attr(self) -> int: |
| pass |
| |
| class P(Protocol): |
| attr: int |
| |
| x: P |
| y: PP |
| y = x |
| |
| x2: P |
| y2: PP |
| x2 = y2 # E: Incompatible types in assignment (expression has type "PP", variable has type "P") \ |
| # N: Protocol member P.attr expected settable variable, got read-only attribute |
| [builtins fixtures/property.pyi] |
| |
| [case testSettablePropertyInProtocols] |
| from typing import Protocol |
| |
| class PPS(Protocol): |
| @property |
| def attr(self) -> int: |
| pass |
| @attr.setter |
| def attr(self, x: int) -> None: |
| pass |
| |
| class PP(Protocol): |
| @property |
| def attr(self) -> int: |
| pass |
| |
| class P(Protocol): |
| attr: int |
| |
| x: P |
| z: PPS |
| z = x |
| |
| x2: P |
| z2: PPS |
| x2 = z2 |
| |
| y3: PP |
| z3: PPS |
| y3 = z3 |
| |
| y4: PP |
| z4: PPS |
| z4 = y4 # E: Incompatible types in assignment (expression has type "PP", variable has type "PPS") \ |
| # N: Protocol member PPS.attr expected settable variable, got read-only attribute |
| [builtins fixtures/property.pyi] |
| |
| [case testStaticAndClassMethodsInProtocols] |
| from typing import Protocol, Type, TypeVar |
| |
| class P(Protocol): |
| def meth(self, x: int) -> str: |
| pass |
| |
| class PC(Protocol): |
| @classmethod |
| def meth(cls, x: int) -> str: |
| pass |
| |
| class B: |
| @staticmethod |
| def meth(x: int) -> str: |
| pass |
| |
| class C: |
| def meth(self, x: int) -> str: |
| pass |
| |
| x: P |
| x = C() |
| if int(): |
| x = B() |
| |
| y: PC |
| y = B() |
| if int(): |
| y = C() \ |
| # E: Incompatible types in assignment (expression has type "C", variable has type "PC") \ |
| # N: Protocol member PC.meth expected class or static method |
| [builtins fixtures/classmethod.pyi] |
| |
| [case testOverloadedMethodsInProtocols] |
| from typing import overload, Protocol, Union |
| |
| class P(Protocol): |
| @overload |
| def f(self, x: int) -> int: pass |
| @overload |
| def f(self, x: str) -> str: pass |
| |
| class C: |
| def f(self, x: Union[int, str]) -> None: |
| pass |
| class D: |
| def f(self, x: int) -> None: |
| pass |
| |
| x: P = C() |
| if int(): |
| x = D() |
| [out] |
| main:18: error: Incompatible types in assignment (expression has type "D", variable has type "P") |
| main:18: note: Following member(s) of "D" have conflicts: |
| main:18: note: Expected: |
| main:18: note: @overload |
| main:18: note: def f(self, x: int) -> int |
| main:18: note: @overload |
| main:18: note: def f(self, x: str) -> str |
| main:18: note: Got: |
| main:18: note: def f(self, x: int) -> None |
| |
| [case testCannotInstantiateProtocolWithOverloadedUnimplementedMethod] |
| from typing import overload, Protocol |
| |
| class P(Protocol): |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: str) -> bytes: pass |
| class C(P): |
| pass |
| C() # E: Cannot instantiate abstract class "C" with abstract attribute "meth" |
| |
| [case testCanUseOverloadedImplementationsInProtocols] |
| from typing import overload, Protocol, Union |
| class P(Protocol): |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: str) -> bool: pass |
| def meth(self, x: Union[int, str]): |
| if isinstance(x, int): |
| return x |
| return True |
| |
| class C(P): |
| pass |
| x = C() |
| reveal_type(x.meth('hi')) # N: Revealed type is "builtins.bool" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testProtocolsWithIdenticalOverloads] |
| from typing import overload, Protocol |
| |
| class PA(Protocol): |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: str) -> bytes: pass |
| class PB(Protocol): # identical to above |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: str) -> bytes: pass |
| |
| x: PA |
| y: PB |
| x = y |
| def fun(arg: PB) -> None: pass |
| fun(x) |
| |
| [case testProtocolsWithIncompatibleOverloads] |
| from typing import overload, Protocol |
| |
| class PA(Protocol): |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: str) -> bytes: pass |
| class PB(Protocol): |
| @overload |
| def meth(self, x: int) -> int: pass |
| @overload |
| def meth(self, x: bytes) -> str: pass |
| |
| x: PA |
| y: PB |
| x = y |
| [out] |
| main:16: error: Incompatible types in assignment (expression has type "PB", variable has type "PA") |
| main:16: note: Following member(s) of "PB" have conflicts: |
| main:16: note: Expected: |
| main:16: note: @overload |
| main:16: note: def meth(self, x: int) -> int |
| main:16: note: @overload |
| main:16: note: def meth(self, x: str) -> bytes |
| main:16: note: Got: |
| main:16: note: @overload |
| main:16: note: def meth(self, x: int) -> int |
| main:16: note: @overload |
| main:16: note: def meth(self, x: bytes) -> str |
| |
| -- Join and meet with protocol types |
| -- --------------------------------- |
| |
| [case testJoinProtocolWithProtocol] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr: int |
| class P2(Protocol): |
| attr: int |
| attr2: str |
| |
| x: P |
| y: P2 |
| |
| l0 = [x, x] |
| l1 = [y, y] |
| l = [x, y] |
| reveal_type(l0) # N: Revealed type is "builtins.list[__main__.P]" |
| reveal_type(l1) # N: Revealed type is "builtins.list[__main__.P2]" |
| reveal_type(l) # N: Revealed type is "builtins.list[__main__.P]" |
| [builtins fixtures/list.pyi] |
| |
| [case testJoinOfIncompatibleProtocols] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr: int |
| class P2(Protocol): |
| attr2: str |
| |
| x: P |
| y: P2 |
| reveal_type([x, y]) # N: Revealed type is "builtins.list[builtins.object]" |
| [builtins fixtures/list.pyi] |
| |
| [case testJoinProtocolWithNormal] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr: int |
| |
| class C: |
| attr: int |
| |
| x: P |
| y: C |
| |
| l = [x, y] |
| |
| reveal_type(l) # N: Revealed type is "builtins.list[__main__.P]" |
| [builtins fixtures/list.pyi] |
| |
| [case testMeetProtocolWithProtocol] |
| from typing import Protocol, Callable, TypeVar |
| |
| class P(Protocol): |
| attr: int |
| class P2(Protocol): |
| attr: int |
| attr2: str |
| |
| T = TypeVar('T') |
| def f(x: Callable[[T, T], None]) -> T: pass |
| def g(x: P, y: P2) -> None: pass |
| reveal_type(f(g)) # N: Revealed type is "__main__.P2" |
| |
| [case testMeetOfIncompatibleProtocols] |
| from typing import Protocol, Callable, TypeVar |
| |
| class P(Protocol): |
| attr: int |
| class P2(Protocol): |
| attr2: str |
| |
| T = TypeVar('T') |
| def f(x: Callable[[T, T], None]) -> T: pass |
| def g(x: P, y: P2) -> None: pass |
| x = f(g) |
| reveal_type(x) # N: Revealed type is "None" |
| [case testMeetProtocolWithNormal] |
| from typing import Protocol, Callable, TypeVar |
| |
| class P(Protocol): |
| attr: int |
| class C: |
| attr: int |
| |
| T = TypeVar('T') |
| def f(x: Callable[[T, T], None]) -> T: pass |
| def g(x: P, y: C) -> None: pass |
| reveal_type(f(g)) # N: Revealed type is "__main__.C" |
| |
| [case testInferProtocolFromProtocol] |
| from typing import Protocol, Sequence, TypeVar, Generic |
| |
| T = TypeVar('T') |
| class Box(Protocol[T]): |
| content: T |
| class Linked(Protocol[T]): |
| val: T |
| def next(self) -> Linked[T]: pass |
| |
| class L(Generic[T]): |
| val: Box[T] |
| def next(self) -> L[T]: pass |
| |
| def last(seq: Linked[T]) -> T: |
| pass |
| |
| reveal_type(last(L[int]())) # N: Revealed type is "__main__.Box[builtins.int]" |
| reveal_type(last(L[str]()).content) # N: Revealed type is "builtins.str" |
| |
| [case testOverloadOnProtocol] |
| from typing import overload, Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class P1(Protocol): |
| attr1: int |
| class P2(Protocol): |
| attr2: str |
| |
| class C1: |
| attr1: int |
| class C2: |
| attr2: str |
| class C: pass |
| |
| @overload |
| def f(x: P1) -> int: ... |
| @overload |
| def f(x: P2) -> str: ... |
| def f(x): |
| if isinstance(x, P1): |
| return P1.attr1 |
| if isinstance(x, P2): # E: Only @runtime_checkable protocols can be used with instance and class checks |
| return P1.attr2 |
| |
| reveal_type(f(C1())) # N: Revealed type is "builtins.int" |
| reveal_type(f(C2())) # N: Revealed type is "builtins.str" |
| class D(C1, C2): pass # Compatible with both P1 and P2 |
| # TODO: Should this return a union instead? |
| reveal_type(f(D())) # N: Revealed type is "builtins.int" |
| f(C()) # E: No overload variant of "f" matches argument type "C" \ |
| # N: Possible overload variants: \ |
| # N: def f(x: P1) -> int \ |
| # N: def f(x: P2) -> str |
| [builtins fixtures/isinstance.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| -- Unions of protocol types |
| -- ------------------------ |
| |
| [case testBasicUnionsOfProtocols] |
| from typing import Union, Protocol |
| |
| class P1(Protocol): |
| attr1: int |
| class P2(Protocol): |
| attr2: int |
| |
| class C1: |
| attr1: int |
| class C2: |
| attr2: int |
| class C(C1, C2): |
| pass |
| |
| class B: ... |
| |
| x: Union[P1, P2] |
| |
| x = C1() |
| if int(): |
| x = C2() |
| x = C() |
| x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "Union[P1, P2]") |
| |
| [case testUnionsOfNormalClassesWithProtocols] |
| from typing import Protocol, Union |
| |
| class P1(Protocol): |
| attr1: int |
| class P2(Protocol): |
| attr2: int |
| |
| class C1: |
| attr1: int |
| class C2: |
| attr2: int |
| class C(C1, C2): |
| pass |
| |
| class D1: |
| attr1: int |
| |
| def f1(x: P1) -> None: |
| pass |
| def f2(x: P2) -> None: |
| pass |
| |
| x: Union[C1, C2] |
| y: Union[C1, D1] |
| z: Union[C, D1] |
| |
| f1(x) # E: Argument 1 to "f1" has incompatible type "Union[C1, C2]"; expected "P1" |
| f1(y) |
| f1(z) |
| f2(x) # E: Argument 1 to "f2" has incompatible type "Union[C1, C2]"; expected "P2" |
| f2(z) # E: Argument 1 to "f2" has incompatible type "Union[C, D1]"; expected "P2" |
| |
| -- Type[] with protocol types |
| -- -------------------------- |
| |
| [case testInstantiationProtocolInTypeForFunctions] |
| from typing import Type, Protocol |
| |
| class P(Protocol): |
| def m(self) -> None: pass |
| class P1(Protocol): |
| def m(self) -> None: pass |
| class Pbad(Protocol): |
| def mbad(self) -> int: pass |
| class B(P): pass |
| class C: |
| def m(self) -> None: |
| pass |
| |
| def f(cls: Type[P]) -> P: |
| return cls() # OK |
| def g() -> P: |
| return P() # E: Cannot instantiate protocol class "P" |
| |
| f(P) # E: Only concrete class can be given where "Type[P]" is expected |
| f(B) # OK |
| f(C) # OK |
| x: Type[P1] |
| xbad: Type[Pbad] |
| f(x) # OK |
| f(xbad) # E: Argument 1 to "f" has incompatible type "Type[Pbad]"; expected "Type[P]" |
| |
| [case testInstantiationProtocolInTypeForAliases] |
| from typing import Type, Protocol |
| |
| class P(Protocol): |
| def m(self) -> None: pass |
| class C: |
| def m(self) -> None: |
| pass |
| |
| def f(cls: Type[P]) -> P: |
| return cls() # OK |
| |
| Alias = P |
| GoodAlias = C |
| Alias() # E: Cannot instantiate protocol class "P" |
| GoodAlias() |
| f(Alias) # E: Only concrete class can be given where "Type[P]" is expected |
| f(GoodAlias) |
| |
| [case testInstantiationProtocolInTypeForVariables] |
| from typing import Type, Protocol |
| |
| class P(Protocol): |
| def m(self) -> None: pass |
| class B(P): pass |
| class C: |
| def m(self) -> None: |
| pass |
| |
| var: Type[P] |
| var() |
| if int(): |
| var = P # E: Can only assign concrete classes to a variable of type "Type[P]" |
| var = B # OK |
| var = C # OK |
| |
| var_old = None # type: Type[P] # Old syntax for variable annotations |
| var_old() |
| if int(): |
| var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]" |
| var_old = B # OK |
| var_old = C # OK |
| |
| [case testInstantiationProtocolInTypeForClassMethods] |
| from typing import Type, Protocol |
| |
| class Logger: |
| @staticmethod |
| def log(a: Type[C]): |
| pass |
| class C(Protocol): |
| @classmethod |
| def action(cls) -> None: |
| cls() #OK for classmethods |
| Logger.log(cls) #OK for classmethods |
| [builtins fixtures/classmethod.pyi] |
| |
| -- isinstance() with @runtime_checkable protocols |
| -- ---------------------------------------------- |
| |
| [case testSimpleRuntimeProtocolCheck] |
| from typing import Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class C: # E: @runtime_checkable can only be used with protocol classes |
| pass |
| |
| class P(Protocol): |
| def meth(self) -> None: |
| pass |
| |
| @runtime_checkable |
| class R(Protocol): |
| def meth(self) -> int: |
| pass |
| |
| x: object |
| |
| if isinstance(x, P): # E: Only @runtime_checkable protocols can be used with instance and class checks |
| reveal_type(x) # N: Revealed type is "__main__.P" |
| |
| if isinstance(x, R): |
| reveal_type(x) # N: Revealed type is "__main__.R" |
| reveal_type(x.meth()) # N: Revealed type is "builtins.int" |
| [builtins fixtures/isinstance.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testRuntimeIterableProtocolCheck] |
| from typing import Iterable, List, Union |
| |
| x: Union[int, List[str]] |
| |
| if isinstance(x, Iterable): |
| reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testConcreteClassesInProtocolsIsInstance] |
| from typing import Protocol, runtime_checkable, TypeVar, Generic |
| |
| T = TypeVar('T') |
| |
| @runtime_checkable |
| class P1(Protocol): |
| def meth1(self) -> int: |
| pass |
| @runtime_checkable |
| class P2(Protocol): |
| def meth2(self) -> int: |
| pass |
| @runtime_checkable |
| class P(P1, P2, Protocol): |
| pass |
| |
| class C1(Generic[T]): |
| def meth1(self) -> T: |
| pass |
| class C2: |
| def meth2(self) -> int: |
| pass |
| class C(C1[int], C2): pass |
| |
| c = C() |
| if isinstance(c, P1): |
| reveal_type(c) # N: Revealed type is "__main__.C" |
| else: |
| reveal_type(c) # Unreachable |
| if isinstance(c, P): |
| reveal_type(c) # N: Revealed type is "__main__.C" |
| else: |
| reveal_type(c) # Unreachable |
| |
| c1i: C1[int] |
| if isinstance(c1i, P1): |
| reveal_type(c1i) # N: Revealed type is "__main__.C1[builtins.int]" |
| else: |
| reveal_type(c1i) # Unreachable |
| if isinstance(c1i, P): |
| reveal_type(c1i) # N: Revealed type is "__main__.<subclass of "C1" and "P">" |
| else: |
| reveal_type(c1i) # N: Revealed type is "__main__.C1[builtins.int]" |
| |
| c1s: C1[str] |
| if isinstance(c1s, P1): |
| reveal_type(c1s) # Unreachable |
| else: |
| reveal_type(c1s) # N: Revealed type is "__main__.C1[builtins.str]" |
| |
| c2: C2 |
| if isinstance(c2, P): |
| reveal_type(c2) # N: Revealed type is "__main__.<subclass of "C2" and "P">" |
| else: |
| reveal_type(c2) # N: Revealed type is "__main__.C2" |
| |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testConcreteClassesUnionInProtocolsIsInstance] |
| from typing import Protocol, runtime_checkable, TypeVar, Generic, Union |
| |
| T = TypeVar('T') |
| |
| @runtime_checkable |
| class P1(Protocol): |
| def meth1(self) -> int: |
| pass |
| @runtime_checkable |
| class P2(Protocol): |
| def meth2(self) -> int: |
| pass |
| |
| class C1(Generic[T]): |
| def meth1(self) -> T: |
| pass |
| class C2: |
| def meth2(self) -> int: |
| pass |
| |
| x: Union[C1[int], C2] |
| if isinstance(x, P1): |
| reveal_type(x) # N: Revealed type is "__main__.C1[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.C2" |
| |
| if isinstance(x, P2): |
| reveal_type(x) # N: Revealed type is "__main__.C2" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.C1[builtins.int]" |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| -- Non-Instances and protocol types (Callable vs __call__ etc.) |
| -- ------------------------------------------------------------ |
| |
| [case testBasicTupleStructuralSubtyping] |
| from typing import Tuple, TypeVar, Protocol |
| |
| T = TypeVar('T', covariant=True) |
| |
| class MyProto(Protocol[T]): |
| def __len__(self) -> T: |
| pass |
| |
| t: Tuple[int, str] |
| def f(x: MyProto[int]) -> None: |
| pass |
| f(t) # OK |
| |
| y: MyProto[str] |
| y = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "MyProto[str]") |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testBasicNamedTupleStructuralSubtyping] |
| from typing import NamedTuple, TypeVar, Protocol |
| |
| T = TypeVar('T', covariant=True) |
| S = TypeVar('S', covariant=True) |
| |
| class P(Protocol[T, S]): |
| @property |
| def x(self) -> T: pass |
| @property |
| def y(self) -> S: pass |
| |
| class N(NamedTuple): |
| x: int |
| y: str |
| class N2(NamedTuple): |
| x: int |
| class N3(NamedTuple): |
| x: int |
| y: int |
| |
| z: N |
| z3: N3 |
| |
| def fun(x: P[int, str]) -> None: |
| pass |
| def fun2(x: P[int, int]) -> None: |
| pass |
| def fun3(x: P[T, T]) -> T: |
| return x.x |
| |
| fun(z) |
| fun2(z) # E: Argument 1 to "fun2" has incompatible type "N"; expected "P[int, int]" \ |
| # N: Following member(s) of "N" have conflicts: \ |
| # N: y: expected "int", got "str" |
| |
| fun(N2(1)) # E: Argument 1 to "fun" has incompatible type "N2"; expected "P[int, str]" \ |
| # N: "N2" is missing following "P" protocol member: \ |
| # N: y |
| |
| reveal_type(fun3(z)) # N: Revealed type is "builtins.object" |
| |
| reveal_type(fun3(z3)) # N: Revealed type is "builtins.int" |
| [builtins fixtures/list.pyi] |
| |
| [case testBasicCallableStructuralSubtyping] |
| from typing import Callable, Generic, TypeVar |
| |
| def apply(f: Callable[[int], int], x: int) -> int: |
| return f(x) |
| |
| class Add5: |
| def __call__(self, x: int) -> int: |
| return x + 5 |
| |
| apply(Add5(), 5) |
| |
| T = TypeVar('T') |
| def apply_gen(f: Callable[[T], T]) -> T: |
| pass |
| |
| reveal_type(apply_gen(Add5())) # N: Revealed type is "builtins.int" |
| def apply_str(f: Callable[[str], int], x: str) -> int: |
| return f(x) |
| apply_str(Add5(), 'a') # E: Argument 1 to "apply_str" has incompatible type "Add5"; expected "Callable[[str], int]" \ |
| # N: "Add5.__call__" has type "Callable[[Arg(int, 'x')], int]" |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testMoreComplexCallableStructuralSubtyping] |
| from mypy_extensions import Arg, VarArg |
| from typing import Protocol, Callable |
| |
| def call_soon(cb: Callable[[Arg(int, 'x'), VarArg(str)], int]): pass |
| |
| class Good: |
| def __call__(self, x: int, *rest: str) -> int: pass |
| class Bad1: |
| def __call__(self, x: int, *rest: int) -> int: pass |
| class Bad2: |
| def __call__(self, y: int, *rest: str) -> int: pass |
| call_soon(Good()) |
| call_soon(Bad1()) # E: Argument 1 to "call_soon" has incompatible type "Bad1"; expected "Callable[[int, VarArg(str)], int]" \ |
| # N: "Bad1.__call__" has type "Callable[[Arg(int, 'x'), VarArg(int)], int]" |
| call_soon(Bad2()) # E: Argument 1 to "call_soon" has incompatible type "Bad2"; expected "Callable[[int, VarArg(str)], int]" \ |
| # N: "Bad2.__call__" has type "Callable[[Arg(int, 'y'), VarArg(str)], int]" |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testStructuralSupportForPartial] |
| from typing import Callable, TypeVar, Generic, Any |
| |
| T = TypeVar('T') |
| |
| class partial(Generic[T]): |
| def __init__(self, func: Callable[..., T], *args: Any) -> None: ... |
| def __call__(self, *args: Any) -> T: ... |
| |
| def inc(a: int, temp: str) -> int: |
| pass |
| |
| def foo(f: Callable[[int], T]) -> T: |
| return f(1) |
| |
| reveal_type(foo(partial(inc, 'temp'))) # N: Revealed type is "builtins.int" |
| [builtins fixtures/list.pyi] |
| |
| [case testStructuralInferenceForCallable] |
| from typing import Callable, TypeVar, Tuple |
| |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class Actual: |
| def __call__(self, arg: int) -> str: pass |
| |
| def fun(cb: Callable[[T], S]) -> Tuple[T, S]: pass |
| reveal_type(fun(Actual())) # N: Revealed type is "Tuple[builtins.int, builtins.str]" |
| [builtins fixtures/tuple.pyi] |
| |
| -- Standard protocol types (SupportsInt, Sized, etc.) |
| -- -------------------------------------------------- |
| |
| -- More tests could be added for types from typing converted to protocols |
| |
| [case testBasicSizedProtocol] |
| from typing import Sized |
| |
| class Foo: |
| def __len__(self) -> int: |
| return 42 |
| |
| def bar(a: Sized) -> int: |
| return a.__len__() |
| |
| bar(Foo()) |
| bar((1, 2)) |
| bar(1) # E: Argument 1 to "bar" has incompatible type "int"; expected "Sized" |
| |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testBasicSupportsIntProtocol] |
| from typing import SupportsInt |
| |
| class Bar: |
| def __int__(self): |
| return 1 |
| |
| def foo(a: SupportsInt): |
| pass |
| |
| foo(Bar()) |
| foo('no way') # E: Argument 1 to "foo" has incompatible type "str"; expected "SupportsInt" |
| |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| -- Additional tests and corner cases for protocols |
| -- ---------------------------------------------- |
| |
| [case testAnyWithProtocols] |
| from typing import Protocol, Any, TypeVar |
| |
| T = TypeVar('T') |
| |
| class P1(Protocol): |
| attr1: int |
| class P2(Protocol[T]): |
| attr2: T |
| class P3(Protocol): |
| attr: P3 |
| |
| def f1(x: P1) -> None: pass |
| def f2(x: P2[str]) -> None: pass |
| def f3(x: P3) -> None: pass |
| |
| class C1: |
| attr1: Any |
| class C2: |
| attr2: Any |
| class C3: |
| attr: Any |
| |
| f1(C1()) |
| f2(C2()) |
| f3(C3()) |
| |
| f2(C3()) # E: Argument 1 to "f2" has incompatible type "C3"; expected "P2[str]" |
| a: Any |
| f1(a) |
| f2(a) |
| f3(a) |
| |
| [case testErrorsForProtocolsInDifferentPlaces] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr1: int |
| attr2: str |
| attr3: int |
| |
| class C: |
| attr1: str |
| @property |
| def attr2(self) -> int: pass |
| |
| x: P = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P") \ |
| # N: "C" is missing following "P" protocol member: \ |
| # N: attr3 \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: attr1: expected "int", got "str" \ |
| # N: attr2: expected "str", got "int" \ |
| # N: Protocol member P.attr2 expected settable variable, got read-only attribute |
| |
| def f(x: P) -> P: |
| return C() # E: Incompatible return value type (got "C", expected "P") \ |
| # N: "C" is missing following "P" protocol member: \ |
| # N: attr3 \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: attr1: expected "int", got "str" \ |
| # N: attr2: expected "str", got "int" \ |
| # N: Protocol member P.attr2 expected settable variable, got read-only attribute |
| |
| f(C()) # E: Argument 1 to "f" has incompatible type "C"; expected "P" \ |
| # N: "C" is missing following "P" protocol member: \ |
| # N: attr3 \ |
| # N: Following member(s) of "C" have conflicts: \ |
| # N: attr1: expected "int", got "str" \ |
| # N: attr2: expected "str", got "int" \ |
| # N: Protocol member P.attr2 expected settable variable, got read-only attribute |
| [builtins fixtures/list.pyi] |
| |
| [case testIterableProtocolOnClass] |
| from typing import TypeVar, Iterator |
| T = TypeVar('T', bound='A') |
| |
| class A: |
| def __iter__(self: T) -> Iterator[T]: pass |
| |
| class B(A): pass |
| |
| reveal_type(list(b for b in B())) # N: Revealed type is "builtins.list[__main__.B]" |
| reveal_type(list(B())) # N: Revealed type is "builtins.list[__main__.B]" |
| [builtins fixtures/list.pyi] |
| |
| [case testIterableProtocolOnMetaclass] |
| from typing import TypeVar, Iterator, Type |
| T = TypeVar('T') |
| |
| class EMeta(type): |
| def __iter__(self: Type[T]) -> Iterator[T]: pass |
| |
| class E(metaclass=EMeta): |
| pass |
| |
| class C(E): |
| pass |
| |
| reveal_type(list(c for c in C)) # N: Revealed type is "builtins.list[__main__.C]" |
| reveal_type(list(C)) # N: Revealed type is "builtins.list[__main__.C]" |
| [builtins fixtures/list.pyi] |
| |
| [case testClassesGetattrWithProtocols] |
| from typing import Protocol |
| |
| class P(Protocol): |
| attr: int |
| |
| class PP(Protocol): |
| @property |
| def attr(self) -> int: |
| pass |
| |
| class C: |
| def __getattr__(self, attr: str) -> int: |
| pass |
| class C2(C): |
| def __setattr__(self, attr: str, val: int) -> None: |
| pass |
| |
| class D: |
| def __getattr__(self, attr: str) -> str: |
| pass |
| |
| def fun(x: P) -> None: |
| reveal_type(P.attr) # N: Revealed type is "builtins.int" |
| def fun_p(x: PP) -> None: |
| reveal_type(P.attr) # N: Revealed type is "builtins.int" |
| |
| fun(C()) # E: Argument 1 to "fun" has incompatible type "C"; expected "P" \ |
| # N: Protocol member P.attr expected settable variable, got read-only attribute |
| fun(C2()) |
| fun_p(D()) # E: Argument 1 to "fun_p" has incompatible type "D"; expected "PP" \ |
| # N: Following member(s) of "D" have conflicts: \ |
| # N: attr: expected "int", got "str" |
| fun_p(C()) # OK |
| [builtins fixtures/list.pyi] |
| |
| [case testImplicitTypesInProtocols] |
| from typing import Protocol |
| |
| class P(Protocol): |
| x = 1 # E: All protocol members must have explicitly declared types |
| |
| class C: |
| x: int |
| |
| class D: |
| x: str |
| |
| x: P |
| x = D() # E: Incompatible types in assignment (expression has type "D", variable has type "P") \ |
| # N: Following member(s) of "D" have conflicts: \ |
| # N: x: expected "int", got "str" |
| x = C() # OK |
| [builtins fixtures/list.pyi] |
| |
| [case testProtocolIncompatibilityWithGenericMethod] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class A(Protocol): |
| def f(self, x: T) -> None: pass |
| class B: |
| def f(self, x: S, y: T) -> None: pass |
| |
| x: A = B() |
| [out] |
| main:11: error: Incompatible types in assignment (expression has type "B", variable has type "A") |
| main:11: note: Following member(s) of "B" have conflicts: |
| main:11: note: Expected: |
| main:11: note: def [T] f(self, x: T) -> None |
| main:11: note: Got: |
| main:11: note: def [S, T] f(self, x: S, y: T) -> None |
| |
| [case testProtocolIncompatibilityWithGenericMethodBounded] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| S = TypeVar('S', bound=int) |
| |
| class A(Protocol): |
| def f(self, x: T) -> None: pass |
| class B: |
| def f(self, x: S, y: T) -> None: pass |
| |
| x: A = B() |
| [out] |
| main:11: error: Incompatible types in assignment (expression has type "B", variable has type "A") |
| main:11: note: Following member(s) of "B" have conflicts: |
| main:11: note: Expected: |
| main:11: note: def [T] f(self, x: T) -> None |
| main:11: note: Got: |
| main:11: note: def [S <: int, T] f(self, x: S, y: T) -> None |
| |
| [case testProtocolIncompatibilityWithGenericRestricted] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| S = TypeVar('S', int, str) |
| |
| class A(Protocol): |
| def f(self, x: T) -> None: pass |
| class B: |
| def f(self, x: S, y: T) -> None: pass |
| |
| x: A = B() |
| [out] |
| main:11: error: Incompatible types in assignment (expression has type "B", variable has type "A") |
| main:11: note: Following member(s) of "B" have conflicts: |
| main:11: note: Expected: |
| main:11: note: def [T] f(self, x: T) -> None |
| main:11: note: Got: |
| main:11: note: def [S in (int, str), T] f(self, x: S, y: T) -> None |
| |
| [case testProtocolIncompatibilityWithManyOverloads] |
| from typing import Protocol, overload |
| |
| class C1: pass |
| class C2: pass |
| class A(Protocol): |
| @overload |
| def f(self, x: int) -> int: pass |
| @overload |
| def f(self, x: str) -> str: pass |
| @overload |
| def f(self, x: C1) -> C2: pass |
| @overload |
| def f(self, x: C2) -> C1: pass |
| |
| class B: |
| def f(self) -> None: pass |
| |
| x: A = B() |
| [out] |
| main:18: error: Incompatible types in assignment (expression has type "B", variable has type "A") |
| main:18: note: Following member(s) of "B" have conflicts: |
| main:18: note: Expected: |
| main:18: note: @overload |
| main:18: note: def f(self, x: int) -> int |
| main:18: note: @overload |
| main:18: note: def f(self, x: str) -> str |
| main:18: note: @overload |
| main:18: note: def f(self, x: C1) -> C2 |
| main:18: note: @overload |
| main:18: note: def f(self, x: C2) -> C1 |
| main:18: note: Got: |
| main:18: note: def f(self) -> None |
| |
| [case testProtocolIncompatibilityWithManyConflicts] |
| from typing import Protocol |
| |
| class A(Protocol): |
| def f(self, x: int) -> None: pass |
| def g(self, x: int) -> None: pass |
| def h(self, x: int) -> None: pass |
| def i(self, x: int) -> None: pass |
| class B: |
| def f(self, x: str) -> None: pass |
| def g(self, x: str) -> None: pass |
| def h(self, x: str) -> None: pass |
| def i(self, x: str) -> None: pass |
| |
| x: A = B() |
| [out] |
| main:14: error: Incompatible types in assignment (expression has type "B", variable has type "A") |
| main:14: note: Following member(s) of "B" have conflicts: |
| main:14: note: Expected: |
| main:14: note: def f(self, x: int) -> None |
| main:14: note: Got: |
| main:14: note: def f(self, x: str) -> None |
| main:14: note: Expected: |
| main:14: note: def g(self, x: int) -> None |
| main:14: note: Got: |
| main:14: note: def g(self, x: str) -> None |
| main:14: note: <2 more conflict(s) not shown> |
| |
| [case testProtocolIncompatibilityWithUnionType] |
| from typing import Any, Optional, Protocol |
| |
| class A(Protocol): |
| def execute(self, statement: Any, *args: Any, **kwargs: Any) -> None: ... |
| |
| class B(Protocol): |
| def execute(self, stmt: Any, *args: Any, **kwargs: Any) -> None: ... |
| def cool(self) -> None: ... |
| |
| def func1(arg: A) -> None: ... |
| def func2(arg: Optional[A]) -> None: ... |
| |
| x: B |
| func1(x) |
| func2(x) |
| [builtins fixtures/tuple.pyi] |
| [builtins fixtures/dict.pyi] |
| [out] |
| main:14: error: Argument 1 to "func1" has incompatible type "B"; expected "A" |
| main:14: note: Following member(s) of "B" have conflicts: |
| main:14: note: Expected: |
| main:14: note: def execute(self, statement: Any, *args: Any, **kwargs: Any) -> None |
| main:14: note: Got: |
| main:14: note: def execute(self, stmt: Any, *args: Any, **kwargs: Any) -> None |
| main:15: error: Argument 1 to "func2" has incompatible type "B"; expected "Optional[A]" |
| main:15: note: Following member(s) of "B" have conflicts: |
| main:15: note: Expected: |
| main:15: note: def execute(self, statement: Any, *args: Any, **kwargs: Any) -> None |
| main:15: note: Got: |
| main:15: note: def execute(self, stmt: Any, *args: Any, **kwargs: Any) -> None |
| |
| [case testDontShowNotesForTupleAndIterableProtocol] |
| from typing import Iterable, Sequence, Protocol, NamedTuple |
| |
| class N(NamedTuple): |
| x: int |
| |
| def f1(x: Iterable[str]) -> None: pass |
| def f2(x: Sequence[str]) -> None: pass |
| |
| # The errors below should be short |
| f1(N(1)) # E: Argument 1 to "f1" has incompatible type "N"; expected "Iterable[str]" |
| f2(N(2)) # E: Argument 1 to "f2" has incompatible type "N"; expected "Sequence[str]" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testNotManyFlagConflitsShownInProtocols] |
| from typing import Protocol |
| |
| class AllSettable(Protocol): |
| a: int |
| b: int |
| c: int |
| d: int |
| |
| class AllReadOnly: |
| @property |
| def a(self) -> int: pass |
| @property |
| def b(self) -> int: pass |
| @property |
| def c(self) -> int: pass |
| @property |
| def d(self) -> int: pass |
| |
| x: AllSettable = AllReadOnly() |
| [builtins fixtures/property.pyi] |
| [out] |
| main:19: error: Incompatible types in assignment (expression has type "AllReadOnly", variable has type "AllSettable") |
| main:19: note: Protocol member AllSettable.a expected settable variable, got read-only attribute |
| main:19: note: Protocol member AllSettable.b expected settable variable, got read-only attribute |
| main:19: note: <2 more conflict(s) not shown> |
| |
| [case testProtocolsMoreConflictsNotShown] |
| from typing_extensions import Protocol |
| from typing import Generic, TypeVar |
| |
| T = TypeVar('T') |
| |
| class MockMapping(Protocol[T]): |
| def a(self, x: T) -> int: pass |
| def b(self, x: T) -> int: pass |
| def c(self, x: T) -> int: pass |
| d: T |
| e: T |
| f: T |
| |
| class MockDict(MockMapping[T]): |
| more: int |
| |
| def f(x: MockMapping[int]) -> None: pass |
| x: MockDict[str] |
| f(x) # E: Argument 1 to "f" has incompatible type "MockDict[str]"; expected "MockMapping[int]" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testProtocolNotesForComplexSignatures] |
| from typing import Protocol, Optional |
| |
| class P(Protocol): |
| def meth(self, x: int, *args: str) -> None: pass |
| def other(self, *args, hint: Optional[str] = None, **kwargs: str) -> None: pass |
| class C: |
| def meth(self) -> int: pass |
| def other(self) -> int: pass |
| |
| x: P = C() |
| [builtins fixtures/dict.pyi] |
| [out] |
| main:10: error: Incompatible types in assignment (expression has type "C", variable has type "P") |
| main:10: note: Following member(s) of "C" have conflicts: |
| main:10: note: Expected: |
| main:10: note: def meth(self, x: int, *args: str) -> None |
| main:10: note: Got: |
| main:10: note: def meth(self) -> int |
| main:10: note: Expected: |
| main:10: note: def other(self, *args: Any, hint: Optional[str] = ..., **kwargs: str) -> None |
| main:10: note: Got: |
| main:10: note: def other(self) -> int |
| |
| [case testObjectAllowedInProtocolBases] |
| from typing import Protocol |
| class P(Protocol, object): |
| pass |
| [out] |
| |
| [case testNoneSubtypeOfEmptyProtocol] |
| from typing import Protocol |
| class P(Protocol): |
| pass |
| |
| x: P = None |
| [out] |
| |
| [case testNoneSubtypeOfAllProtocolsWithoutStrictOptional] |
| from typing import Protocol |
| class P(Protocol): |
| attr: int |
| def meth(self, arg: str) -> str: |
| pass |
| |
| x: P = None |
| [out] |
| |
| [case testNoneSubtypeOfEmptyProtocolStrict] |
| # flags: --strict-optional |
| from typing import Protocol |
| class P(Protocol): |
| pass |
| x: P = None |
| |
| class PBad(Protocol): |
| x: int |
| y: PBad = None # E: Incompatible types in assignment (expression has type "None", variable has type "PBad") |
| [out] |
| |
| [case testOnlyMethodProtocolUsableWithIsSubclass] |
| from typing import Protocol, runtime_checkable, Union, Type, Sequence, overload |
| @runtime_checkable |
| class P(Protocol): |
| def meth(self) -> int: |
| pass |
| @runtime_checkable |
| class PBad(Protocol): |
| x: str |
| |
| class C: |
| x: str |
| def meth(self) -> int: |
| pass |
| class E: pass |
| |
| cls: Type[Union[C, E]] |
| issubclass(cls, PBad) # E: Only protocols that don't have non-method members can be used with issubclass() \ |
| # N: Protocol "PBad" has non-method member(s): x |
| if issubclass(cls, P): |
| reveal_type(cls) # N: Revealed type is "Type[__main__.C]" |
| else: |
| reveal_type(cls) # N: Revealed type is "Type[__main__.E]" |
| |
| @runtime_checkable |
| class POverload(Protocol): |
| @overload |
| def meth(self, a: int) -> float: ... |
| @overload |
| def meth(self, a: str) -> Sequence[float]: ... |
| def meth(self, a): |
| pass |
| |
| reveal_type(issubclass(int, POverload)) # N: Revealed type is "builtins.bool" |
| [builtins fixtures/isinstance.pyi] |
| [typing fixtures/typing-full.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocol] |
| from typing import Protocol |
| |
| class Caller(Protocol): |
| def __call__(self, x: str, *args: int) -> None: ... |
| |
| def call(x: str, *args: int) -> None: |
| pass |
| def bad(x: int, *args: str) -> None: |
| pass |
| |
| def func(caller: Caller) -> None: |
| pass |
| |
| func(call) |
| func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int, VarArg(str)], None]"; expected "Caller" |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocolGeneric] |
| from typing import Protocol, TypeVar, Tuple |
| |
| T = TypeVar('T') |
| S = TypeVar('S') |
| |
| class Caller(Protocol[T, S]): |
| def __call__(self, x: T, y: S) -> Tuple[T, S]: ... |
| |
| def call(x: int, y: str) -> Tuple[int, str]: ... |
| |
| def func(caller: Caller[T, S]) -> Tuple[T, S]: |
| pass |
| |
| reveal_type(func(call)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocolGenericTight] |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| |
| class Caller(Protocol): |
| def __call__(self, x: T) -> T: ... |
| |
| def call(x: T) -> T: ... |
| def bad(x: int) -> int: ... |
| |
| def func(caller: Caller) -> None: |
| pass |
| |
| func(call) |
| func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[int], int]"; expected "Caller" |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocolGenericNotGeneric] |
| from typing import Protocol, TypeVar, Tuple |
| |
| T = TypeVar('T') |
| |
| class Caller(Protocol): |
| def __call__(self, x: int) -> int: ... |
| |
| def call(x: T) -> T: ... |
| |
| def bad(x: T) -> Tuple[T, T]: ... |
| |
| def func(caller: Caller) -> None: |
| pass |
| |
| func(call) |
| func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[T], Tuple[T, T]]"; expected "Caller" |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocolOverload] |
| from typing import Protocol, overload, Union |
| |
| class Caller(Protocol): |
| @overload |
| def __call__(self, x: int) -> int: ... |
| @overload |
| def __call__(self, x: str) -> str: ... |
| |
| @overload |
| def call(x: int) -> int: ... |
| @overload |
| def call(x: str) -> str: ... |
| def call(x: Union[int, str]) -> Union[int, str]: |
| pass |
| |
| def bad(x: Union[int, str]) -> Union[int, str]: |
| pass |
| |
| def func(caller: Caller) -> None: |
| pass |
| |
| func(call) |
| func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[Union[int, str]], Union[int, str]]"; expected "Caller" |
| [out] |
| |
| [case testCallableImplementsProtocolExtraNote] |
| from typing import Protocol |
| |
| class Caller(Protocol): |
| def __call__(self, x: str, *args: int) -> None: ... |
| |
| def bad(x: int, *args: str) -> None: |
| pass |
| |
| cb: Caller = bad # E: Incompatible types in assignment (expression has type "Callable[[int, VarArg(str)], None]", variable has type "Caller") \ |
| # N: "Caller.__call__" has type "Callable[[Arg(str, 'x'), VarArg(int)], None]" |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testCallableImplementsProtocolArgName] |
| from typing import Protocol |
| |
| class Caller(Protocol): |
| def __call__(self, x: str) -> None: ... |
| |
| class CallerAnon(Protocol): |
| def __call__(self, __x: str) -> None: ... |
| |
| def call(x: str) -> None: |
| pass |
| def bad(y: str) -> None: |
| pass |
| |
| def func(caller: Caller) -> None: |
| pass |
| |
| def anon(caller: CallerAnon) -> None: |
| pass |
| |
| |
| func(call) |
| func(bad) # E: Argument 1 to "func" has incompatible type "Callable[[str], None]"; expected "Caller" |
| anon(bad) |
| [out] |
| |
| [case testCallableProtocolVsProtocol] |
| from typing import Protocol |
| |
| class One(Protocol): |
| def __call__(self, x: str) -> None: ... |
| |
| class Other(Protocol): |
| def __call__(self, x: str) -> None: ... |
| |
| class Bad(Protocol): |
| def __call__(self, zzz: str) -> None: ... |
| |
| def func(caller: One) -> None: |
| pass |
| |
| a: Other |
| b: Bad |
| |
| func(a) |
| func(b) # E: Argument 1 to "func" has incompatible type "Bad"; expected "One" |
| [out] |
| |
| [case testJoinProtocolCallback] |
| from typing import Protocol, Callable |
| |
| class A: ... |
| class B(A): ... |
| class C(B): ... |
| class D(B): ... |
| |
| class Call(Protocol): |
| def __call__(self, x: B) -> C: ... |
| Normal = Callable[[A], D] |
| |
| a: Call |
| b: Normal |
| |
| reveal_type([a, b]) # N: Revealed type is "builtins.list[def (__main__.B) -> __main__.B]" |
| reveal_type([b, a]) # N: Revealed type is "builtins.list[def (__main__.B) -> __main__.B]" |
| [builtins fixtures/list.pyi] |
| [out] |
| |
| [case testMeetProtocolCallback] |
| from typing import Protocol, Callable |
| |
| class A: ... |
| class B(A): ... |
| class C(B): ... |
| class D(B): ... |
| |
| class Call(Protocol): |
| def __call__(self, __x: C) -> B: ... |
| Normal = Callable[[D], A] |
| |
| def a(x: Call) -> None: ... |
| def b(x: Normal) -> None: ... |
| |
| reveal_type([a, b]) # N: Revealed type is "builtins.list[def (x: def (__main__.B) -> __main__.B)]" |
| reveal_type([b, a]) # N: Revealed type is "builtins.list[def (x: def (__main__.B) -> __main__.B)]" |
| [builtins fixtures/list.pyi] |
| [out] |
| |
| [case testProtocolsAlwaysABCs] |
| from typing import Protocol |
| |
| class P(Protocol): ... |
| class C(P): ... |
| |
| reveal_type(C.register(int)) # N: Revealed type is "def () -> builtins.int" |
| [typing fixtures/typing-full.pyi] |
| [out] |
| |
| [case testProtocolVarianceAfterDecorators] |
| # The test case is simplified, in reality this caused problems with @abstractmethod |
| # in stubs and test fixtures. |
| from typing import Protocol, TypeVar |
| |
| T = TypeVar('T') |
| def dec(x: T) -> T: ... |
| alias = dec |
| |
| class P(Protocol[T]): |
| @alias |
| def meth(self, arg: T) -> T: ... |
| [out] |
| |
| [case testNamedTupleWithNoArgsCallableField] |
| from typing import Callable, NamedTuple, Protocol |
| |
| class N(NamedTuple): |
| func: Callable[[], str] |
| |
| class P(Protocol): |
| @property |
| def func(self) -> Callable[[], str]: ... |
| |
| p: P = N(lambda: 'foo') |
| [builtins fixtures/property.pyi] |
| |
| [case testNamedTupleWithManyArgsCallableField] |
| from typing import Callable, NamedTuple, Protocol |
| |
| class N(NamedTuple): |
| func: Callable[[str, str, str], str] |
| |
| class P(Protocol): |
| @property |
| def func(self) -> Callable[[str, str, str], str]: ... |
| |
| p: P = N(lambda a, b, c: 'foo') |
| [builtins fixtures/property.pyi] |
| |
| [case testLiteralsAgainstProtocols] |
| from typing import SupportsInt, SupportsAbs, TypeVar |
| from typing_extensions import Literal, Final |
| |
| T = TypeVar('T') |
| def abs(x: SupportsAbs[T]) -> T: ... |
| def foo(x: SupportsInt) -> None: ... |
| |
| ONE: Final = 1 |
| TWO: Literal[2] |
| ALL: Literal[1, 2, 3] |
| |
| foo(ONE) |
| foo(TWO) |
| foo(3) |
| |
| reveal_type(abs(ONE)) # N: Revealed type is "builtins.int" |
| reveal_type(abs(TWO)) # N: Revealed type is "builtins.int" |
| reveal_type(abs(3)) # N: Revealed type is "builtins.int" |
| reveal_type(abs(ALL)) # N: Revealed type is "builtins.int" |
| [builtins fixtures/float.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testProtocolWithSlots] |
| from typing import Protocol |
| |
| class A(Protocol): |
| __slots__ = () |
| |
| [builtins fixtures/tuple.pyi] |
| |
| [case testNoneVsProtocol] |
| # mypy: strict-optional |
| from typing_extensions import Protocol |
| |
| class MyHashable(Protocol): |
| def __hash__(self) -> int: ... |
| |
| def f(h: MyHashable) -> None: pass |
| f(None) |
| |
| class Proto(Protocol): |
| def __hash__(self) -> int: ... |
| def method(self) -> None: ... |
| |
| def g(h: Proto) -> None: pass |
| g(None) # E: Argument 1 to "g" has incompatible type "None"; expected "Proto" |
| |
| class Proto2(Protocol): |
| def hash(self) -> None: ... |
| |
| def h(h: Proto2) -> None: pass |
| h(None) # E: Argument 1 to "h" has incompatible type "None"; expected "Proto2" |
| |
| class EmptyProto(Protocol): ... |
| |
| def hh(h: EmptyProto) -> None: pass |
| hh(None) |
| [builtins fixtures/tuple.pyi] |
| |
| |
| [case testPartialTypeProtocol] |
| from typing import Protocol |
| |
| class Flapper(Protocol): |
| def flap(self) -> int: ... |
| |
| class Blooper: |
| flap = None |
| |
| def bloop(self, x: Flapper) -> None: |
| reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" |
| |
| class Gleemer: |
| flap = [] # E: Need type annotation for "flap" (hint: "flap: List[<type>] = ...") |
| |
| def gleem(self, x: Flapper) -> None: |
| reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" |
| [builtins fixtures/tuple.pyi] |
| |
| |
| [case testPartialTypeProtocolHashable] |
| # flags: --no-strict-optional |
| from typing import Protocol |
| |
| class Hashable(Protocol): |
| def __hash__(self) -> int: ... |
| |
| class ObjectHashable: |
| def __hash__(self) -> int: ... |
| |
| class DataArray(ObjectHashable): |
| __hash__ = None |
| |
| def f(self, x: Hashable) -> None: |
| reveal_type([self, x]) # N: Revealed type is "builtins.list[builtins.object]" |
| [builtins fixtures/tuple.pyi] |
| |
| |
| [case testPartialAttributeNoneType] |
| # flags: --no-strict-optional |
| from typing import Optional, Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class MyProtocol(Protocol): |
| def is_valid(self) -> bool: ... |
| text: Optional[str] |
| |
| class MyClass: |
| text = None |
| def is_valid(self) -> bool: |
| reveal_type(self.text) # N: Revealed type is "None" |
| assert isinstance(self, MyProtocol) |
| [builtins fixtures/isinstance.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| |
| [case testPartialAttributeNoneTypeStrictOptional] |
| # flags: --strict-optional |
| from typing import Optional, Protocol, runtime_checkable |
| |
| @runtime_checkable |
| class MyProtocol(Protocol): |
| def is_valid(self) -> bool: ... |
| text: Optional[str] |
| |
| class MyClass: |
| text = None |
| def is_valid(self) -> bool: |
| reveal_type(self.text) # N: Revealed type is "None" |
| assert isinstance(self, MyProtocol) |
| [builtins fixtures/isinstance.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testProtocolAndTypeVariableSpecialCase] |
| from typing import TypeVar, Iterable, Optional, Callable, Protocol |
| |
| T_co = TypeVar('T_co', covariant=True) |
| |
| class SupportsNext(Protocol[T_co]): |
| def __next__(self) -> T_co: ... |
| |
| N = TypeVar("N", bound=SupportsNext, covariant=True) |
| |
| class SupportsIter(Protocol[T_co]): |
| def __iter__(self) -> T_co: ... |
| |
| def f(i: SupportsIter[N]) -> N: ... |
| |
| I = TypeVar('I', bound=Iterable) |
| |
| def g(x: I, y: Iterable) -> None: |
| f(x) |
| f(y) |
| |
| [case testMatchProtocolAgainstOverloadWithAmbiguity] |
| from typing import TypeVar, Protocol, Union, Generic, overload |
| |
| T = TypeVar("T", covariant=True) |
| |
| class slice: pass |
| |
| class GetItem(Protocol[T]): |
| def __getitem__(self, k: int) -> T: ... |
| |
| class Str: # Resembles 'str' |
| def __getitem__(self, k: Union[int, slice]) -> Str: ... |
| |
| class Lst(Generic[T]): # Resembles 'list' |
| def __init__(self, x: T): ... |
| @overload |
| def __getitem__(self, k: int) -> T: ... |
| @overload |
| def __getitem__(self, k: slice) -> Lst[T]: ... |
| def __getitem__(self, k): pass |
| |
| def f(x: GetItem[GetItem[Str]]) -> None: ... |
| |
| a: Lst[Str] |
| f(Lst(a)) |
| |
| class Lst2(Generic[T]): |
| def __init__(self, x: T): ... |
| # The overload items are tweaked but still compatible |
| @overload |
| def __getitem__(self, k: Str) -> None: ... |
| @overload |
| def __getitem__(self, k: slice) -> Lst2[T]: ... |
| @overload |
| def __getitem__(self, k: Union[int, str]) -> T: ... |
| def __getitem__(self, k): pass |
| |
| b: Lst2[Str] |
| f(Lst2(b)) |
| |
| class Lst3(Generic[T]): # Resembles 'list' |
| def __init__(self, x: T): ... |
| # The overload items are no longer compatible (too narrow argument type) |
| @overload |
| def __getitem__(self, k: slice) -> Lst3[T]: ... |
| @overload |
| def __getitem__(self, k: bool) -> T: ... |
| def __getitem__(self, k): pass |
| |
| c: Lst3[Str] |
| f(Lst3(c)) # E: Argument 1 to "f" has incompatible type "Lst3[Lst3[Str]]"; expected "GetItem[GetItem[Str]]" \ |
| # N: Following member(s) of "Lst3[Lst3[Str]]" have conflicts: \ |
| # N: Expected: \ |
| # N: def __getitem__(self, int) -> GetItem[Str] \ |
| # N: Got: \ |
| # N: @overload \ |
| # N: def __getitem__(self, slice) -> Lst3[Lst3[Str]] \ |
| # N: @overload \ |
| # N: def __getitem__(self, bool) -> Lst3[Str] |
| |
| [builtins fixtures/list.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testMatchProtocolAgainstOverloadWithMultipleMatchingItems] |
| from typing import Protocol, overload, TypeVar, Any |
| |
| _T_co = TypeVar("_T_co", covariant=True) |
| _T = TypeVar("_T") |
| |
| class SupportsRound(Protocol[_T_co]): |
| @overload |
| def __round__(self) -> int: ... |
| @overload |
| def __round__(self, __ndigits: int) -> _T_co: ... |
| |
| class C: |
| # This matches both overload items of SupportsRound |
| def __round__(self, __ndigits: int = ...) -> int: ... |
| |
| def round(number: SupportsRound[_T], ndigits: int) -> _T: ... |
| |
| round(C(), 1) |