← Back to index

generics_variance_inference.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 23
FP: 0
FN: 0
Optional: 0 / 0
1"""
2Tests variance inference for type parameters.
3"""
4
5# Specification: https://peps.python.org/pep-0695/#variance-inference
6
7from dataclasses import dataclass
8
9# T1 should be invariant
10# T2 should be contravariant
11# T3 should be covariant
12from typing import Generic, Iterator, Sequence, TypeVar
15class ClassA[T1, T2, T3](list[T1]):
16 def method1(self, a: T2) -> None:
17 ...
19 def method2(self) -> T3:
20 raise NotImplementedError
23def func_a(p1: ClassA[float, int, int], p2: ClassA[int, float, float]):
24 v1: ClassA[int, int, int] = p1 # E
[invalid-assignment] Object of type `ClassA[int | float, int, int]` is not assignable to `ClassA[int, int, int]`
25 v2: ClassA[float, float, int] = p1 # E
[invalid-assignment] Object of type `ClassA[int | float, int, int]` is not assignable to `ClassA[int | float, int | float, int]`
26 v3: ClassA[float, int, float] = p1 # OK
28 v4: ClassA[int, int, int] = p2 # E
[invalid-assignment] Object of type `ClassA[int, int | float, int | float]` is not assignable to `ClassA[int, int, int]`
29 v5: ClassA[int, int, float] = p2 # OK
32class ShouldBeCovariant1[T]:
33 def __getitem__(self, index: int) -> T:
34 raise NotImplementedError
36 def __iter__(self) -> Iterator[T]:
37 raise NotImplementedError
40vco1_1: ShouldBeCovariant1[float] = ShouldBeCovariant1[int]() # OK
41vco1_2: ShouldBeCovariant1[int] = ShouldBeCovariant1[float]() # E
[invalid-assignment] Object of type `ShouldBeCovariant1[int | float]` is not assignable to `ShouldBeCovariant1[int]`
44class ShouldBeCovariant2[T](ShouldBeCovariant1[T]):
45 pass
48vco2_1: ShouldBeCovariant2[float] = ShouldBeCovariant2[int]() # OK
49vco2_2: ShouldBeCovariant2[int] = ShouldBeCovariant2[float]() # E
[invalid-assignment] Object of type `ShouldBeCovariant2[int | float]` is not assignable to `ShouldBeCovariant2[int]`
52class ShouldBeCovariant3[T]:
53 def method1(self) -> "ShouldBeCovariant2[T]":
54 raise NotImplementedError
57vco3_1: ShouldBeCovariant3[float] = ShouldBeCovariant3[int]() # OK
58vco3_2: ShouldBeCovariant3[int] = ShouldBeCovariant3[float]() # E
[invalid-assignment] Object of type `ShouldBeCovariant3[int | float]` is not assignable to `ShouldBeCovariant3[int]`
61@dataclass(frozen=True)
62class ShouldBeCovariant4[T]:
63 x: T
66vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK
67vo4_2: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1) # E
[invalid-assignment] Object of type `ShouldBeCovariant4[int | float]` is not assignable to `ShouldBeCovariant4[int]`
70class ShouldBeCovariant5[T]:
71 def __init__(self, x: T) -> None:
72 self._x = x
74 @property
75 def x(self) -> T:
76 return self._x
79vo5_1: ShouldBeCovariant5[float] = ShouldBeCovariant5[int](1) # OK
80vo5_2: ShouldBeCovariant5[int] = ShouldBeCovariant5[float](1) # E
[invalid-assignment] Object of type `ShouldBeCovariant5[int | float]` is not assignable to `ShouldBeCovariant5[int]`
83class ShouldBeInvariant1[T]:
84 def __init__(self, value: T) -> None:
85 self._value = value
87 @property
88 def value(self) -> T:
89 return self._value
91 @value.setter
92 def value(self, value: T):
93 self._value = value
96vinv1_1: ShouldBeInvariant1[float] = ShouldBeInvariant1[int](1) # E
[invalid-assignment] Object of type `ShouldBeInvariant1[int]` is not assignable to `ShouldBeInvariant1[int | float]`
97vinv1_2: ShouldBeInvariant1[int] = ShouldBeInvariant1[float](1.1) # E
[invalid-assignment] Object of type `ShouldBeInvariant1[int | float]` is not assignable to `ShouldBeInvariant1[int]`
100class ShouldBeInvariant2[T]:
101 def __init__(self, value: T) -> None:
102 self._value = value
104 def get_value(self) -> T:
105 return self._value
107 def set_value(self, value: T):
108 self._value = value
111vinv2_1: ShouldBeInvariant2[float] = ShouldBeInvariant2[int](1) # E
[invalid-assignment] Object of type `ShouldBeInvariant2[int]` is not assignable to `ShouldBeInvariant2[int | float]`
112vinv2_2: ShouldBeInvariant2[int] = ShouldBeInvariant2[float](1.1) # E
[invalid-assignment] Object of type `ShouldBeInvariant2[int | float]` is not assignable to `ShouldBeInvariant2[int]`
115class ShouldBeInvariant3[K, V](dict[K, V]):
116 pass
119vinv3_1: ShouldBeInvariant3[float, str] = ShouldBeInvariant3[int, str]() # E
[invalid-assignment] Object of type `ShouldBeInvariant3[int, str]` is not assignable to `ShouldBeInvariant3[int | float, str]`
120vinv3_2: ShouldBeInvariant3[int, str] = ShouldBeInvariant3[float, str]() # E
[invalid-assignment] Object of type `ShouldBeInvariant3[int | float, str]` is not assignable to `ShouldBeInvariant3[int, str]`
121vinv3_3: ShouldBeInvariant3[str, float] = ShouldBeInvariant3[str, int]() # E
[invalid-assignment] Object of type `ShouldBeInvariant3[str, int]` is not assignable to `ShouldBeInvariant3[str, int | float]`
122vinv3_4: ShouldBeInvariant3[str, int] = ShouldBeInvariant3[str, float]() # E
[invalid-assignment] Object of type `ShouldBeInvariant3[str, int | float]` is not assignable to `ShouldBeInvariant3[str, int]`
125@dataclass
126class ShouldBeInvariant4[T]:
127 x: T
130vinv4_1: ShouldBeInvariant4[float] = ShouldBeInvariant4[int](1) # E
[invalid-assignment] Object of type `ShouldBeInvariant4[int]` is not assignable to `ShouldBeInvariant4[int | float]`
133class ShouldBeInvariant5[T]:
134 def __init__(self, x: T) -> None:
135 self.x = x
138vinv5_1: ShouldBeInvariant5[float] = ShouldBeInvariant5[int](1) # E
[invalid-assignment] Object of type `ShouldBeInvariant5[int]` is not assignable to `ShouldBeInvariant5[int | float]`
141class ShouldBeContravariant1[T]:
142 def __init__(self, value: T) -> None:
143 pass
145 def set_value(self, value: T) -> None:
146 pass
149vcontra1_1: ShouldBeContravariant1[float] = ShouldBeContravariant1[int](1) # E
[invalid-assignment] Object of type `ShouldBeContravariant1[int]` is not assignable to `ShouldBeContravariant1[int | float]`
150vcontra1_2: ShouldBeContravariant1[int] = ShouldBeContravariant1[float](1.2) # OK
153# Test the case where a class with inferred variance derives from
154# a traditional class that doesn't use inferred variance.
156T = TypeVar("T")
157T_co = TypeVar("T_co", covariant=True)
158T_contra = TypeVar("T_contra", contravariant=True)
161class Parent_Invariant(Generic[T]):
162 pass
165class ShouldBeInvariant6[T](Parent_Invariant[T]):
166 pass
169a1: ShouldBeInvariant6[int] = ShouldBeInvariant6[float]() # E
[invalid-assignment] Object of type `ShouldBeInvariant6[int | float]` is not assignable to `ShouldBeInvariant6[int]`
170a2: ShouldBeInvariant6[float] = ShouldBeInvariant6[int]() # E
[invalid-assignment] Object of type `ShouldBeInvariant6[int]` is not assignable to `ShouldBeInvariant6[int | float]`
173class Parent_Covariant(Generic[T_co]):
174 pass
177class ShouldBeCovariant6[T](Parent_Covariant[T]):
178 pass
181b1: ShouldBeCovariant6[int] = ShouldBeCovariant6[float]() # E
[invalid-assignment] Object of type `ShouldBeCovariant6[int | float]` is not assignable to `ShouldBeCovariant6[int]`
182b2: ShouldBeCovariant6[float] = ShouldBeCovariant6[int]() # OK
185class Parent_Contravariant(Generic[T_contra]):
186 pass
189class ShouldBeContravariant2[T](Parent_Contravariant[T]):
190 pass
193c1: ShouldBeContravariant2[int] = ShouldBeContravariant2[float]() # OK
194c2: ShouldBeContravariant2[float] = ShouldBeContravariant2[int]() # E
[invalid-assignment] Object of type `ShouldBeContravariant2[int]` is not assignable to `ShouldBeContravariant2[int | float]`