← Back to index

protocols_class_objects.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 1
FP: 1
FN: 7
Optional: 0 / 0
1"""
2Tests the handling of class objects as implementations of a protocol.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#type-and-class-objects-vs-protocols
6
7# > Variables and parameters annotated with Type[Proto] accept only concrete
8# (non-protocol) subtypes of Proto.
9
10from abc import abstractmethod
11from typing import Any, ClassVar, Protocol
14class Proto(Protocol):
15 @abstractmethod
16 def meth(self) -> int:
17 ...
20class Concrete:
21 def meth(self) -> int:
22 return 42
25def fun(cls: type[Proto]) -> int:
26 return cls().meth() # OK
29fun(Proto) # E
Expected a ty diagnostic for this line
30fun(Concrete) # OK
33var: type[Proto]
34var = Proto # E
Expected a ty diagnostic for this line
35var = Concrete # OK
36var().meth() # OK
39# > A class object is considered an implementation of a protocol if accessing
40# > all members on it results in types compatible with the protocol members.
43class ProtoA1(Protocol):
44 def method1(self, x: int) -> int:
45 ...
48class ProtoA2(Protocol):
49 def method1(_self, self: Any, x: int) -> int:
50 ...
53class ConcreteA:
54 def method1(self, x: int) -> int:
55 return 0
58pa1: ProtoA1 = ConcreteA # E: signatures don't match
[invalid-assignment] Object of type `<class 'ConcreteA'>` is not assignable to `ProtoA1`
59pa2: ProtoA2 = ConcreteA # OK
Unexpected error [invalid-assignment] Object of type `<class 'ConcreteA'>` is not assignable to `ProtoA2`
62class ProtoB1(Protocol):
63 @property
64 def prop1(self) -> int:
65 ...
68class ConcreteB:
69 @property
70 def prop1(self) -> int:
71 return 0
74pb1: ProtoB1 = ConcreteB # E
Expected a ty diagnostic for this line
77class ProtoC1(Protocol):
78 attr1: ClassVar[int]
81class ProtoC2(Protocol):
82 attr1: int
85class ConcreteC1:
86 attr1: ClassVar[int] = 1
89class ConcreteC2:
90 attr1: int = 1
93class CMeta(type):
94 attr1: int
96 def __init__(self, attr1: int) -> None:
97 self.attr1 = attr1
100class ConcreteC3(metaclass=CMeta):
101 pass
104pc1: ProtoC1 = ConcreteC1 # E
Expected a ty diagnostic for this line
105pc2: ProtoC2 = ConcreteC1 # OK
106pc3: ProtoC1 = ConcreteC2 # E
Expected a ty diagnostic for this line
107pc4: ProtoC2 = ConcreteC2 # E
Expected a ty diagnostic for this line
108pc5: ProtoC1 = ConcreteC3 # E
Expected a ty diagnostic for this line
109pc6: ProtoC2 = ConcreteC3 # OK