diff --git a/docs/guides/libraries.rst b/docs/guides/libraries.rst index 8e62af9c5..66404aac8 100644 --- a/docs/guides/libraries.rst +++ b/docs/guides/libraries.rst @@ -568,10 +568,10 @@ type annotation would be redundant. COLOR_FORMAT_RGB: Literal["rgb"] = "rgb" LATEST_VERSION: tuple[int, int] = (4, 5) - # Final variable with inferred type + # Bare Final variable with inferred type ColorFormatRgb: Final = "rgb" - # Final variable with explicit type + # Explicitly typed Final variable ColorFormatRgb: Final[Literal["rgb"]] = "rgb" LATEST_VERSION: Final[tuple[int, int]] = (4, 5) diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst index 7964566c9..af0b402cd 100644 --- a/docs/guides/writing_stubs.rst +++ b/docs/guides/writing_stubs.rst @@ -349,8 +349,8 @@ still recommended to separately define known attributes. Constants --------- -When the value of a constant is important, mark it as ``Final`` and assign it -to its value. +When the value of a constant is important, mark it as a :term:`bare Final` and +assign it to its value. Yes:: diff --git a/docs/spec/dataclasses.rst b/docs/spec/dataclasses.rst index ef08344b4..3d9648923 100644 --- a/docs/spec/dataclasses.rst +++ b/docs/spec/dataclasses.rst @@ -526,8 +526,9 @@ This includes, but is not limited to, the following semantics: * ClassVar attributes are not considered dataclass fields and are `ignored by dataclass mechanisms `_. -* A dataclass field may be annotated with ``Final[...]``. For example, ``x: - Final[int]`` in a dataclass body specifies a dataclass field ``x``, which +* A dataclass field may be annotated with an :term:`explicitly typed Final` + :term:`type qualifier`. For example, ``x: Final[int]`` + in a dataclass body specifies a dataclass field ``x``, which will be initialized in the generated ``__init__`` and cannot be assigned to thereafter. A ``Final`` dataclass field initialized in the class body is not a class attribute unless explicitly annotated with ``ClassVar``. For example, diff --git a/docs/spec/distributing.rst b/docs/spec/distributing.rst index 13cfff81a..93edf5666 100644 --- a/docs/spec/distributing.rst +++ b/docs/spec/distributing.rst @@ -105,8 +105,8 @@ assignments:: x = 0 # type: int x = ... # type: int -The :ref:`Literal shortcut using Final ` should be -supported:: +The :ref:`Literal shortcut using a bare Final ` +should be supported:: x: Final = 0 # type is Literal[0] diff --git a/docs/spec/glossary.rst b/docs/spec/glossary.rst index 340dcbd4a..3c8a32cfd 100644 --- a/docs/spec/glossary.rst +++ b/docs/spec/glossary.rst @@ -27,6 +27,10 @@ This section defines a few terms that may be used elsewhere in the specification ``B``, respectively, such that ``B'`` is a subtype of ``A'``. See :ref:`type-system-concepts`. + bare ``Final`` + A :ref:`Final ` :term:`type qualifier` without an + explicit type, e.g. ``x: Final``. + closed A :ref:`TypedDict ` type is closed if it may not contain any additional :term:`items ` beyond those specified in the TypedDict definition. @@ -59,6 +63,10 @@ This section defines a few terms that may be used elsewhere in the specification also materializations of ``B``, and all materializations of ``B`` are also materializations of ``A``. + explicitly typed ``Final`` + A :ref:`Final ` :term:`type qualifier` with an explicit + type, e.g. ``x: Final[int]``. + extra items A :ref:`TypedDict ` type with extra items may contain arbitrary additional key-value pairs beyond those specified in the TypedDict definition, but those diff --git a/docs/spec/literal.rst b/docs/spec/literal.rst index 0eb93c27a..7254632b2 100644 --- a/docs/spec/literal.rst +++ b/docs/spec/literal.rst @@ -504,8 +504,8 @@ involving Literal bools. For example, we can combine ``Literal[True]``, Interactions with Final """"""""""""""""""""""" -The ``Final`` qualifier can be used to declare that some variable or -attribute cannot be reassigned:: +A :term:`bare Final` type qualifier can be used to declare that some variable +or attribute cannot be reassigned:: foo: Final = 3 foo = 4 # Error: 'foo' is declared to be Final @@ -518,8 +518,8 @@ is valid to use in any context that expects a ``Literal[3]``:: expects_three(foo) # Type checks, since 'foo' is Final and equal to 3 -The ``Final`` qualifier serves as a shorthand for declaring that a variable -is *effectively Literal*. +A :term:`bare Final` type qualifier serves as a shorthand for declaring that a +variable is *effectively Literal*. Type checkers are expected to support this shortcut. Specifically, given a variable or attribute assignment @@ -530,7 +530,7 @@ any context that expects a ``Literal[value]``. Type checkers are not obligated to understand any other uses of Final. For example, whether or not the following program type checks is left unspecified:: - # Note: The assignment does not exactly match the form 'var: Final = value'. + # Note: Literal inference is unspecified for explicitly typed Final. bar1: Final[int] = 3 expects_three(bar1) # May or may not be accepted by type checkers diff --git a/docs/spec/qualifiers.rst b/docs/spec/qualifiers.rst index 6814692e0..b05202125 100644 --- a/docs/spec/qualifiers.rst +++ b/docs/spec/qualifiers.rst @@ -81,30 +81,27 @@ variable or attribute should not be reassigned, redefined, or overridden. Syntax ^^^^^^ -``Final`` may be used in one of several forms: +``Final`` may be used in one of two forms: -* With an explicit type, using the syntax ``Final[]``. Example:: +* As an :term:`explicitly typed Final`, using the syntax ``Final[]``. + Example:: ID: Final[float] = 1 -* With no type annotation. Example:: + In class bodies and stub files the right hand side can be omitted:: + + ID: Final[float] + +* As a :term:`bare Final`, without a type argument. Example:: ID: Final = 1 + Bare ``Final`` annotations must have an assigned value. + The typechecker should apply its usual type inference mechanisms to determine the type of ``ID`` (here, likely, ``int``). Note that unlike for generic classes this is *not* the same as ``Final[Any]``. -* In class bodies and stub files you can omit the right hand side and just write - ``ID: Final[float]``. If the right hand side is omitted, there must - be an explicit type argument to ``Final``. - -* Finally, as ``self.id: Final = 1`` (also optionally with a type in - square brackets). This is allowed *only* in ``__init__`` methods, so - that the final instance attribute is assigned only once when an - instance is created. - - Semantics and examples ^^^^^^^^^^^^^^^^^^^^^^ @@ -156,8 +153,8 @@ be initialized in the ``__init__`` method (except in stub files):: self.x = 1 # Good The generated ``__init__`` method of :doc:`dataclasses` qualifies for this -requirement: a bare ``x: Final[int]`` is permitted in a dataclass body, because -the generated ``__init__`` will initialize ``x``. +requirement: an ``x: Final[int]`` without an initializer is permitted in a +dataclass body, because the generated ``__init__`` will initialize ``x``. Type checkers should infer a final attribute that is initialized in a class body as being a class variable, except in the case of :doc:`dataclasses`, where @@ -167,6 +164,15 @@ necessary to create a final class variable with value ``3``. In non-dataclasses, combining ``ClassVar`` and ``Final`` is redundant, and type checkers may choose to warn or error on the redundancy. +``Final`` attributes can also be declared in ``__init__`` methods, as +annotations on an assignment to an attribute on ``self``. This is allowed +*only* in ``__init__``, not in any other method:: + + class ImmutableAttributes: + def __init__(self) -> None: + self.x: Final = 3 + self.y: Final[str] = "knight" + ``Final`` may only be used in assignments or variable annotations. Using it in any other position is an error. In particular, ``Final`` can't be used in annotations for function arguments:: @@ -193,9 +199,9 @@ with ``Final`` to prevent mutating such values:: z: Final = ('a', 'b') # Also works -Type checkers should treat uses of a final name that was initialized -with a literal as if it was replaced by the literal. For example, the -following should be allowed:: +Type checkers should treat uses of names declared with a :term:`bare Final` +that was initialized with a literal as if it was replaced by the literal. For +example, the following should be allowed:: from typing import NamedTuple, Final diff --git a/docs/spec/typeddict.rst b/docs/spec/typeddict.rst index cff7e8499..81de917a6 100644 --- a/docs/spec/typeddict.rst +++ b/docs/spec/typeddict.rst @@ -732,8 +732,8 @@ certain items. For example, ``d['x'] = 1`` should generate a type check error if ``'x'`` is not a valid key for ``d`` (which is assumed to be a TypedDict type), unless ``d`` has mutable :term:`extra items` of a compatible type. -Type checkers should allow :ref:`final names ` with -string values to be used instead of string literals in operations on +Type checkers should allow names declared with a :term:`bare Final` and a +string value to be used instead of string literals in operations on TypedDict objects. For example, this is valid:: YEAR: Final = 'year'