← Back to index

classes_classvar.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 15
FP: 0
FN: 2
Optional: 0 / 1
1"""
2Tests the typing.ClassVar special form.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/class-compat.html#classvar
6
7from typing import (
8 Annotated,
9 Any,
10 Callable,
11 ClassVar,
12 Final,
13 Generic,
14 ParamSpec,
15 Protocol,
16 TypeAlias,
17 TypeVar,
18 TypeVarTuple,
19 assert_type,
20 cast,
21)
22from typing import ClassVar as CV
25var1 = 3
27P = ParamSpec("P")
28T = TypeVar("T")
29Ts = TypeVarTuple("Ts")
31# This is currently commented out because it causes mypy to crash.
32# class ClassA(Generic[T, P, Unpack[Ts]]):
35class ClassA(Generic[T, P]):
36 # > ClassVar accepts only a single argument that should be a valid type
38 bad1: ClassVar[int, str] = cast(Any, 0) # E: too many arguments
[invalid-type-form] Type qualifier `typing.ClassVar` expected exactly 1 argument, got 2
39 bad2: CV[3] = cast(Any, 0) # E: invalid type
[invalid-type-form] Int literals are not allowed in this context in a type expression
40 bad3: CV[var] = cast(Any, 0) # E: invalid type
[unresolved-reference] Name `var` used when not defined
42 # > Note that a ClassVar parameter cannot include any type variables,
43 # > regardless of the level of nesting.
45 bad4: ClassVar[T] = cast(Any, 0) # E: cannot use TypeVar
[invalid-type-form] `ClassVar` cannot contain type variables
46 bad5: ClassVar[list[T]] = cast(Any, 0) # E: cannot use TypeVar
[invalid-type-form] `ClassVar` cannot contain type variables
47 bad6: ClassVar[Callable[P, Any]] = cast(Any, 0) # E: cannot use ParamSpec
[invalid-type-form] `ClassVar` cannot contain type variables
49 # This is currently commented out because it causes mypy to crash.
50 # bad7: ClassVar[tuple[*Ts]] # E: cannot use TypeVarTuple
52 bad8: ClassVar[list[str]] = {} # E: type violation in initialization
[invalid-assignment] Object of type `dict[Unknown, Unknown]` is not assignable to `list[str]`
54 bad9: Final[ClassVar[int]] = 3 # E: ClassVar cannot be nested with Final
Expected a ty diagnostic for this line
55 bad10: list[ClassVar[int]] = [] # E: ClassVar cannot be nested
[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions)
57 good1: CV[int] = 1
58 good2: ClassVar[list[str]] = []
59 good3: ClassVar[Any] = 1
60 # > If an assigned value is available, the type should be inferred as some type
61 # > to which this value is assignable.
62 # Here, type checkers could infer good4 as `float` or `Any`, for example.
63 good4: ClassVar = 3.1
64 # > If the `ClassVar` qualifier is used without any assigned value, the type
65 # > should be inferred as `Any`:
66 good5: ClassVar # E?: Type checkers may error on uninitialized ClassVar
67 good6: Annotated[ClassVar[list[int]], ""] = []
69 def method1(self, a: ClassVar[int]): # E: ClassVar not allowed here
[invalid-type-form] `ClassVar` is not allowed in function parameter annotations
70 x: ClassVar[str] = "" # E: ClassVar not allowed here
[invalid-type-form] `ClassVar` annotations are only allowed in class-body scopes
71 self.xx: ClassVar[str] = "" # E: ClassVar not allowed here
[invalid-type-form] `ClassVar` annotations are not allowed for non-name targets
73 def method2(self) -> ClassVar[int]: # E: ClassVar not allowed here
[invalid-type-form] `ClassVar` is not allowed in function return type annotations
74 return 3
77bad11: ClassVar[int] = 3 # E: ClassVar not allowed here
[invalid-type-form] `ClassVar` annotations are only allowed in class-body scopes
78bad12: TypeAlias = ClassVar[str] # E: ClassVar not allowed here
Expected a ty diagnostic for this line
81assert_type(ClassA.good1, int)
82assert_type(ClassA.good2, list[str])
83assert_type(ClassA.good3, Any)
84assert_type(ClassA.good5, Any)
87class BasicStarship:
88 captain: str = "Picard" # Instance variable with default
89 damage: int # Instance variable without default
90 stats: ClassVar[dict[str, int]] = {} # Class variable
92 def __init__(self, damage: int) -> None:
93 self.damage = damage
96class Starship:
97 captain: str = "Picard"
98 damage: int
99 stats: ClassVar[dict[str, int]] = {}
101 def __init__(self, damage: int, captain: str | None = None):
102 self.damage = damage
103 if captain:
104 self.captain = captain
106 def hit(self):
107 Starship.stats["hits"] = Starship.stats.get("hits", 0) + 1
110enterprise_d = Starship(3000)
111enterprise_d.stats = {} # E: cannot access via instance
[invalid-attribute-access] Cannot assign to ClassVar `stats` from an instance of type `Starship`
112Starship.stats = {} # OK
115# > As a matter of convenience (and convention), instance variables can be
116# > annotated in __init__ or other methods, rather than in the class.
119class Box(Generic[T]):
120 def __init__(self, content) -> None:
121 self.content: T = content
124# Tests for ClassVar in Protocol
127class ProtoA(Protocol):
128 x: ClassVar[str]
129 y: ClassVar[str]
130 z: CV = [""]
133class ProtoAImpl:
134 x = ""
136 def __init__(self) -> None:
137 self.y = ""
140a: ProtoA = ProtoAImpl() # E: y is not a ClassVar
[invalid-assignment] Object of type `ProtoAImpl` is not assignable to `ProtoA`
143class ProtoB(Protocol):
144 x: int = 3
145 y: int
146 z: ClassVar[int]
148 @classmethod
149 def method1(cls) -> None:
150 return None
152 @staticmethod
153 def method2() -> None:
154 return None
157class ProtoBImpl(ProtoB):
158 y = 0
159 z = 0
162b_x: int = ProtoBImpl.x
163b_y: int = ProtoBImpl.y
164b_z: int = ProtoBImpl.z
166b_m1 = ProtoBImpl.method1
167b_m2 = ProtoBImpl.method2