← Back to index

qualifiers_final_annotation.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 21
FP: 0
FN: 5
Optional: 0 / 0
1"""
2Tests the typing.Final special form.
3"""
4
5from typing import ClassVar, Final, Literal, NamedTuple, assert_type
6
7# Specification: https://typing.readthedocs.io/en/latest/spec/qualifiers.html#id1
8
9ID1: Final[int] = 1
11ID2: Final = 1
12assert_type(ID2, Literal[1])
14# > If the right hand side is omitted, there must be an explicit type argument to Final.
16BAD1: Final # E: missing assignment
[final-without-value] `Final` symbol `BAD1` is not assigned a value
18BAD2: Final[str, int] = "" # E: only one type argument allowed
[invalid-type-form] Type qualifier `typing.Final` expected exactly 1 argument, got 2
20# > There can be at most one final declaration per module or class for a given
21# > attribute.
23# > As self.id: Final = 1 (also optionally with a type in square brackets).
24# > This is allowed only in __init__ methods, so that the final instance
25# > attribute is assigned only once when an instance is created.
28class ClassA:
29 ID1: Final = 1
31 # > In class bodies and stub files you can omit the right hand side and just
32 # > write ID: Final[float]. If the right hand side is omitted, there must be
33 # > an explicit type argument to Final.
34 ID2: Final # E: missing initialization
[final-without-value] `Final` symbol `ID2` is not assigned a value
36 # > A final attribute declared in a class body without an initializer must
37 # > be initialized in the __init__ method (except in stub files):
38 ID3: Final[int] # E: missing initialization
[final-without-value] `Final` symbol `ID3` is not assigned a value
40 ID4: Final[int] # OK because initialized in __init__
42 ID5: Final[int] = 0
44 ID6: Final[int]
46 ID7: Final[int] = 0
48 def __init__(self, cond: bool) -> None:
49 self.id1: Final = 1
50 self.id2: Final[int] = 1
52 self.ID4 = 1
54 self.ID5 = 0 # E: Already initialized
[invalid-assignment] Cannot assign to final attribute `ID5` in `__init__` because it already has a value at class level
56 if cond:
57 self.ID6 = 1
58 else:
59 self.ID6 = 2
61 def method1(self) -> None:
62 self.id3: Final = 1 # E: not allowed outside of __init__ method.
Expected a ty diagnostic for this line
63 self.id4: Final[int] = 1 # E: not allowed outside of __init__ method.
Expected a ty diagnostic for this line
65 self.ID7 = 0 # E: cannot modify Final value
[invalid-assignment] Cannot assign to final attribute `ID7` on type `Self@method1`
67 self.ID7 += 1 # E: cannot modify Final value
Expected a ty diagnostic for this line
70RATE: Final = 3000
71RATE = 300 # E: Cannot redefine Final value
[invalid-assignment] Reassignment of `Final` symbol `RATE` is not allowed: Symbol later reassigned here
73# > There can’t be separate class-level and instance-level constants
74# > with the same name.
77class ClassB:
78 DEFAULT_ID: Final = 0
81ClassB.DEFAULT_ID = 0 # E: Cannot redefined value
[invalid-assignment] Cannot assign to final attribute `DEFAULT_ID` on type `<class 'ClassB'>`
84# > A type checker should prevent final attributes from being overridden in a subclass:
87class ClassC:
88 BORDER_WIDTH: Final = 2.5
90 __private: Final = 0
93class ClassCChild(ClassC):
94 BORDER_WIDTH = 2.5 # E: Cannot override Final value
[override-of-final-variable] Cannot override final variable `BORDER_WIDTH` from superclass `ClassC`
96 __private = 0 # OK
99# > Type checkers should infer a final attribute that is initialized in a class
100# > body as being a class variable. Variables should not be annotated with both
101# > ClassVar and Final. (Except in a dataclass; see dataclasses_final.py.)
104class ClassD:
105 VALUE1: Final = 1
107 VALUE2: ClassVar[Final] = 1 # E: Final cannot be used with ClassVar
Expected a ty diagnostic for this line
108 VALUE3: Final[ClassVar] = 1 # E: Final cannot be used with ClassVar
Expected a ty diagnostic for this line
111ClassD.VALUE1 # OK
114# > Final may only be used as the outermost type in assignments or variable
115# > annotations. Using it in any other position is an error. In particular,
116# > Final can’t be used in annotations for function arguments.
118x: list[Final[int]] = [] # E
[invalid-type-form] Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)
121def func1(x: Final[list[int]]) -> None: # E
[invalid-type-form] `Final` is not allowed in function parameter annotations
122 ...
125# > Type checkers should treat uses of a final name that was initialized with
126# > a literal as if it was replaced by the literal. For example, the following
127# > should be allowed:
129X: Final = "x"
130Y: Final = "y"
131N = NamedTuple("N", [(X, int), (Y, int)])
133N(x=3, y=4) # OK
134N(a=1) # E
[missing-argument] No arguments provided for required parameters `x`, `y` [unknown-argument] Argument `a` does not match any known parameter
135N(x="", y="") # E
[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` [invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]`
138def func2() -> None:
139 global ID1
141 ID1 = 2 # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `ID1` is not allowed: Reassignment of `Final` symbol
143 x: Final = 3
145 x += 1 # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
147 a = (x := 4) # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
149 for x in [1, 2, 3]: # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
150 pass
152 with open("FileName") as x: # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
153 pass
155 (a, x) = (1, 2) # E: cannot modify Final value
[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here
158# > If a module declares a ``Final`` variable and another module imports that
159# > variable in an import statement by name or wildcard, the imported symbol
160# > inherits the ``Final`` type qualifier. Any attempt to assign a different value
161# > to this symbol should be flagged as an error by a type checker.
164from _qualifiers_final_annotation_1 import TEN
166TEN = 9 # E: Cannot redefine Final value
[invalid-assignment] Reassignment of `Final` symbol `TEN` is not allowed: Reassignment of `Final` symbol
168from _qualifiers_final_annotation_2 import *
170PI = 3.14159 # E: Cannot redefine Final value
[invalid-assignment] Reassignment of `Final` symbol `PI` is not allowed: Reassignment of `Final` symbol