2Tests the basic definition rules for protocols.
5# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#defining-a-protocol
7from abc import abstractmethod
8from typing import Any, ClassVar, Iterable, NamedTuple, Protocol, Sequence
9from dataclasses import dataclass
12class SupportsClose(Protocol):
13 def close(self) -> None:
18 def close(self) -> None:
22def close_all(things: Iterable[SupportsClose]) -> None:
30close_all([1]) # E: 'int' has no 'close' method
[invalid-argument-type] Argument to function `close_all` is incorrect: Expected `Iterable[SupportsClose]`, found `list[int]`
33class Example(Protocol):
34 def first(self) -> int: # This is a protocol member
38 def second(self) -> int: # Method without a default implementation
39 raise NotImplementedError
41 # > Static methods, class methods, and properties are equally allowed in protocols.
48 def fourth(cls) -> int:
52 def fifth(self) -> int:
56# > To define a protocol variable, one can use PEP 526 variable annotations
57# > in the class body. Additional attributes only defined in the body of a
58# > method by assignment via self are not allowed.
61class Template(Protocol):
62 name: str # This is a protocol member
63 value: int = 0 # This one too (with default)
65 def method(self) -> None:
66 self.name = "name" # OK
67 self.temp: list[int] = [] # E: use of self variables not allowed
Expected a ty diagnostic for this line
71 def __init__(self, name: str, value: int) -> None:
75 def method(self) -> None:
79var: Template = Concrete("value", 42) # OK
82# > To distinguish between protocol class variables and protocol instance
83# > variables, the special ClassVar annotation should be used as specified
84# > by PEP 526. By default, protocol variables as defined above are considered
85# > readable and writable. To define a read-only protocol variable, one can
86# > use an (abstract) property.
89class Template2(Protocol):
90 val1: ClassVar[Sequence[int]]
94 val1: ClassVar[Sequence[int]] = [2]
102 val1: Sequence[float] = [2]
106 val1: list[int] = [2]
110 val1: Sequence[int] = [2]
113v2_good1: Template2 = Concrete2_Good1() # OK
114v2_bad1: Template2 = Concrete2_Bad1() # E
[invalid-assignment] Object of type `Concrete2_Bad1` is not assignable to `Template2`
115v2_bad2: Template2 = Concrete2_Bad2() # E
[invalid-assignment] Object of type `Concrete2_Bad2` is not assignable to `Template2`
116v2_bad3: Template2 = Concrete2_Bad3() # E
[invalid-assignment] Object of type `Concrete2_Bad3` is not assignable to `Template2`
117v2_bad4: Template2 = Concrete2_Bad4() # E
Expected a ty diagnostic for this line
120class Template3(Protocol):
124class Concrete3_Good1:
125 val1: Sequence[int] = [0]
128class Concrete3_Good2:
129 def __init__(self) -> None:
130 self.val1: Sequence[int] = [0]
138 val1: ClassVar[Sequence[int]] = [0]
143 def val1(self) -> Sequence[int]:
148 val1: Sequence[float] = [0]
152 val1: list[int] = [0]
155v3_good1: Template3 = Concrete3_Good1() # OK
156v3_bad1: Template3 = Concrete3_Bad1() # E
[invalid-assignment] Object of type `Concrete3_Bad1` is not assignable to `Template3`
157v3_bad2: Template3 = Concrete3_Bad2() # E
Expected a ty diagnostic for this line
158v3_bad3: Template3 = Concrete3_Bad3() # E
Expected a ty diagnostic for this line
159v3_bad4: Template3 = Concrete3_Bad4() # E
[invalid-assignment] Object of type `Concrete3_Bad4` is not assignable to `Template3`
160v3_bad5: Template3 = Concrete3_Bad5() # E
[invalid-assignment] Object of type `Concrete3_Bad5` is not assignable to `Template3`
163class Template4(Protocol):
165 def val1(self) -> Sequence[float]:
169class Concrete4_Good1:
171 def val1(self) -> Sequence[float]:
175class Concrete4_Good2:
177 def val1(self) -> Sequence[int]:
181class Concrete4_Good3:
182 val1: Sequence[float] = [0]
185class Concrete4_Good4:
186 val1: Sequence[int] = [0]
189class Concrete4_Good5:
190 val1: list[float] = [0]
193class Concrete4_Good6(NamedTuple):
194 val1: Sequence[float] = [0]
197@dataclass(frozen=False)
198class Concrete4_Good7:
199 val1: Sequence[float] = [0]
203 def val1(self) -> Sequence[int]: # Not a property
211v4_good1: Template4 = Concrete4_Good1() # OK
212v4_good2: Template4 = Concrete4_Good2() # OK
213v4_good3: Template4 = Concrete4_Good3() # OK
214v4_good4: Template4 = Concrete4_Good4() # OK
215v4_good5: Template4 = Concrete4_Good5() # OK
216v4_good6: Template4 = Concrete4_Good6() # OK
217v4_good7: Template4 = Concrete4_Good7() # OK
218v4_bad1: Template4 = Concrete4_Bad1() # E
Expected a ty diagnostic for this line
219v4_bad2: Template4 = Concrete4_Bad2() # E
[invalid-assignment] Object of type `Concrete4_Bad2` is not assignable to `Template4`
222class Template5(Protocol):
223 def method1(self, a: int, b: int) -> float:
227class Concrete5_Good1:
228 def method1(self, a: float, b: float) -> int:
232class Concrete5_Good2:
233 def method1(self, *args: float, **kwargs: float) -> Any:
237class Concrete5_Good3:
238 def method1(self, a, b) -> Any:
242class Concrete5_Good4:
244 def method1(cls, a: int, b: int) -> float:
248class Concrete5_Good5:
250 def method1(a: int, b: int) -> float:
255 def method1(self, a, c) -> int:
260 def method1(self, a: int, c: int) -> int:
265 def method1(self, *, a: int, b: int) -> float:
270 def method1(self, a: int, b: int, /) -> float:
276 def method1(self, a: int, b: int) -> float:
280v5_good1: Template5 = Concrete5_Good1() # OK
281v5_good2: Template5 = Concrete5_Good2() # OK
282v5_good3: Template5 = Concrete5_Good3() # OK
283v5_good4: Template5 = Concrete5_Good4() # OK
284v5_good5: Template5 = Concrete5_Good5() # OK
285v5_bad1: Template5 = Concrete5_Bad1() # E
[invalid-assignment] Object of type `Concrete5_Bad1` is not assignable to `Template5`
286v5_bad2: Template5 = Concrete5_Bad2() # E
[invalid-assignment] Object of type `Concrete5_Bad2` is not assignable to `Template5`
287v5_bad3: Template5 = Concrete5_Bad3() # E
[invalid-assignment] Object of type `Concrete5_Bad3` is not assignable to `Template5`
288v5_bad4: Template5 = Concrete5_Bad4() # E
[invalid-assignment] Object of type `Concrete5_Bad4` is not assignable to `Template5`
289v5_bad5: Template5 = Concrete5_Bad5() # E
[invalid-assignment] Object of type `Concrete5_Bad5` is not assignable to `Template5`
292class Template6(Protocol):
294 def val1(self) -> Sequence[float]:
298 def val1(self, val: Sequence[float]) -> None:
302class Concrete6_Good1:
304 def val1(self) -> Sequence[float]:
308 def val1(self, val: Sequence[float]) -> None:
312class Concrete6_Good2:
313 val1: Sequence[float] = [0]
316@dataclass(frozen=False)
317class Concrete6_Good3:
318 val1: Sequence[float] = [0]
323 def val1(self) -> Sequence[float]:
327class Concrete6_Bad2(NamedTuple):
328 val1: Sequence[float] = [0]
331@dataclass(frozen=True)
333 val1: Sequence[float] = [0]
336v6_good1: Template6 = Concrete6_Good1() # OK
337v6_good2: Template6 = Concrete6_Good2() # OK
338v6_good3: Template6 = Concrete6_Good3() # OK
339v6_bad1: Template6 = Concrete6_Bad1() # E
Expected a ty diagnostic for this line
340v6_bad2: Template6 = Concrete6_Bad2() # E: named tuple is immutable
Expected a ty diagnostic for this line
341v6_bad3: Template6 = Concrete6_Bad3() # E: dataclass is frozen
Expected a ty diagnostic for this line