| -- Test cases for final qualifier | |
| -- | |
| -- Definitions | |
| [case testFinalDefiningModuleVar] | |
| from typing import Final | |
| x: Final = int() | |
| y: Final[float] = int() | |
| z: Final[int] = int() | |
| bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") | |
| reveal_type(x) # N: Revealed type is "builtins.int" | |
| reveal_type(y) # N: Revealed type is "builtins.float" | |
| reveal_type(z) # N: Revealed type is "builtins.int" | |
| [out] | |
| [case testFinalDefiningInstanceVar] | |
| from typing import Final | |
| class C: | |
| x: Final = int() | |
| y: Final[float] = int() | |
| z: Final[int] = int() | |
| bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") | |
| class D(C): pass | |
| reveal_type(D.x) # N: Revealed type is "builtins.int" | |
| reveal_type(D.y) # N: Revealed type is "builtins.float" | |
| reveal_type(D.z) # N: Revealed type is "builtins.int" | |
| reveal_type(D().x) # N: Revealed type is "builtins.int" | |
| reveal_type(D().y) # N: Revealed type is "builtins.float" | |
| reveal_type(D().z) # N: Revealed type is "builtins.int" | |
| [out] | |
| [case testFinalDefiningInstanceVarImplicit] | |
| from typing import Final, Tuple, Any | |
| class C: | |
| def __init__(self, x: Tuple[int, Any]) -> None: | |
| self.x: Final = x | |
| self.y: Final[float] = 1 | |
| reveal_type(C((1, 2)).x) # N: Revealed type is "tuple[builtins.int, Any]" | |
| reveal_type(C((1, 2)).y) # N: Revealed type is "builtins.float" | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalBadDefinitionTooManyArgs] | |
| from typing import Final | |
| x: Final[int, str] # E: Final name must be initialized with a value \ | |
| # E: Final[...] takes at most one type argument | |
| reveal_type(x) # N: Revealed type is "builtins.int" | |
| class C: | |
| def __init__(self) -> None: | |
| self.x: Final[float, float] = 1 # E: Final[...] takes at most one type argument | |
| reveal_type(C().x) # N: Revealed type is "builtins.float" | |
| [out] | |
| [case testFinalInvalidDefinitions] | |
| from typing import Final, Any | |
| x = y = 1 # type: Final[float] # E: Invalid final declaration | |
| z: Any | |
| z[0]: Final[int] # E: Invalid final declaration \ | |
| # E: Unexpected type declaration | |
| [out] | |
| [case testFinalDefiningInstanceVarStubs] | |
| # Allow skipping r.h.s. | |
| import mod | |
| [file mod.pyi] | |
| from typing import Final | |
| x: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| y: Final[int] | |
| class C: | |
| x: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| y: Final[int] | |
| def __init__(self) -> None: | |
| self.z: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| reveal_type(x) # N: Revealed type is "Any" | |
| reveal_type(C.x) # N: Revealed type is "Any" | |
| v: C | |
| reveal_type(v.z) # N: Revealed type is "Any" | |
| [out] | |
| [case testFinalDefiningFunc] | |
| from typing import final | |
| @final # E: @final cannot be used with non-method functions | |
| def f(x: int) -> None: ... | |
| [out] | |
| [case testFinalDefiningFuncOverloaded] | |
| from typing import final, overload | |
| @overload | |
| def f(x: int) -> int: ... | |
| @overload | |
| def f(x: str) -> str: ... | |
| @final # E: @final cannot be used with non-method functions | |
| def f(x): | |
| pass | |
| [out] | |
| [case testFinalDefiningMeth] | |
| from typing import final | |
| class C: | |
| @final | |
| def f(self, x: int) -> None: ... | |
| reveal_type(C().f) # N: Revealed type is "def (x: builtins.int)" | |
| [out] | |
| [case testFinalDefiningMethOverloaded] | |
| from typing import final, overload | |
| class C: | |
| @overload | |
| def f(self, x: int) -> int: ... | |
| @overload | |
| def f(self, x: str) -> str: ... | |
| @final | |
| def f(self, x): | |
| pass | |
| @overload | |
| def bad(self, x: int) -> int: ... | |
| @final # E: @final should be applied only to overload implementation | |
| @overload | |
| def bad(self, x: str) -> str: ... | |
| def bad(self, x): | |
| pass | |
| reveal_type(C().f) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" | |
| [out] | |
| [case testFinalDefiningMethOverloadedStubs] | |
| from mod import C | |
| reveal_type(C().f) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.int, def (x: builtins.str) -> builtins.str)" | |
| [file mod.pyi] | |
| from typing import final, overload | |
| class C: | |
| @final | |
| @overload | |
| def f(self, x: int) -> int: ... | |
| @overload | |
| def f(self, x: str) -> str: ... | |
| @overload | |
| def bad(self, x: int) -> int: ... | |
| @final # E: In a stub file @final must be applied only to the first overload | |
| @overload | |
| def bad(self, x: str) -> str: ... | |
| [case testFinalDefiningProperty] | |
| from typing import final | |
| class C: | |
| @final | |
| @property | |
| def f(self) -> int: pass | |
| @property | |
| @final | |
| def g(self) -> int: pass | |
| reveal_type(C().f) # N: Revealed type is "builtins.int" | |
| reveal_type(C().g) # N: Revealed type is "builtins.int" | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalDefiningOuterOnly] | |
| from typing import Final, Callable, Tuple, Any | |
| x: Tuple[Final] # E: Final can be only used as an outermost qualifier in a variable annotation | |
| y: Callable[[], Tuple[Final[int]]] # E: Final can be only used as an outermost qualifier in a variable annotation | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalDefiningNotInMethod] | |
| from typing import Final | |
| def f(x: Final[int]) -> int: ... # E: Final can be only used as an outermost qualifier in a variable annotation | |
| def g(x: int) -> Final[int]: ... # E: Final can be only used as an outermost qualifier in a variable annotation | |
| [out] | |
| [case testFinalDefiningNotInMethodExtensions] | |
| # flags: --python-version 3.14 | |
| from typing_extensions import Final | |
| def f(x: Final[int]) -> int: ... # E: Final can be only used as an outermost qualifier in a variable annotation | |
| def g(x: int) -> Final[int]: ... # E: Final can be only used as an outermost qualifier in a variable annotation | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalDefiningNoRhs] | |
| from typing import Final | |
| x: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| y: Final[int] # E: Final name must be initialized with a value | |
| class C: | |
| x: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| y: Final[int] # E: Final name must be initialized with a value | |
| def __init__(self) -> None: | |
| self.z: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| reveal_type(x) # N: Revealed type is "Any" | |
| reveal_type(y) # N: Revealed type is "builtins.int" | |
| reveal_type(C().x) # N: Revealed type is "Any" | |
| reveal_type(C().y) # N: Revealed type is "builtins.int" | |
| reveal_type(C().z) # N: Revealed type is "Any" | |
| [out] | |
| [case testFinalDefiningNoRhsSubclass] | |
| from typing import Final | |
| class A: | |
| x: Final[int] # E: Final name must be initialized with a value | |
| class B(A): | |
| x = 1 # E: Cannot assign to final name "x" | |
| def __init__(self) -> None: | |
| self.x = 1 # E: Cannot assign to final attribute "x" | |
| [out] | |
| [case testFinalDefiningNoTypevarsExplicit] | |
| from typing import Final, TypeVar, Generic, Tuple, Any | |
| T = TypeVar('T') | |
| d: Any | |
| class C(Generic[T]): | |
| x: Final[Tuple[T, T]] = d # E: Final name declared in class body cannot depend on type variables | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalDefiningTypevarsImplicit] | |
| from typing import Final, TypeVar, Generic, Tuple, Any | |
| T = TypeVar('T') | |
| class C(Generic[T]): | |
| def __init__(self, x: Tuple[T, T]) -> None: | |
| self.x: Final = x | |
| self.y: Final = 1 | |
| reveal_type(C((1, 2)).x) # N: Revealed type is "tuple[builtins.int, builtins.int]" | |
| C.x # E: Cannot access final instance attribute "x" on class object \ | |
| # E: Access to generic instance variables via class is ambiguous | |
| C.y # E: Cannot access final instance attribute "y" on class object | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalDefiningNotInOtherMethod] | |
| from typing import Final, Any, Tuple | |
| class C: | |
| def meth(self, x: Tuple[int, Any]) -> None: | |
| self.x: Final = x # E: Can only declare a final attribute in class body or __init__ | |
| self.y: Final[float] = 1 # E: Can only declare a final attribute in class body or __init__ | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalDefiningOnlyOnSelf] | |
| from typing import Final, Any, Tuple | |
| class U: | |
| x: Any | |
| y: Any | |
| class C: | |
| def __init__(self, x: Tuple[int, Any]) -> None: | |
| slf = U() | |
| slf.x: Final = x # E: Final can be only applied to a name or an attribute on self | |
| slf.y: Final[float] = 1 # E: Type cannot be declared in assignment to non-self attribute \ | |
| # E: Final can be only applied to a name or an attribute on self | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalNotInProtocol] | |
| from typing import Final, final, Protocol, overload | |
| class P(Protocol): | |
| x: Final[float] = 1 # E: Protocol member cannot be final | |
| @final # E: Protocol member cannot be final | |
| def meth(self, x) -> int: | |
| pass | |
| @overload | |
| def other(self, x: int) -> int: ... | |
| @overload | |
| def other(self, x: str) -> str: ... | |
| @final # E: Protocol member cannot be final | |
| def other(self, x): | |
| pass | |
| [out] | |
| [case testFinalInProtocol] | |
| from typing import Final, Protocol, final | |
| class P(Protocol): | |
| var1 : Final[int] = 0 # E: Protocol member cannot be final | |
| @final # E: Protocol member cannot be final | |
| def meth1(self) -> None: | |
| var2: Final = 0 | |
| def meth2(self) -> None: | |
| var3: Final = 0 | |
| def meth3(self) -> None: | |
| class Inner: | |
| var3: Final = 0 # OK | |
| @final | |
| def inner(self) -> None: ... | |
| class Inner: | |
| var3: Final = 0 # OK | |
| @final | |
| def inner(self) -> None: ... | |
| [out] | |
| [case testFinalWithClassVarInProtocol] | |
| from typing import Protocol, Final, final, ClassVar | |
| class P(Protocol): | |
| var1 : Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final | |
| var2: ClassVar[int] = 1 | |
| @final # E: Protocol member cannot be final | |
| def meth1(self) -> None: | |
| ... | |
| def meth2(self) -> None: | |
| var3: Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final # E: ClassVar can only be used for assignments in class body | |
| [out] | |
| [case testFinalNotInLoops] | |
| from typing import Final | |
| for i in [1, 2, 3]: | |
| x: Final = i # E: Cannot use Final inside a loop | |
| while True: | |
| y: Final = True # E: Cannot use Final inside a loop | |
| [builtins fixtures/list.pyi] | |
| [out] | |
| [case testFinalDelayedDefinition] | |
| from typing import Final | |
| class C: | |
| x: Final[int] # OK, defined in __init__ | |
| bad: Final[int] # E: Final name must be initialized with a value | |
| def __init__(self, x: int) -> None: | |
| self.x = x # OK, deferred definition | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| def meth(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| c: C | |
| c.x = 3 # E: Cannot assign to final attribute "x" | |
| class D(C): | |
| x = 4 # E: Cannot assign to final name "x" | |
| d: D | |
| d.x = 5 # E: Cannot assign to final attribute "x" | |
| [out] | |
| [case testFinalDelayedDefinitionOtherMethod] | |
| from typing import Final | |
| class C: | |
| x: Final[int] # E: Final name must be initialized with a value | |
| def meth(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| [out] | |
| -- Reassignments | |
| [case testFinalReassignModuleVar] | |
| # flags: --allow-redefinition-old | |
| from typing import Final | |
| x: Final = 1 | |
| x | |
| x = 2 # E: Cannot assign to final name "x" | |
| def f() -> int: | |
| global x | |
| x = 3 # No error here is okay since we reported an error above | |
| return x | |
| x2: Final = 1 | |
| x2 | |
| def f2() -> None: | |
| global x2 | |
| x2 = 1 # E: Cannot assign to final name "x2" | |
| y = 1 | |
| y | |
| y: Final = 2 # E: Cannot redefine an existing name as final | |
| y = 3 # E: Cannot assign to final name "y" | |
| z: Final = 1 | |
| z: Final = 2 # E: Cannot redefine an existing name as final | |
| z = 3 # E: Cannot assign to final name "z" | |
| [case testFinalReassignModuleVar2] | |
| # flags: --allow-redefinition-old | |
| from typing import Final | |
| x: Final = 1 | |
| x | |
| def f() -> int: | |
| global x | |
| x = 3 # E: Cannot assign to final name "x" | |
| return x | |
| y = 1 | |
| y | |
| y = 2 | |
| y | |
| y: Final = 3 # E: Cannot redefine an existing name as final | |
| [case testFinalReassignModuleVar3] | |
| # flags: --disallow-redefinition-old | |
| from typing import Final | |
| x: Final = 1 | |
| x | |
| x = 2 # E: Cannot assign to final name "x" | |
| def f() -> int: | |
| global x | |
| x = 3 # E: Cannot assign to final name "x" | |
| return x | |
| x2: Final = 1 | |
| x2 | |
| def f2() -> None: | |
| global x2 | |
| x2 = 1 # E: Cannot assign to final name "x2" | |
| y = 1 # E: Cannot assign to final name "y" | |
| y | |
| y: Final = 2 # E: Cannot redefine an existing name as final | |
| y = 3 # E: Cannot assign to final name "y" | |
| z: Final = 1 | |
| z: Final = 2 # E: Cannot redefine an existing name as final | |
| z = 3 # E: Cannot assign to final name "z" | |
| [case testFinalReassignModuleReexport] | |
| from typing import Final | |
| from lib import X | |
| from lib.mod import ID | |
| X = 1 # E: Cannot assign to final name "X" | |
| ID: Final = 1 # E: Cannot redefine an existing name as final | |
| ID = 1 # E: Cannot assign to final name "ID" | |
| [file lib/__init__.pyi] | |
| from lib.const import X as X | |
| [file lib/mod.pyi] | |
| from lib.const import * | |
| [file lib/const.pyi] | |
| from typing import Final | |
| ID: Final # E: Type in Final[...] can only be omitted if there is an initializer | |
| X: Final[int] | |
| [case testFinalReassignFuncScope] | |
| from typing import Final | |
| def f() -> None: | |
| nl: Final = 0 | |
| x: Final = 1 | |
| x = 1 # E: Cannot assign to final name "x" | |
| y: Final = 1 | |
| y: Final = 2 # E: Cannot redefine an existing name as final | |
| def nested() -> None: | |
| nonlocal nl | |
| nl = 1 # E: Cannot assign to final name "nl" | |
| [out] | |
| [case testFinalReassignModuleVarExternal] | |
| import mod | |
| mod.x = 2 # E: Cannot assign to final name "x" | |
| [file mod.pyi] | |
| from typing import Final | |
| x: Final[int] | |
| [out] | |
| [case testFinalReassignInstanceVarClassBody] | |
| from typing import Final | |
| class C: | |
| x: Final = 1 | |
| x = 2 # E: Cannot assign to final name "x" | |
| y = 1 # E: Cannot assign to final name "y" | |
| y: Final = 2 # E: Cannot redefine an existing name as final | |
| [out] | |
| [case testFinalReassignInstanceVarInit] | |
| from typing import Final | |
| class C: | |
| def __init__(self) -> None: | |
| self.x: Final = 1 | |
| self.y = 1 | |
| self.y: Final = 2 # E: Cannot redefine an existing name as final | |
| def meth(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| [out] | |
| [case testFinalReassignInstanceVarClassVsInit] | |
| from typing import Final | |
| class C: | |
| y: Final = 1 | |
| def __init__(self) -> None: | |
| # Methods are processed after top-level in new analyzer. | |
| self.x: Final = 1 # E: Cannot redefine an existing name as final | |
| self.y = 2 # E: Cannot assign to final attribute "y" | |
| x = 2 | |
| [out] | |
| [case testFinalReassignInstanceVarMethod] | |
| from typing import Final | |
| class C: | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| def meth(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| self.y = 2 # E: Cannot assign to final attribute "y" | |
| def other(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| self.y = 2 # E: Cannot assign to final attribute "y" | |
| @classmethod | |
| def cm(cls) -> None: | |
| cls.x = 2 # E: Cannot assign to final attribute "x" | |
| cls.y # E: Cannot access final instance attribute "y" on class object | |
| [builtins fixtures/classmethod.pyi] | |
| [out] | |
| [case testFinalReassignInstanceVarExternalClass] | |
| from typing import Final | |
| class C: | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class D(C): pass | |
| C.x = 2 # E: Cannot assign to final attribute "x" | |
| D.x = 2 # E: Cannot assign to final attribute "x" | |
| D.y = 2 # E: Cannot access final instance attribute "y" on class object \ | |
| # E: Cannot assign to final attribute "y" | |
| [out] | |
| [case testFinalReassignInstanceVarExternalInstance] | |
| from typing import Final | |
| class C: | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class D(C): pass | |
| C().x = 2 # E: Cannot assign to final attribute "x" | |
| D().x = 2 # E: Cannot assign to final attribute "x" | |
| D().y = 2 # E: Cannot assign to final attribute "y" | |
| [out] | |
| [case testFinalWorksWithComplexTargets] | |
| from typing import Final, Any | |
| y: Final[Any] = 1 | |
| x = a, (b, y), c = 2, (2, 2), 2 # E: Cannot assign to final name "y" | |
| t, *y, s = u = [2, 2, 2] # E: Cannot assign to final name "y" | |
| [builtins fixtures/list.pyi] | |
| [out] | |
| [case testFinalInplaceAssign] | |
| from typing import Final | |
| class A: # no such things in fixtures | |
| def __add__(self, other: A) -> A: ... | |
| class B: | |
| def __add__(self, other: B) -> B: ... | |
| def __iadd__(self, other: B) -> B: ... | |
| a: Final = A() | |
| b: Final = B() | |
| class C: | |
| a: Final = A() | |
| b: Final = B() | |
| class D(C): | |
| pass | |
| a += A() # E: Cannot assign to final name "a" | |
| b += B() # E: Cannot assign to final name "b" | |
| D().a += A() # E: Cannot assign to final attribute "a" | |
| D().b += B() # E: Cannot assign to final attribute "b" | |
| [out] | |
| -- Overriding | |
| [case testFinalOverridingVarClassBody] | |
| from typing import Final | |
| # We use properties in this tests and below because we want to check | |
| # that any existing variable before final doesn't affect logic of | |
| # subsequent overrides but writable attributes cannot be overridden by final. | |
| class A: | |
| @property | |
| def x(self) -> int: ... | |
| @property | |
| def y(self) -> int: ... | |
| class B(A): | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class C(B): | |
| x: int = 2 # E: Cannot assign to final name "x" | |
| y: int = 2 # E: Cannot assign to final name "y" | |
| x = 3 # E: Cannot assign to final name "x" | |
| y = 3 # E: Cannot assign to final name "y" | |
| class D(C): | |
| pass | |
| D.x = 4 # E: Cannot assign to final attribute "x" | |
| D.y = 4 # E: Cannot assign to final attribute "y" | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarClassBodyExplicit] | |
| from typing import Final | |
| class A: | |
| @property | |
| def x(self) -> int: ... | |
| @property | |
| def y(self) -> int: ... | |
| class B(A): | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class C(B): | |
| x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B") | |
| y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B") | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarInit] | |
| from typing import Final | |
| class A: | |
| @property | |
| def x(self) -> int: ... | |
| @property | |
| def y(self) -> int: ... | |
| class B(A): | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class C(B): | |
| def __init__(self) -> None: | |
| self.x = 2 # E: Cannot assign to final attribute "x" | |
| self.y = 2 # E: Cannot assign to final attribute "y" | |
| def meth(self) -> None: | |
| self.x = 3 # E: Cannot assign to final attribute "x" | |
| self.y = 3 # E: Cannot assign to final attribute "y" | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarInit2] | |
| from typing import Final | |
| class A: | |
| @property | |
| def x(self) -> int: ... | |
| @property | |
| def y(self) -> int: ... | |
| class B(A): | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class C(B): | |
| def __init__(self) -> None: | |
| self.x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B") | |
| self.y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B") | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarOtherMethod] | |
| from typing import Final | |
| class A: | |
| @property | |
| def x(self) -> int: ... | |
| @property | |
| def y(self) -> int: ... | |
| class B(A): | |
| x: Final = 1 | |
| def __init__(self) -> None: | |
| self.y: Final = 1 | |
| class C(B): | |
| def meth(self) -> None: | |
| self.x: int = 2 # E: Cannot assign to final attribute "x" | |
| self.y: int = 2 # E: Cannot assign to final attribute "y" | |
| self.x = 3 # E: Cannot assign to final attribute "x" | |
| self.y = 3 # E: Cannot assign to final attribute "y" | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarMultipleInheritanceClass] | |
| from typing import Final, Any | |
| class A: | |
| x: Final[Any] = 1 | |
| class B: | |
| @property | |
| def x(self) -> int: ... | |
| class C(A, B): ... | |
| class D(B, A): ... # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| C.x = 3 # E: Cannot assign to final attribute "x" | |
| C().x = 4 # E: Cannot assign to final attribute "x" | |
| D().x = 4 # E: Cannot assign to final attribute "x" \ | |
| # E: Property "x" defined in "B" is read-only | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarMultipleInheritanceInit] | |
| from typing import Final, Any | |
| class A: | |
| def __init__(self) -> None: | |
| self.x: Final[Any] = 1 | |
| class B: | |
| @property | |
| def x(self) -> int: ... | |
| class C(A, B): ... | |
| class D(B, A): ... # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| C.x = 3 # E: Cannot access final instance attribute "x" on class object \ | |
| # E: Cannot assign to final attribute "x" | |
| C().x = 4 # E: Cannot assign to final attribute "x" | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarMultipleInheritanceMixed] | |
| from typing import Final | |
| class A: | |
| x: Final = 1 | |
| class B: | |
| def __init__(self) -> None: | |
| self.x = 2 | |
| class C(A, B): ... # E: Cannot override writable attribute "x" with a final one | |
| class D(B, A): ... # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| C.x = 3 # E: Cannot assign to final attribute "x" | |
| D.x = 3 # E: Cannot assign to final attribute "x" | |
| C().x = 4 # E: Cannot assign to final attribute "x" | |
| D().x = 4 # E: Cannot assign to final attribute "x" | |
| [out] | |
| [case testFinalOverridingVarWithMethod] | |
| from typing import Final, Any | |
| class A: | |
| x: Final[Any] = 1 | |
| def __init__(self) -> None: | |
| self.y: Final[Any] = 1 | |
| class B(A): | |
| def x(self) -> None: pass # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| def y(self) -> None: pass # E: Cannot override final attribute "y" (previously declared in base class "A") | |
| class C(A): | |
| @property # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| def x(self) -> None: pass | |
| @property # E: Cannot override final attribute "y" (previously declared in base class "A") | |
| def y(self) -> None: pass | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingVarWithMethodClass] | |
| from typing import Final, Any | |
| class A: | |
| x: Final[Any] = 1 | |
| def __init__(self) -> None: | |
| self.y: Final[Any] = 1 | |
| class B(A): | |
| @classmethod # E: Cannot override final attribute "x" (previously declared in base class "A") | |
| def x(self) -> None: pass | |
| @classmethod # E: Cannot override final attribute "y" (previously declared in base class "A") | |
| def y(self) -> None: pass | |
| [builtins fixtures/classmethod.pyi] | |
| [out] | |
| [case testFinalOverridingMethodRegular] | |
| from typing import final | |
| class B: | |
| @final | |
| def meth(self) -> None: ... | |
| class C(B): | |
| def meth(self) -> None: ... # E: Cannot override final attribute "meth" (previously declared in base class "B") | |
| [out] | |
| [case testFinalOverridingMethodInitNew] | |
| from typing import final | |
| class B: | |
| @final | |
| def __init__(self) -> None: ... | |
| @final | |
| def __new__(cls) -> B: ... | |
| class C(B): | |
| def __init__(self) -> None: ... # E: Cannot override final attribute "__init__" (previously declared in base class "B") | |
| def __new__(cls) -> C: ... # E: Cannot override final attribute "__new__" (previously declared in base class "B") | |
| [out] | |
| [case testFinalOverridingMethodWithVar] | |
| from typing import final, Final, Any | |
| a: Any | |
| class A: | |
| @final | |
| def f(self) -> None: pass | |
| @final | |
| @property | |
| def p(self) -> int: pass | |
| class B(A): | |
| f = a # E: Cannot override final attribute "f" (previously declared in base class "A") | |
| p = a # E: Cannot override final attribute "p" (previously declared in base class "A") | |
| class C(A): | |
| f: Any # E: Cannot override final attribute "f" (previously declared in base class "A") | |
| p: Any # E: Cannot override final attribute "p" (previously declared in base class "A") | |
| class D(A): | |
| f: Final = a # E: Cannot override final attribute "f" (previously declared in base class "A") | |
| p: Final = a # E: Cannot override final attribute "p" (previously declared in base class "A") | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingMethodWithVarImplicit] | |
| from typing import final, Any, Final | |
| a: Any | |
| class A: | |
| @final | |
| def f(self) -> None: pass | |
| @final | |
| @classmethod | |
| def c(cls) -> int: pass | |
| class B(A): | |
| def __init__(self) -> None: | |
| self.f: Any # E: Cannot assign to final attribute "f" \ | |
| # E: Cannot override final attribute "f" (previously declared in base class "A") | |
| self.c: Any # E: Cannot assign to final attribute "c" \ | |
| # E: Cannot override final attribute "c" (previously declared in base class "A") | |
| B().f = a # E: Cannot assign to final attribute "f" | |
| B().c = a # E: Cannot assign to final attribute "c" | |
| class C(A): | |
| def __init__(self) -> None: | |
| self.f: Final = a # E: Cannot override final attribute "f" (previously declared in base class "A") | |
| self.c: Final = a # E: Cannot override final attribute "c" (previously declared in base class "A") | |
| [builtins fixtures/classmethod.pyi] | |
| [out] | |
| [case testFinalCanOverrideMethodWithFinal] | |
| from typing import final | |
| class B: | |
| def meth(self) -> None: ... | |
| class C(B): | |
| @final # OK | |
| def meth(self) -> None: ... | |
| [out] | |
| [case testFinalOverridingMethodMultipleInheritance] | |
| from typing import final | |
| class A: | |
| def m(self) -> int: pass | |
| class B: | |
| @final | |
| def m(self) -> int: pass | |
| class C(A, B): pass # E: Cannot override final attribute "m" (previously declared in base class "B") | |
| class D(B, A): pass | |
| [out] | |
| [case testFinalOverridingMethodMultipleInheritanceVar] | |
| from typing import final, Any | |
| class A: | |
| m: Any | |
| class B: | |
| @final | |
| def m(self) -> int: pass | |
| class C(A, B): pass # E: Cannot override final attribute "m" (previously declared in base class "B") | |
| class D(B, A): pass # E: Cannot override writable attribute "m" with a final one | |
| [out] | |
| [case testFinalOverridingClassMethod] | |
| from typing import final | |
| class B: | |
| @classmethod | |
| @final | |
| def f(cls) -> int: pass | |
| class C(B): | |
| @classmethod # E: Cannot override final attribute "f" (previously declared in base class "B") | |
| def f(cls) -> int: pass | |
| [builtins fixtures/classmethod.pyi] | |
| [out] | |
| [case testFinalOverridingStaticMethod] | |
| from typing import final | |
| class B: | |
| @staticmethod | |
| @final | |
| def f() -> int: pass | |
| @final | |
| @staticmethod | |
| def g() -> int: pass | |
| class C(B): | |
| @staticmethod # E: Cannot override final attribute "f" (previously declared in base class "B") | |
| def f() -> int: pass | |
| @staticmethod # E: Cannot override final attribute "g" (previously declared in base class "B") | |
| def g() -> int: pass | |
| [builtins fixtures/staticmethod.pyi] | |
| [out] | |
| [case testFinalOverridingProperty] | |
| from typing import final | |
| class B: | |
| @final | |
| @property | |
| def f(self) -> int: pass | |
| @property | |
| @final | |
| def g(self) -> int: pass | |
| class C(B): | |
| @property # E: Cannot override final attribute "f" (previously declared in base class "B") | |
| def f(self) -> int: pass | |
| @property # E: Cannot override final attribute "g" (previously declared in base class "B") | |
| def g(self) -> int: pass | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalOverridingMethodOverloads] | |
| from typing import final, overload | |
| class B: | |
| @overload | |
| def f(self, x: int) -> int: ... | |
| @overload | |
| def f(self, x: str) -> str: ... | |
| @final | |
| def f(self, x): | |
| pass | |
| class C(B): | |
| @overload # E: Cannot override final attribute "f" (previously declared in base class "B") | |
| def f(self, x: int) -> int: ... | |
| @overload | |
| def f(self, x: str) -> str: ... | |
| def f(self, x): | |
| pass | |
| [out] | |
| [case testFinalClassNoInheritance] | |
| from typing import final | |
| @final | |
| class B: ... | |
| class C(B): # E: Cannot inherit from final class "B" | |
| pass | |
| class D(C): # E: Cannot inherit from final class "B" | |
| pass | |
| [out] | |
| [case testFinalClassNoInheritanceMulti] | |
| from typing import final | |
| class A: ... | |
| @final | |
| class B: ... | |
| class C(B, A): # E: Cannot inherit from final class "B" | |
| pass | |
| class D(A, B): # E: Cannot inherit from final class "B" | |
| pass | |
| [out] | |
| [case testFinalCantOverrideWriteable] | |
| from typing import Any, Final, final | |
| class B: | |
| x: Any | |
| @property | |
| def y(self) -> Any: ... | |
| @y.setter | |
| def y(self, x: Any) -> None: ... | |
| class C(B): | |
| x: Final = 1 # E: Cannot override writable attribute "x" with a final one | |
| y: Final = 1 # E: Cannot override writable attribute "y" with a final one | |
| class D(B): | |
| @final # E: Cannot override writable attribute "x" with a final one | |
| def x(self) -> int: ... | |
| @final # E: Cannot override writable attribute "y" with a final one | |
| def y(self) -> int: ... | |
| [builtins fixtures/property.pyi] | |
| [out] | |
| [case testFinalCanUseTypingExtensions] | |
| from typing_extensions import final, Final | |
| x: Final = 1 | |
| x = 2 # E: Cannot assign to final name "x" | |
| class S: | |
| x: Final = 1 | |
| S.x = 2 # E: Cannot assign to final attribute "x" | |
| class B: | |
| @final | |
| def meth(self) -> None: ... | |
| class C(B): | |
| def meth(self) -> None: ... # E: Cannot override final attribute "meth" (previously declared in base class "B") | |
| @final | |
| class F: ... | |
| class E(F): ... # E: Cannot inherit from final class "F" | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalCanUseTypingExtensionsAliased] | |
| from typing_extensions import final as f, Final as F | |
| x: F = 1 | |
| x = 2 # E: Cannot assign to final name "x" | |
| class S: | |
| x: F = 1 | |
| S.x = 2 # E: Cannot assign to final attribute "x" | |
| class B: | |
| @f | |
| def meth(self) -> None: ... | |
| class C(B): | |
| def meth(self) -> None: ... # E: Cannot override final attribute "meth" (previously declared in base class "B") | |
| @f | |
| class D(C): ... | |
| class E(D): ... # E: Cannot inherit from final class "D" | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| [case testFinalMultiassignAllowed] | |
| from typing import Final | |
| class A: | |
| x: Final[int] | |
| y: Final[int] | |
| def __init__(self) -> None: | |
| self.x, self.y = 1, 2 | |
| class B: | |
| x: Final[int] | |
| y: Final[int] | |
| def __init__(self) -> None: | |
| self.x = self.y = 1 | |
| [out] | |
| [case testFinalInDeferredMethod] | |
| from typing import Final | |
| class A: | |
| def __init__(self) -> None: | |
| self.x = 10 # type: Final | |
| undefined # type: ignore | |
| [builtins fixtures/tuple.pyi] | |
| [case testFinalUsedWithClassVar] | |
| # flags: --python-version 3.12 | |
| from typing import Final, ClassVar | |
| class A: | |
| a: Final[ClassVar[int]] # E: Variable should not be annotated with both ClassVar and Final | |
| b: ClassVar[Final[int]] # E: Final can be only used as an outermost qualifier in a variable annotation | |
| c: ClassVar[Final] = 1 # E: Final can be only used as an outermost qualifier in a variable annotation | |
| [out] | |
| [case testFinalUsedWithClassVarAfterPy313] | |
| # flags: --python-version 3.13 | |
| from typing import Final, ClassVar | |
| class A: | |
| a: Final[ClassVar[int]] = 1 | |
| b: ClassVar[Final[int]] = 1 | |
| c: ClassVar[Final] = 1 | |
| [case testFinalClassWithAbstractMethod] | |
| from typing import final | |
| from abc import ABC, abstractmethod | |
| @final | |
| class A(ABC): # E: Final class __main__.A has abstract attributes "B" | |
| @abstractmethod | |
| def B(self) -> None: ... | |
| [case testFinalDefiningFuncWithAbstractMethod] | |
| from typing import final | |
| from abc import ABC, abstractmethod | |
| class A(ABC): | |
| @final # E: Method B is both abstract and final | |
| @abstractmethod | |
| def B(self) -> None: ... | |
| [case testFinalClassVariableRedefinitionDoesNotCrash] | |
| # This used to crash -- see #12950 | |
| from typing import Final | |
| class MyClass: | |
| a: None | |
| a: Final[int] = 1 # E: Cannot redefine an existing name as final # E: Name "a" already defined on line 5 | |
| [case testFinalOverrideAllowedForPrivate] | |
| from typing import Final, final | |
| class Parent: | |
| __foo: Final[int] = 0 | |
| @final | |
| def __bar(self) -> None: ... | |
| class Child(Parent): | |
| __foo: Final[int] = 1 | |
| @final | |
| def __bar(self) -> None: ... | |
| [case testFinalWithoutBool] | |
| from typing import Literal, final | |
| class A: | |
| pass | |
| @final | |
| class B: | |
| pass | |
| @final | |
| class C: | |
| def __len__(self) -> Literal[1]: return 1 | |
| reveal_type(A() and 42) # N: Revealed type is "__main__.A | Literal[42]?" | |
| reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" | |
| reveal_type(C() and 42) # N: Revealed type is "Literal[42]?" | |
| [builtins fixtures/bool.pyi] | |
| [case testFinalWithoutBoolButWithLen] | |
| from typing import Literal, final | |
| # Per Python data model, __len__ is called if __bool__ does not exist. | |
| # In a @final class, __bool__ would not exist. | |
| @final | |
| class A: | |
| def __len__(self) -> int: ... | |
| @final | |
| class B: | |
| def __len__(self) -> Literal[1]: return 1 | |
| @final | |
| class C: | |
| def __len__(self) -> Literal[0]: return 0 | |
| reveal_type(A() and 42) # N: Revealed type is "__main__.A | Literal[42]?" | |
| reveal_type(B() and 42) # N: Revealed type is "Literal[42]?" | |
| reveal_type(C() and 42) # N: Revealed type is "__main__.C" | |
| [builtins fixtures/bool.pyi] | |
| [case testCanAccessFinalClassInit] | |
| from typing import final | |
| @final | |
| class FinalClass: | |
| pass | |
| def check_final_class() -> None: | |
| new_instance = FinalClass() | |
| new_instance.__init__() | |
| class FinalInit: | |
| @final | |
| def __init__(self) -> None: | |
| pass | |
| def check_final_init() -> None: | |
| new_instance = FinalInit() | |
| new_instance.__init__() | |
| [builtins fixtures/tuple.pyi] | |
| [case testNarrowingOfFinalInFunctionsNotSpecial] | |
| from typing import Final, Union | |
| def _init() -> Union[int, None]: | |
| return 0 | |
| FOO: Final = _init() | |
| class Example: | |
| if FOO is not None: | |
| reveal_type(FOO) # N: Revealed type is "builtins.int" | |
| def fn(self) -> int: | |
| return FOO # E: Incompatible return value type (got "int | None", expected "int") | |
| if FOO is not None: | |
| reveal_type(FOO) # N: Revealed type is "builtins.int" | |
| def func() -> int: | |
| return FOO # E: Incompatible return value type (got "int | None", expected "int") | |
| [case testDisjointBase] | |
| from typing_extensions import disjoint_base | |
| @disjoint_base | |
| class Disjoint1: pass | |
| @disjoint_base | |
| class Disjoint2: pass | |
| @disjoint_base | |
| class DisjointChild(Disjoint1): pass | |
| class C1: pass | |
| class C2(Disjoint1, C1): pass | |
| class C3(DisjointChild, Disjoint1): pass | |
| class C4(Disjoint1, Disjoint2): # E: Class "C4" has incompatible disjoint bases | |
| pass | |
| class C5(Disjoint2, Disjoint1): # E: Class "C5" has incompatible disjoint bases | |
| pass | |
| class C6(Disjoint2, DisjointChild): # E: Class "C6" has incompatible disjoint bases | |
| pass | |
| class C7(DisjointChild, Disjoint2): # E: Class "C7" has incompatible disjoint bases | |
| pass | |
| class C8(DisjointChild, Disjoint1, Disjoint2): # E: Class "C8" has incompatible disjoint bases | |
| pass | |
| class C9(C2, Disjoint2): # E: Class "C9" has incompatible disjoint bases | |
| pass | |
| class C10(C3, Disjoint2): # E: Class "C10" has incompatible disjoint bases | |
| pass | |
| [builtins fixtures/tuple.pyi] | |
| [case testDisjointBaseSlots] | |
| class S1: | |
| __slots__ = ("a",) | |
| class S2: | |
| __slots__ = ("b",) | |
| class S3: | |
| __slots__ = () | |
| class S4(S1): | |
| __slots__ = ("c",) | |
| class S5(S1, S2): # E: Class "S5" has incompatible disjoint bases | |
| pass | |
| class S6(S1, S3): pass # OK | |
| class S7(S3, S1): pass # OK | |
| class S8(S4, S1): pass # OK | |
| class S9(S2, S4): # E: Class "S9" has incompatible disjoint bases | |
| pass | |
| [builtins fixtures/tuple.pyi] |