← Back to index

protocols_recursive.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 0
FP: 1
FN: 0
Optional: 0 / 0
1"""
2Tests the handling of recursive protocols.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#recursive-protocols
6
7
8from typing import Generic, Iterable, Never, Protocol, Self, TypeVar, assert_type
9
10T = TypeVar("T")
11T_co = TypeVar("T_co", covariant=True)
12T_contra = TypeVar("T_contra", contravariant=True)
15class Traversable(Protocol):
16 def leaves(self) -> Iterable["Traversable"]:
17 ...
20class SimpleTree:
21 def leaves(self) -> list["SimpleTree"]:
22 return []
25root: Traversable = SimpleTree() # OK
28class Tree(Generic[T]):
29 def leaves(self) -> list["Tree[T]"]:
30 return []
33def walk(graph: Traversable) -> None:
34 pass
37tree: Tree[float] = Tree()
38walk(tree) # OK
41class ProtoA(Protocol[T_co, T_contra]):
42 def method1(self) -> "ProtoA[T_co, T_contra]":
43 ...
45 @classmethod
46 def method2(cls, value: T_contra) -> None:
47 ...
50class ProtoB(Protocol[T_co, T_contra]):
51 def method3(self) -> ProtoA[T_co, T_contra]:
52 ...
55class ImplA:
56 def method1(self) -> Self:
57 return self
59 @classmethod
60 def method2(cls, value: int) -> None:
61 pass
64class ImplB:
65 def method3(self) -> ImplA:
66 return ImplA()
68 def method1(self) -> Self:
69 return self
71 @classmethod
72 def method2(cls: type[ProtoB[object, T]], value: list[T]) -> None:
73 pass
76def func1(x: ProtoA[Never, T]) -> T:
77 raise NotImplementedError
80v1 = func1(ImplB())
81assert_type(v1, list[int])
Unexpected error [type-assertion-failure] Type `list[int]` does not match asserted type `Unknown`