@@ -19,10 +19,6 @@ FINAL_A: Final[int] = 1
1919FINAL_B : Annotated[Final[int ], " the annotation for FINAL_B" ] = 1
2020FINAL_C : Final[Annotated[int , " the annotation for FINAL_C" ]] = 1
2121FINAL_D : " Final[int]" = 1
22- # Note: Some type checkers do not support a separate declaration and
23- # assignment for `Final` symbols, but it's possible to support this in
24- # ty, and is useful for code that declares symbols `Final` inside
25- # `if TYPE_CHECKING` blocks.
2622FINAL_F : Final[int ]
2723FINAL_F = 1
2824
@@ -52,7 +48,7 @@ reveal_type(FINAL_D) # revealed: int
5248reveal_type(FINAL_F ) # revealed: int
5349```
5450
55- ### ` Final ` without a type
51+ ### Bare ` Final ` without a type
5652
5753When a symbol is qualified with ` Final ` but no type is specified, the type is inferred from the
5854right-hand side of the assignment. We do not union the inferred type with ` Unknown ` , because the
@@ -231,7 +227,96 @@ FINAL_LIST: Final[list[int]] = [1, 2, 3]
231227FINAL_LIST [0 ] = 4
232228```
233229
234- ## Too many arguments
230+ ## Overriding in subclasses
231+
232+ When a symbol is qualified with ` Final ` in a class, it cannot be overridden in subclasses.
233+
234+ ``` py
235+ from typing import Final
236+
237+ class Base :
238+ FINAL_A : Final[int ] = 1
239+ FINAL_B : Final[int ] = 1
240+ FINAL_C : Final = 1
241+
242+ class Derived (Base ):
243+ # TODO : This should be an error
244+ FINAL_A = 2
245+ # TODO : This should be an error
246+ FINAL_B : Final[int ] = 2
247+ # TODO : This should be an error
248+ FINAL_C = 2
249+ ```
250+
251+ ## Syntax and usage
252+
253+ ### Legal syntactical positions
254+
255+ Final may only be used in assignments or variable annotations. Using it in any other position is an
256+ error.
257+
258+ ``` py
259+ from typing import Final, ClassVar, Annotated
260+
261+ LEGAL_A : Final[int ] = 1
262+ LEGAL_B : Final = 1
263+ LEGAL_C : Final[int ]
264+ LEGAL_C = 1
265+ LEGAL_D : Final
266+ LEGAL_D = 1
267+
268+ class C :
269+ LEGAL_E : ClassVar[Final[int ]] = 1
270+ LEGAL_F : Final[ClassVar[int ]] = 1
271+ LEGAL_G : Annotated[Final[ClassVar[int ]], " metadata" ] = 1
272+
273+ def __init__ (self ):
274+ self .LEGAL_H : Final[int ] = 1
275+ self .LEGAL_I : Final[int ]
276+ self .LEGAL_I = 1
277+
278+ # TODO : This should be an error
279+ def f (ILLEGAL : Final[int ]) -> None :
280+ pass
281+
282+ # TODO : This should be an error
283+ def f () -> Final[None ]: ...
284+
285+ # TODO : This should be an error
286+ class Foo (Final[tuple[int ]]): ...
287+
288+ # TODO : Show `Unknown` instead of `@Todo` type in the MRO; or ignore `Final` and show the MRO as if `Final` was not there
289+ # revealed: tuple[<class 'Foo'>, @Todo(Inference of subscript on special form), <class 'object'>]
290+ reveal_type(Foo.__mro__ )
291+ ```
292+
293+ ### Attribute assignment outside ` __init__ `
294+
295+ Qualifying an instance attribute with ` Final ` outside of ` __init__ ` is not allowed. The instance
296+ attribute must be assigned only once, when the instance is created.
297+
298+ ``` py
299+ from typing import Final
300+
301+ class C :
302+ def some_method (self ):
303+ # TODO : This should be an error
304+ self .x: Final[int ] = 1
305+ ```
306+
307+ ### ` Final ` in loops
308+
309+ Using ` Final ` in a loop is not allowed.
310+
311+ ``` py
312+ from typing import Final
313+
314+ for _ in range (10 ):
315+ # TODO : This should be an error
316+ i: Final[int ] = 1
317+ ```
318+
319+ ### Too many arguments
235320
236321``` py
237322from typing import Final
@@ -241,39 +326,58 @@ class C:
241326 x: Final[int , str ] = 1
242327```
243328
244- ## Illegal ` Final ` in type expression
329+ ### Illegal ` Final ` in type expression
245330
246331``` py
247332from typing import Final
248333
334+ # error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
335+ x: list[Final[int ]] = [] # Error!
336+
249337class C :
250- # error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
338+ # error: [invalid-type-form]
251339 x: Final | int
252340
253- # error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
341+ # error: [invalid-type-form]
254342 y: int | Final[str ]
255343```
256344
257345## No assignment
258346
347+ Some type checkers do not support a separate declaration and assignment for ` Final ` symbols, but
348+ it's possible to support this in ty, and is useful for code that declares symbols ` Final ` inside
349+ ` if TYPE_CHECKING ` blocks.
350+
351+ ### Basic
352+
259353``` py
260354from typing import Final
261355
262356DECLARED_THEN_BOUND : Final[int ]
263357DECLARED_THEN_BOUND = 1
264358```
265359
266- ## No assignment for bare ` Final `
360+ ### No assignment
267361
268362``` py
269363from typing import Final
270364
271365# TODO : This should be an error
272- NO_RHS : Final
366+ NO_ASSIGNMENT_A : Final
367+ # TODO : This should be an error
368+ NO_ASSIGNMENT_B : Final[int ]
273369
274370class C :
275371 # TODO : This should be an error
276- NO_RHS : Final
372+ NO_ASSIGNMENT_A : Final
373+ # TODO : This should be an error
374+ NO_ASSIGNMENT_B : Final[int ]
375+
376+ # This is okay. `DEFINED_IN_INIT` is defined in `__init__`.
377+ DEFINED_IN_INIT : Final[int ]
378+
379+ def __init__ (self ):
380+ self .DEFINED_IN_INIT = 1
277381```
278382
279383## Full diagnostics
0 commit comments