← Back to index

generics_variance.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 1
FP: 0
FN: 16
Optional: 0 / 0
1"""
2Tests the handling and enforcement of TypeVar variance.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#variance
6
7from typing import Sequence, TypeVar, Generic
8from collections.abc import Iterable, Iterator
9
10# > To facilitate the declaration of container types where covariant or
11# > contravariant type checking is acceptable, type variables accept
12# > keyword arguments covariant=True or contravariant=True. At most one of
13# > these may be passed.
14X1 = TypeVar("X1", covariant=True, contravariant=True) # E
[invalid-legacy-type-variable] A `TypeVar` cannot be both covariant and contravariant
17T = TypeVar("T")
18T_co = TypeVar("T_co", covariant=True)
19T_contra = TypeVar("T_contra", contravariant=True)
22class ImmutableList(Generic[T_co]):
23 def __init__(self, items: Iterable[T_co]) -> None:
24 ...
26 def __iter__(self) -> Iterator[T_co]:
27 raise NotImplementedError
30class Employee:
31 ...
34class Manager(Employee):
35 ...
38managers: ImmutableList[Manager] = ImmutableList([Manager()])
39employees: ImmutableList[Employee] = managers # OK
42E = TypeVar("E", bound=Employee)
45def dump_employee(e: E) -> E:
46 return e
49dump_employee(Manager()) # OK
52B_co = TypeVar("B_co", covariant=True)
55# > Variance has no meaning, and should therefore be ignored by type checkers,
56# > if a type variable is bound to a generic function or type alias.
57def func(x: list[B_co]) -> B_co: # OK
58 raise NotImplementedError
61class Co(Generic[T_co]):
62 ...
65class Contra(Generic[T_contra]):
66 ...
69class Inv(Generic[T]):
70 ...
73class CoContra(Generic[T_co, T_contra]):
74 ...
77class Class1(Inv[T_co]): # E: Inv requires invariant TypeVar
Expected a ty diagnostic for this line
78 pass
81class Class2(Inv[T_contra]): # E: Inv requires invariant TypeVar
Expected a ty diagnostic for this line
82 pass
85class Co_Child1(Co[T_co]): # OK
86 ...
89class Co_Child2(Co[T]): # OK
90 ...
93class Co_Child3(Co[T_contra]): # E: Co requires covariant
Expected a ty diagnostic for this line
94 ...
97class Contra_Child1(Contra[T_contra]): # OK
98 ...
101class Contra_Child2(Contra[T]): # OK
102 ...
105class Contra_Child3(Contra[T_co]): # E: Contra requires contravariant
Expected a ty diagnostic for this line
106 ...
109class Contra_Child4(Contra[Co[T_contra]]): # OK
110 ...
113class Contra_Child5(Contra[Co[T_co]]): # E: Contra requires contravariant
Expected a ty diagnostic for this line
114 ...
117class Contra_Child6(Contra[Co[T]]): # OK
118 ...
121class CoContra_Child1(CoContra[T_co, T_contra]): # OK
122 ...
125class CoContra_Child2( # E[CoContra_Child2]: Second type arg must be contravariant
Expected a ty diagnostic for this line (tag 'CoContra_Child2')
126 CoContra[T_co, T_co] # E[CoContra_Child2]: Second type arg must be contravariant
Expected a ty diagnostic for this line (tag 'CoContra_Child2')
127):
128 ...
131class CoContra_Child3( # E[CoContra_Child3]: First type arg must be covariant
Expected a ty diagnostic for this line (tag 'CoContra_Child3')
132 CoContra[T_contra, T_contra] # E[CoContra_Child3]: First type arg must be covariant
Expected a ty diagnostic for this line (tag 'CoContra_Child3')
133):
134 ...
137class CoContra_Child4(CoContra[T, T]): # OK
138 ...
141class CoContra_Child5( # E[CoContra_Child5]: Second type arg must be contravariant
Expected a ty diagnostic for this line (tag 'CoContra_Child5')
142 CoContra[Co[T_co], Co[T_co]] # E[CoContra_Child5]: Second type arg must be contravariant
Expected a ty diagnostic for this line (tag 'CoContra_Child5')
143):
144 ...
147class CoToContra(Contra[Co[T_contra]]): # OK
148 ...
151class ContraToContra(Contra[Contra[T_co]]): # OK
152 ...
155class CoToCo(Co[Co[T_co]]): # OK
156 ...
159class ContraToCo(Co[Contra[T_contra]]): # OK
160 ...
163class CoToContraToContra(Contra[Co[Contra[T_contra]]]): # E
Expected a ty diagnostic for this line
164 ...
167class ContraToContraToContra(Contra[Contra[Contra[T_co]]]): # E
Expected a ty diagnostic for this line
168 ...
171Co_TA = Co[T_co]
172Contra_TA = Contra[T_contra]
175class CoToContra_WithTA(Contra_TA[Co_TA[T_contra]]): # OK
176 ...
179class ContraToContra_WithTA(Contra_TA[Contra_TA[T_co]]): # OK
180 ...
183class CoToCo_WithTA(Co_TA[Co_TA[T_co]]): # OK
184 ...
187class ContraToCo_WithTA(Co_TA[Contra_TA[T_contra]]): # OK
188 ...
191class CoToContraToContra_WithTA(Contra_TA[Co_TA[Contra_TA[T_contra]]]): # E
Expected a ty diagnostic for this line
192 ...
195class ContraToContraToContra_WithTA( # E[ContraToContraToContra_WithTA]
Expected a ty diagnostic for this line (tag 'ContraToContraToContra_WithTA')
196 Contra_TA[Contra_TA[Contra_TA[T_co]]] # E[ContraToContraToContra_WithTA]
Expected a ty diagnostic for this line (tag 'ContraToContraToContra_WithTA')
197):
198 ...