← Back to index

protocols_generic.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 6
FP: 0
FN: 3
Optional: 0 / 0
1"""
2Tests the handling of generic protocols.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#generic-protocols
6
7
8from typing import Callable, Generic, Iterator, Protocol, Self, TypeVar, assert_type
9
10S = TypeVar("S")
11T = TypeVar("T")
12T_co = TypeVar("T_co", covariant=True)
13T_contra = TypeVar("T_contra", contravariant=True)
16class Iterable(Protocol[T_co]):
17 def __iter__(self) -> Iterator[T_co]:
18 ...
21# > Protocol[T, S, ...] is allowed as a shorthand for Protocol, Generic[T, S, ...].
22# In particular, the implicit order of type parameters is dictated by
23# the order in which they appear in the `Protocol` subscript.
26class Proto1(Iterable[T_co], Protocol[S, T_co]):
27 def method1(self, x: S) -> S:
28 ...
31class Concrete1:
32 def __iter__(self) -> Iterator[int]:
33 return (x for x in [1, 2, 3])
35 def method1(self, x: str) -> str:
36 return ""
39p1: Proto1[str, int] = Concrete1() # OK
40p2: Proto1[int, str] = Concrete1() # E: incompatible type
[invalid-assignment] Object of type `Concrete1` is not assignable to `Proto1[int, str]`
43# > It is an error to combine the shorthand with Generic[T, S, ...]
44class Proto2(Protocol[T_co], Generic[T_co]): # E
[invalid-generic-class] Cannot both inherit from subscripted `Protocol` and subscripted `Generic`
45 ...
48# > User-defined generic protocols support explicitly declared variance.
49class Box(Protocol[T_co]):
50 def content(self) -> T_co:
51 ...
54def func1(box_int: Box[int], box_float: Box[float]):
55 v1: Box[float] = box_int # OK
56 v2: Box[int] = box_float # E
[invalid-assignment] Object of type `Box[int | float]` is not assignable to `Box[int]`
59class Sender(Protocol[T_contra]):
60 def send(self, data: T_contra) -> int:
61 return 0
64def func2(sender_int: Sender[int], sender_float: Sender[float]):
65 v1: Sender[int] = sender_float # OK
66 v2: Sender[float] = sender_int # E
[invalid-assignment] Object of type `Sender[int]` is not assignable to `Sender[int | float]`
69class AttrProto(Protocol[T]):
70 attr: T
73def func3(attr_int: AttrProto[int], attr_float: AttrProto[float]):
74 v1: AttrProto[float] = attr_int # E
[invalid-assignment] Object of type `AttrProto[int]` is not assignable to `AttrProto[int | float]`
75 v2: AttrProto[int] = attr_float # E
[invalid-assignment] Object of type `AttrProto[int | float]` is not assignable to `AttrProto[int]`
78class HasParent(Protocol):
79 def get_parent(self: T) -> T:
80 ...
83GenericHasParent = TypeVar("GenericHasParent", bound=HasParent)
86def generic_get_parent(n: GenericHasParent) -> GenericHasParent:
87 return n.get_parent()
90class ConcreteHasParent:
91 def get_parent(self) -> Self:
92 return self
95parent = generic_get_parent(ConcreteHasParent()) # OK
96assert_type(parent, ConcreteHasParent)
99class HasPropertyProto(Protocol):
100 @property
101 def f(self: T) -> T:
102 ...
104 def m(self, item: T, callback: Callable[[T], str]) -> str:
105 ...
108class ConcreteHasProperty1:
109 @property
110 def f(self: T) -> T:
111 return self
113 def m(self, item: T, callback: Callable[[T], str]) -> str:
114 return ""
117class ConcreteHasProperty2:
118 @property
119 def f(self) -> Self:
120 return self
122 def m(self, item: int, callback: Callable[[int], str]) -> str:
123 return ""
126class ConcreteHasProperty3:
127 @property
128 def f(self) -> int:
129 return 0
131 def m(self, item: int, callback: Callable[[int], str]) -> str:
132 return ""
135class ConcreteHasProperty4:
136 @property
137 def f(self) -> Self:
138 return self
140 def m(self, item: str, callback: Callable[[int], str]) -> str:
141 return ""
144hp1: HasPropertyProto = ConcreteHasProperty1() # OK
145hp2: HasPropertyProto = ConcreteHasProperty2() # E
Expected a ty diagnostic for this line
146hp3: HasPropertyProto = ConcreteHasProperty3() # E
Expected a ty diagnostic for this line
147hp4: HasPropertyProto = ConcreteHasProperty4() # E
Expected a ty diagnostic for this line