← Back to index

callables_protocol.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 17
FP: 0
FN: 0
Optional: 0 / 0
1"""
2Tests handling of callback protocols.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#callback-protocols
6
7from typing import Any, Callable, ParamSpec, Protocol, TypeVar, cast, overload
8
9InputT = TypeVar("InputT", contravariant=True)
10OutputT = TypeVar("OutputT", covariant=True)
13class Proto1(Protocol):
14 def __call__(self, *vals: bytes, max_len: int | None = None) -> list[bytes]:
15 ...
18def cb1_good1(*vals: bytes, max_len: int | None = None) -> list[bytes]:
19 return []
22def cb1_bad1(*vals: bytes, max_items: int | None) -> list[bytes]:
23 return []
26def cb1_bad2(*vals: bytes) -> list[bytes]:
27 return []
30def cb1_bad3(*vals: bytes, max_len: str | None) -> list[bytes]:
31 return []
34cb1: Proto1 = cb1_good1 # OK
35cb1 = cb1_bad1 # E: different names
[invalid-assignment] Object of type `def cb1_bad1(*vals: bytes, *, max_items: int | None) -> list[bytes]` is not assignable to `Proto1`
36cb1 = cb1_bad2 # E: parameter types
[invalid-assignment] Object of type `def cb1_bad2(*vals: bytes) -> list[bytes]` is not assignable to `Proto1`
37cb1 = cb1_bad3 # E: default argument
[invalid-assignment] Object of type `def cb1_bad3(*vals: bytes, *, max_len: str | None) -> list[bytes]` is not assignable to `Proto1`
40class Proto2(Protocol):
41 def __call__(self, *vals: bytes, **kwargs: str) -> None:
42 pass
45def cb2_good1(*a: bytes, **b: str):
46 pass
49def cb2_bad1(*a: bytes):
50 pass
53def cb2_bad2(*a: str, **b: str):
54 pass
57def cb2_bad3(*a: bytes, **b: bytes):
58 pass
61def cb2_bad4(**b: str):
62 pass
65cb2: Proto2 = cb2_good1 # OK
67cb2 = cb2_bad1 # E: missing **kwargs
[invalid-assignment] Object of type `def cb2_bad1(*a: bytes) -> Unknown` is not assignable to `Proto2`
68cb2 = cb2_bad2 # E: parameter type
[invalid-assignment] Object of type `def cb2_bad2(*a: str, **b: str) -> Unknown` is not assignable to `Proto2`
69cb2 = cb2_bad3 # E: parameter type
[invalid-assignment] Object of type `def cb2_bad3(*a: bytes, **b: bytes) -> Unknown` is not assignable to `Proto2`
70cb2 = cb2_bad4 # E: missing parameter
[invalid-assignment] Object of type `def cb2_bad4(**b: str) -> Unknown` is not assignable to `Proto2`
73class Proto3(Protocol):
74 def __call__(self) -> None:
75 pass
78cb3: Proto3 = cb2_good1 # OK
79cb3 = cb2_bad1 # OK
80cb3 = cb2_bad2 # OK
81cb3 = cb2_bad3 # OK
82cb3 = cb2_bad4 # OK
85# A callback protocol with other attributes.
86class Proto4(Protocol):
87 other_attribute: int
89 def __call__(self, x: int) -> None:
90 pass
93def cb4_bad1(x: int) -> None:
94 pass
97var4: Proto4 = cb4_bad1 # E: missing attribute
[invalid-assignment] Object of type `def cb4_bad1(x: int) -> None` is not assignable to `Proto4`
100class Proto5(Protocol):
101 def __call__(self, *, a: int, b: str) -> int:
102 ...
105def cb5_good1(a: int, b: str) -> int:
106 return 0
109cb5: Proto5 = cb5_good1 # OK
112class NotProto6:
113 def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]:
114 return []
117def cb6_bad1(*vals: bytes, max_len: int | None = None) -> list[bytes]:
118 return []
121cb6: NotProto6 = cb6_bad1 # E: NotProto6 isn't a protocol class
[invalid-assignment] Object of type `def cb6_bad1(*vals: bytes, *, max_len: int | None = None) -> list[bytes]` is not assignable to `NotProto6`
124class Proto7(Protocol[InputT, OutputT]):
125 def __call__(self, inputs: InputT) -> OutputT:
126 ...
129class Class7_1:
130 # Test for unannotated parameter.
131 def __call__(self, inputs) -> int:
132 return 5
135cb7_1: Proto7[int, int] = Class7_1() # OK
138class Class7_2:
139 # Test for parameter with type Any.
140 def __call__(self, inputs: Any) -> int:
141 return 5
144cb7_2: Proto7[int, int] = Class7_2() # OK
147class Proto8(Protocol):
148 @overload
149 def __call__(self, x: int) -> int:
150 ...
152 @overload
153 def __call__(self, x: str) -> str:
154 ...
156 def __call__(self, x: Any) -> Any:
157 ...
160def cb8_good1(x: Any) -> Any:
161 return x
164def cb8_bad1(x: int) -> Any:
165 return x
168cb8: Proto8 = cb8_good1 # OK
169cb8 = cb8_bad1 # E: parameter type
[invalid-assignment] Object of type `def cb8_bad1(x: int) -> Any` is not assignable to `Proto8`
172P = ParamSpec("P")
173R = TypeVar("R", covariant=True)
176class Proto9(Protocol[P, R]):
177 other_attribute: int
179 def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
180 ...
183def decorator1(f: Callable[P, R]) -> Proto9[P, R]:
184 converted = cast(Proto9[P, R], f)
185 converted.other_attribute = 1
186 converted.other_attribute = "str" # E: incompatible type
[invalid-assignment] Object of type `Literal["str"]` is not assignable to attribute `other_attribute` of type `int`
187 converted.xxx = 3 # E: unknown attribute
[unresolved-attribute] Unresolved attribute `xxx` on type `Proto9[P@decorator1, R@decorator1]`
188 return converted
191@decorator1
192def cb9_good(x: int) -> str:
193 return ""
196print(cb9_good.other_attribute) # OK
197print(cb9_good.other_attribute2) # E: unknown attribute
[unresolved-attribute] Object of type `Proto9[(x: int), str]` has no attribute `other_attribute2`
199cb9_good(x=3)
202class Proto10(Protocol):
203 __name__: str
204 __module__: str
205 __qualname__: str
206 __annotations__: dict[str, Any]
208 def __call__(self) -> None:
209 ...
212def cb10_good() -> None:
213 pass
216cb10: Proto10 = cb10_good # OK
219class Proto11(Protocol):
220 def __call__(self, x: int, /, y: str) -> Any:
221 ...
224def cb11_good1(x: int, /, y: str, z: None = None) -> Any:
225 pass
228def cb11_good2(x: int, y: str, z: None = None) -> Any:
229 pass
232def cb11_bad1(x: int, y: str, /) -> Any:
233 pass
236cb11: Proto11 = cb11_good1 # OK
237cb11 = cb11_good2 # OK
238cb11 = cb11_bad1 # E: y is position-only
[invalid-assignment] Object of type `def cb11_bad1(x: int, y: str, /) -> Any` is not assignable to `Proto11`
241class Proto12(Protocol):
242 def __call__(self, *args: Any, kwarg0: Any, kwarg1: Any) -> None:
243 ...
246def cb12_good1(*args: Any, kwarg0: Any, kwarg1: Any) -> None:
247 pass
250def cb12_good2(*args: Any, **kwargs: Any) -> None:
251 pass
254def cb12_bad1(*args: Any, kwarg0: Any) -> None:
255 pass
258cb12: Proto12 = cb12_good1 # OK
259cb12 = cb12_good2 # OK
260cb12 = cb12_bad1 # E: missing kwarg1
[invalid-assignment] Object of type `def cb12_bad1(*args: Any, *, kwarg0: Any) -> None` is not assignable to `Proto12`
263class Proto13_Default(Protocol):
264 # Callback with positional parameter with default arg value
265 def __call__(self, path: str = ...) -> str:
266 ...
269class Proto13_NoDefault(Protocol):
270 # Callback with positional parameter but no default arg value
271 def __call__(self, path: str) -> str:
272 ...
275def cb13_default(path: str = "") -> str:
276 return ""
279def cb13_no_default(path: str) -> str:
280 return ""
283cb13_1: Proto13_Default = cb13_default # OK
284cb13_2: Proto13_Default = cb13_no_default # E: no default
[invalid-assignment] Object of type `def cb13_no_default(path: str) -> str` is not assignable to `Proto13_Default`
286cb13_3: Proto13_NoDefault = cb13_default # OK
287cb13_4: Proto13_NoDefault = cb13_no_default # OK
290class Proto14_Default(Protocol):
291 # Callback with keyword parameter with default arg value
292 def __call__(self, *, path: str = ...) -> str:
293 ...
296class Proto14_NoDefault(Protocol):
297 # Callback with keyword parameter with no default arg value
298 def __call__(self, *, path: str) -> str:
299 ...
302def cb14_default(*, path: str = "") -> str:
303 return ""
306def cb14_no_default(*, path: str) -> str:
307 return ""
310cb14_1: Proto14_Default = cb14_default # OK
311cb14_2: Proto14_Default = cb14_no_default # E: no default
[invalid-assignment] Object of type `def cb14_no_default(*, path: str) -> str` is not assignable to `Proto14_Default`
312cb14_3: Proto14_NoDefault = cb14_default # OK
313cb14_4: Proto14_NoDefault = cb14_no_default # OK