[Python-checkins] [3.12] gh-103921: Document PEP 695 (GH-104642) (#104989)

JelleZijlstra webhook-mailer at python.org
Fri May 26 14:19:45 EDT 2023


https://github.com/python/cpython/commit/dcee0aa9119243c294df820d6586d539ff039d3d
commit: dcee0aa9119243c294df820d6586d539ff039d3d
branch: 3.12
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2023-05-26T18:19:34Z
summary:

[3.12] gh-103921: Document PEP 695 (GH-104642) (#104989)

(cherry picked from commit 060277d96bf4ba86df8e4d65831a8cbdfeb51fc5)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra at gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood at Gmail.com>

files:
M Doc/library/ast.rst
M Doc/library/dis.rst
M Doc/library/stdtypes.rst
M Doc/library/typing.rst
M Doc/reference/compound_stmts.rst
M Doc/reference/datamodel.rst
M Doc/reference/executionmodel.rst
M Doc/reference/lexical_analysis.rst
M Doc/reference/simple_stmts.rst
M Doc/whatsnew/3.12.rst
M Objects/typevarobject.c

diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index c75c4e3bd219..8a686354b9f6 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -917,6 +917,25 @@ Statements
             type_ignores=[])
 
 
+.. class:: TypeAlias(name, type_params, value)
+
+   A :ref:`type alias <type-aliases>` created through the :keyword:`type`
+   statement. ``name`` is the name of the alias, ``type_params`` is a list of
+   :ref:`type parameters <ast-type-params>`, and ``value`` is the value of the
+   type alias.
+
+   .. doctest::
+
+        >>> print(ast.dump(ast.parse('type Alias = int'), indent=4))
+        Module(
+            body=[
+                TypeAlias(
+                    name=Name(id='Alias', ctx=Store()),
+                    type_params=[],
+                    value=Name(id='int', ctx=Load()))],
+            type_ignores=[])
+
+
 Other statements which are only applicable inside functions or loops are
 described in other sections.
 
@@ -1644,15 +1663,93 @@ Pattern matching
                                     value=Constant(value=Ellipsis))])])],
             type_ignores=[])
 
+.. _ast-type-params:
+
+Type parameters
+^^^^^^^^^^^^^^^
+
+:ref:`Type parameters <type-params>` can exist on classes, functions, and type
+aliases.
+
+.. class:: TypeVar(name, bound)
+
+   A :class:`typing.TypeVar`. ``name`` is the name of the type variable.
+   ``bound`` is the bound or constraints, if any. If ``bound`` is a :class:`Tuple`,
+   it represents constraints; otherwise it represents the bound.
+
+   .. doctest::
+
+        >>> print(ast.dump(ast.parse("type Alias[T: int] = list[T]"), indent=4))
+        Module(
+            body=[
+                TypeAlias(
+                    name=Name(id='Alias', ctx=Store()),
+                    type_params=[
+                        TypeVar(
+                            name='T',
+                            bound=Name(id='int', ctx=Load()))],
+                    value=Subscript(
+                        value=Name(id='list', ctx=Load()),
+                        slice=Name(id='T', ctx=Load()),
+                        ctx=Load()))],
+            type_ignores=[])
+
+.. class:: ParamSpec(name)
+
+   A :class:`typing.ParamSpec`. ``name`` is the name of the parameter specification.
+
+   .. doctest::
+
+        >>> print(ast.dump(ast.parse("type Alias[**P] = Callable[P, int]"), indent=4))
+        Module(
+            body=[
+                TypeAlias(
+                    name=Name(id='Alias', ctx=Store()),
+                    type_params=[
+                        ParamSpec(name='P')],
+                    value=Subscript(
+                        value=Name(id='Callable', ctx=Load()),
+                        slice=Tuple(
+                            elts=[
+                                Name(id='P', ctx=Load()),
+                                Name(id='int', ctx=Load())],
+                            ctx=Load()),
+                        ctx=Load()))],
+            type_ignores=[])
+
+.. class:: TypeVarTuple(name)
+
+   A :class:`typing.TypeVarTuple`. ``name`` is the name of the type variable tuple.
+
+   .. doctest::
+
+        >>> print(ast.dump(ast.parse("type Alias[*Ts] = tuple[*Ts]"), indent=4))
+        Module(
+            body=[
+                TypeAlias(
+                    name=Name(id='Alias', ctx=Store()),
+                    type_params=[
+                        TypeVarTuple(name='Ts')],
+                    value=Subscript(
+                        value=Name(id='tuple', ctx=Load()),
+                        slice=Tuple(
+                            elts=[
+                                Starred(
+                                    value=Name(id='Ts', ctx=Load()),
+                                    ctx=Load())],
+                            ctx=Load()),
+                        ctx=Load()))],
+            type_ignores=[])
 
 Function and class definitions
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. class:: FunctionDef(name, args, body, decorator_list, returns, type_comment)
+.. class:: FunctionDef(name, type_params, args, body, decorator_list, returns, type_comment)
 
    A function definition.
 
    * ``name`` is a raw string of the function name.
+   * ``type_params`` is a list of :ref:`type parameters <ast-type-params>`.
    * ``args`` is an :class:`arguments` node.
    * ``body`` is the list of nodes inside the function.
    * ``decorator_list`` is the list of decorators to be applied, stored outermost
@@ -1820,11 +1917,12 @@ Function and class definitions
             type_ignores=[])
 
 
-.. class:: ClassDef(name, bases, keywords, body, decorator_list)
+.. class:: ClassDef(name, type_params, bases, keywords, body, decorator_list)
 
    A class definition.
 
    * ``name`` is a raw string for the class name
+   * ``type_params`` is a list of :ref:`type parameters <ast-type-params>`.
    * ``bases`` is a list of nodes for explicitly specified base classes.
    * ``keywords`` is a list of :class:`keyword` nodes, principally for 'metaclass'.
      Other keywords will be passed to the metaclass, as per `PEP-3115
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 4762fb504374..9b90f1ef23d9 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -188,9 +188,9 @@ operation is being performed, so the intermediate analysis object isn't useful:
    For a module, it disassembles all functions. For a class, it disassembles
    all methods (including class and static methods). For a code object or
    sequence of raw bytecode, it prints one line per bytecode instruction.
-   It also recursively disassembles nested code objects (the code of
-   comprehensions, generator expressions and nested functions, and the code
-   used for building nested classes).
+   It also recursively disassembles nested code objects. These can include
+   generator expressions, nested functions, the bodies of nested classes,
+   and the code objects used for :ref:`annotation scopes <annotation-scopes>`.
    Strings are first compiled to code objects with the :func:`compile`
    built-in function before being disassembled.  If no object is provided, this
    function disassembles the last traceback.
@@ -926,6 +926,27 @@ iterations of the loop.
 .. opcode:: LOAD_NAME (namei)
 
    Pushes the value associated with ``co_names[namei]`` onto the stack.
+   The name is looked up within the locals, then the globals, then the builtins.
+
+
+.. opcode:: LOAD_LOCALS
+
+   Pushes a reference to the locals dictionary onto the stack.  This is used
+   to prepare namespace dictionaries for :opcode:`LOAD_FROM_DICT_OR_DEREF`
+   and :opcode:`LOAD_FROM_DICT_OR_GLOBALS`.
+
+   .. versionadded:: 3.12
+
+
+.. opcode:: LOAD_FROM_DICT_OR_GLOBALS (i)
+
+   Pops a mapping off the stack and looks up the value for ``co_names[namei]``.
+   If the name is not found there, looks it up in the globals and then the builtins,
+   similar to :opcode:`LOAD_GLOBAL`.
+   This is used for loading global variables in
+   :ref:`annotation scopes <annotation-scopes>` within class bodies.
+
+   .. versionadded:: 3.12
 
 
 .. opcode:: BUILD_TUPLE (count)
@@ -1243,16 +1264,17 @@ iterations of the loop.
       ``i`` is no longer offset by the length of ``co_varnames``.
 
 
-.. opcode:: LOAD_CLASSDEREF (i)
+.. opcode:: LOAD_FROM_DICT_OR_DEREF (i)
 
-   Much like :opcode:`LOAD_DEREF` but first checks the locals dictionary before
-   consulting the cell.  This is used for loading free variables in class
-   bodies.
-
-   .. versionadded:: 3.4
+   Pops a mapping off the stack and looks up the name associated with
+   slot ``i`` of the "fast locals" storage in this mapping.
+   If the name is not found there, loads it from the cell contained in
+   slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading
+   free variables in class bodies (which previously used
+   :opcode:`!LOAD_CLASSDEREF`) and in
+   :ref:`annotation scopes <annotation-scopes>` within class bodies.
 
-   .. versionchanged:: 3.11
-      ``i`` is no longer offset by the length of ``co_varnames``.
+   .. versionadded:: 3.12
 
 
 .. opcode:: STORE_DEREF (i)
@@ -1504,13 +1526,45 @@ iterations of the loop.
 
    The operand determines which intrinsic function is called:
 
-   * ``0`` Not valid
-   * ``1`` Prints the argument to standard out. Used in the REPL.
-   * ``2`` Performs ``import *`` for the named module.
-   * ``3`` Extracts the return value from a ``StopIteration`` exception.
-   * ``4`` Wraps an aync generator value
-   * ``5`` Performs the unary ``+`` operation
-   * ``6`` Converts a list to a tuple
+   +-----------------------------------+-----------------------------------+
+   | Operand                           | Description                       |
+   +===================================+===================================+
+   | ``INTRINSIC_1_INVALID``           | Not valid                         |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_PRINT``               | Prints the argument to standard   |
+   |                                   | out. Used in the REPL.            |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_IMPORT_STAR``         | Performs ``import *`` for the     |
+   |                                   | named module.                     |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_STOPITERATION_ERROR`` | Extracts the return value from a  |
+   |                                   | ``StopIteration`` exception.      |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_ASYNC_GEN_WRAP``      | Wraps an aync generator value     |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_UNARY_POSITIVE``      | Performs the unary ``+``          |
+   |                                   | operation                         |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_LIST_TO_TUPLE``       | Converts a list to a tuple        |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_TYPEVAR``             | Creates a :class:`typing.TypeVar` |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_PARAMSPEC``           | Creates a                         |
+   |                                   | :class:`typing.ParamSpec`         |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_TYPEVARTUPLE``        | Creates a                         |
+   |                                   | :class:`typing.TypeVarTuple`      |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_SUBSCRIPT_GENERIC``   | Returns :class:`typing.Generic`   |
+   |                                   | subscripted with the argument     |
+   +-----------------------------------+-----------------------------------+
+   | ``INTRINSIC_TYPEALIAS``           | Creates a                         |
+   |                                   | :class:`typing.TypeAliasType`;    |
+   |                                   | used in the :keyword:`type`       |
+   |                                   | statement. The argument is a tuple|
+   |                                   | of the type alias's name,         |
+   |                                   | type parameters, and value.       |
+   +-----------------------------------+-----------------------------------+
 
    .. versionadded:: 3.12
 
@@ -1522,8 +1576,25 @@ iterations of the loop.
 
    The operand determines which intrinsic function is called:
 
-   * ``0`` Not valid
-   * ``1`` Calculates the :exc:`ExceptionGroup` to raise from a ``try-except*``.
+   +----------------------------------------+-----------------------------------+
+   | Operand                                | Description                       |
+   +========================================+===================================+
+   | ``INTRINSIC_2_INVALID``                | Not valid                         |
+   +----------------------------------------+-----------------------------------+
+   | ``INTRINSIC_PREP_RERAISE_STAR``        | Calculates the                    |
+   |                                        | :exc:`ExceptionGroup` to raise    |
+   |                                        | from a ``try-except*``.           |
+   +----------------------------------------+-----------------------------------+
+   | ``INTRINSIC_TYPEVAR_WITH_BOUND``       | Creates a :class:`typing.TypeVar` |
+   |                                        | with a bound.                     |
+   +----------------------------------------+-----------------------------------+
+   | ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a                         |
+   |                                        | :class:`typing.TypeVar` with      |
+   |                                        | constraints.                      |
+   +----------------------------------------+-----------------------------------+
+   | ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__``      |
+   |                                        | attribute of a function.          |
+   +----------------------------------------+-----------------------------------+
 
    .. versionadded:: 3.12
 
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 9203afbf6a4e..fdef5314b9a4 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -5476,6 +5476,14 @@ types, where they are relevant.  Some of these are not reported by the
    .. versionadded:: 3.3
 
 
+.. attribute:: definition.__type_params__
+
+   The :ref:`type parameters <type-params>` of generic classes, functions,
+   and :ref:`type aliases <type-aliases>`.
+
+   .. versionadded:: 3.12
+
+
 .. attribute:: class.__mro__
 
    This attribute is a tuple of classes that are considered when looking for
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index cd4df61c5ac3..a4bba7b5ad63 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -101,6 +101,8 @@ annotations. These include:
 * :pep:`692`: Using ``TypedDict`` for more precise ``**kwargs`` typing
     *Introducing* a new way of typing ``**kwargs`` with :data:`Unpack` and
     :data:`TypedDict`
+* :pep:`695`: Type Parameter Syntax
+    *Introducing* builtin syntax for creating generic functions, classes, and type aliases.
 * :pep:`698`: Adding an override decorator to typing
     *Introducing* the :func:`@override<override>` decorator
 
@@ -109,10 +111,12 @@ annotations. These include:
 Type aliases
 ============
 
-A type alias is defined by assigning the type to the alias. In this example,
-``Vector`` and ``list[float]`` will be treated as interchangeable synonyms::
+A type alias is defined using the :keyword:`type` statement, which creates
+an instance of :class:`TypeAliasType`. In this example,
+``Vector`` and ``list[float]`` will be treated equivalently by static type
+checkers::
 
-   Vector = list[float]
+   type Vector = list[float]
 
    def scale(scalar: float, vector: Vector) -> Vector:
        return [scalar * num for num in vector]
@@ -124,9 +128,9 @@ Type aliases are useful for simplifying complex type signatures. For example::
 
    from collections.abc import Sequence
 
-   ConnectionOptions = dict[str, str]
-   Address = tuple[str, int]
-   Server = tuple[Address, ConnectionOptions]
+   type ConnectionOptions = dict[str, str]
+   type Address = tuple[str, int]
+   type Server = tuple[Address, ConnectionOptions]
 
    def broadcast_message(message: str, servers: Sequence[Server]) -> None:
        ...
@@ -141,6 +145,18 @@ Type aliases are useful for simplifying complex type signatures. For example::
 Note that ``None`` as a type hint is a special case and is replaced by
 ``type(None)``.
 
+The :keyword:`type` statement is new in Python 3.12. For backwards
+compatibility, type aliases can also be created through simple assignment::
+
+   Vector = list[float]
+
+Or marked with :data:`TypeAlias` to make it explicit that this is a type alias,
+not a normal variable assignment::
+
+   from typing import TypeAlias
+
+   Vector: TypeAlias = list[float]
+
 .. _distinct:
 
 NewType
@@ -206,7 +222,7 @@ See :pep:`484` for more details.
 .. note::
 
    Recall that the use of a type alias declares two types to be *equivalent* to
-   one another. Doing ``Alias = Original`` will make the static type checker
+   one another. Doing ``type Alias = Original`` will make the static type checker
    treat ``Alias`` as being *exactly equivalent* to ``Original`` in all cases.
    This is useful when you want to simplify complex type signatures.
 
@@ -282,19 +298,26 @@ subscription to denote expected types for container elements.
    def notify_by_email(employees: Sequence[Employee],
                        overrides: Mapping[str, str]) -> None: ...
 
-Generics can be parameterized by using a factory available in typing
-called :class:`TypeVar`.
+Generics can be parameterized by using :ref:`type parameter syntax <type-params>`::
 
-::
+   from collections.abc import Sequence
+
+   def first[T](l: Sequence[T]) -> T:   # Generic function
+       return l[0]
+
+Or by using the :class:`TypeVar` factory directly::
 
    from collections.abc import Sequence
    from typing import TypeVar
 
-   T = TypeVar('T')      # Declare type variable
+   U = TypeVar('U')      # Declare type variable
 
-   def first(l: Sequence[T]) -> T:   # Generic function
+   def first(l: Sequence[U]) -> U:   # Generic function
        return l[0]
 
+.. versionchanged:: 3.12
+   Syntactic support for generics is new in Python 3.12.
+
 .. _user-defined-generics:
 
 User-defined generic types
@@ -304,12 +327,9 @@ A user-defined class can be defined as a generic class.
 
 ::
 
-   from typing import TypeVar, Generic
    from logging import Logger
 
-   T = TypeVar('T')
-
-   class LoggedVar(Generic[T]):
+   class LoggedVar[T]:
        def __init__(self, value: T, name: str, logger: Logger) -> None:
            self.name = name
            self.logger = logger
@@ -326,12 +346,23 @@ A user-defined class can be defined as a generic class.
        def log(self, message: str) -> None:
            self.logger.info('%s: %s', self.name, message)
 
-``Generic[T]`` as a base class defines that the class ``LoggedVar`` takes a
-single type parameter ``T`` . This also makes ``T`` valid as a type within the
-class body.
+This syntax indicates that the class ``LoggedVar`` is parameterised around a
+single :class:`type variable <TypeVar>` ``T`` . This also makes ``T`` valid as
+a type within the class body.
+
+Generic classes implicitly inherit from :class:`Generic`. For compatibility
+with Python 3.11 and lower, it is also possible to inherit explicitly from
+:class:`Generic` to indicate a generic class::
+
+   from typing import TypeVar, Generic
+
+   T = TypeVar('T')
+
+   class LoggedVar(Generic[T]):
+       ...
 
-The :class:`Generic` base class defines :meth:`~object.__class_getitem__` so
-that ``LoggedVar[T]`` is valid as a type::
+Generic classes have :meth:`~object.__class_getitem__` methods, meaning they
+can be parameterised at runtime (e.g. ``LoggedVar[int]`` below)::
 
    from collections.abc import Iterable
 
@@ -344,11 +375,14 @@ A generic type can have any number of type variables. All varieties of
 
    from typing import TypeVar, Generic, Sequence
 
-   T = TypeVar('T', contravariant=True)
-   B = TypeVar('B', bound=Sequence[bytes], covariant=True)
-   S = TypeVar('S', int, str)
+   class WeirdTrio[T, B: Sequence[bytes], S: (int, str)]:
+       ...
+
+   OldT = TypeVar('OldT', contravariant=True)
+   OldB = TypeVar('OldB', bound=Sequence[bytes], covariant=True)
+   OldS = TypeVar('OldS', int, str)
 
-   class WeirdTrio(Generic[T, B, S]):
+   class OldWeirdTrio(Generic[OldT, OldB, OldS]):
        ...
 
 Each type variable argument to :class:`Generic` must be distinct.
@@ -357,29 +391,26 @@ This is thus invalid::
    from typing import TypeVar, Generic
    ...
 
+   class Pair[M, M]:  # SyntaxError
+       ...
+
    T = TypeVar('T')
 
    class Pair(Generic[T, T]):   # INVALID
        ...
 
-You can use multiple inheritance with :class:`Generic`::
+Generic classes can also inherit from other classes::
 
    from collections.abc import Sized
-   from typing import TypeVar, Generic
 
-   T = TypeVar('T')
-
-   class LinkedList(Sized, Generic[T]):
+   class LinkedList[T](Sized):
        ...
 
-When inheriting from generic classes, some type variables could be fixed::
+When inheriting from generic classes, some type parameters could be fixed::
 
     from collections.abc import Mapping
-    from typing import TypeVar
-
-    T = TypeVar('T')
 
-    class MyDict(Mapping[str, T]):
+    class MyDict[T](Mapping[str, T]):
         ...
 
 In this case ``MyDict`` has a single parameter, ``T``.
@@ -392,49 +423,66 @@ not generic but implicitly inherits from ``Iterable[Any]``::
 
    class MyIterable(Iterable): # Same as Iterable[Any]
 
-User defined generic type aliases are also supported. Examples::
+User-defined generic type aliases are also supported. Examples::
 
    from collections.abc import Iterable
-   from typing import TypeVar
-   S = TypeVar('S')
-   Response = Iterable[S] | int
+
+   type Response[S] = Iterable[S] | int
 
    # Return type here is same as Iterable[str] | int
    def response(query: str) -> Response[str]:
        ...
 
-   T = TypeVar('T', int, float, complex)
-   Vec = Iterable[tuple[T, T]]
+   type Vec[T] = Iterable[tuple[T, T]]
 
-   def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
+   def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
        return sum(x*y for x, y in v)
 
+For backward compatibility, generic type aliases can also be created
+through a simple assignment::
+
+   from collections.abc import Iterable
+   from typing import TypeVar
+
+   S = TypeVar("S")
+   Response = Iterable[S] | int
+
 .. versionchanged:: 3.7
     :class:`Generic` no longer has a custom metaclass.
 
+.. versionchanged:: 3.12
+   Syntactic support for generics and type aliases is new in version 3.12.
+   Previously, generic classes had to explicitly inherit from :class:`Generic`
+   or contain a type variable in one of their bases.
+
 User-defined generics for parameter expressions are also supported via parameter
-specification variables in the form ``Generic[P]``.  The behavior is consistent
+specification variables in the form ``[**P]``.  The behavior is consistent
 with type variables' described above as parameter specification variables are
 treated by the typing module as a specialized type variable.  The one exception
 to this is that a list of types can be used to substitute a :class:`ParamSpec`::
 
-   >>> from typing import Generic, ParamSpec, TypeVar
-
-   >>> T = TypeVar('T')
-   >>> P = ParamSpec('P')
-
-   >>> class Z(Generic[T, P]): ...
+   >>> class Z[T, **P]: ...  # T is a TypeVar; P is a ParamSpec
    ...
    >>> Z[int, [dict, float]]
    __main__.Z[int, [dict, float]]
 
+Classes generic over a :class:`ParamSpec` can also be created using explicit
+inheritance from :class:`Generic`. In this case, ``**`` is not used::
 
-Furthermore, a generic with only one parameter specification variable will accept
+   from typing import ParamSpec, Generic
+
+   P = ParamSpec('P')
+
+   class Z(Generic[P]):
+       ...
+
+Another difference between :class:`TypeVar` and :class:`ParamSpec` is that a
+generic with only one parameter specification variable will accept
 parameter lists in the forms ``X[[Type1, Type2, ...]]`` and also
 ``X[Type1, Type2, ...]`` for aesthetic reasons.  Internally, the latter is converted
 to the former, so the following are equivalent::
 
-   >>> class X(Generic[P]): ...
+   >>> class X[**P]: ...
    ...
    >>> X[int, str]
    __main__.X[[int, str]]
@@ -670,20 +718,20 @@ These can be used as types in annotations and do not support ``[]``.
    This can be used to define a function that should never be
    called, or a function that never returns::
 
-     from typing import Never
+      from typing import Never
 
-     def never_call_me(arg: Never) -> None:
-         pass
+      def never_call_me(arg: Never) -> None:
+          pass
 
-     def int_or_str(arg: int | str) -> None:
-         never_call_me(arg)  # type checker error
-         match arg:
-             case int():
-                 print("It's an int")
-             case str():
-                 print("It's a str")
-             case _:
-                 never_call_me(arg)  # ok, arg is of type Never
+      def int_or_str(arg: int | str) -> None:
+          never_call_me(arg)  # type checker error
+          match arg:
+              case int():
+                  print("It's an int")
+              case str():
+                  print("It's a str")
+              case _:
+                  never_call_me(arg)  # ok, arg is of type Never
 
    .. versionadded:: 3.11
 
@@ -717,9 +765,9 @@ These can be used as types in annotations and do not support ``[]``.
       from typing import Self
 
       class Foo:
-         def return_self(self) -> Self:
-            ...
-            return self
+          def return_self(self) -> Self:
+              ...
+              return self
 
 
    This annotation is semantically equivalent to the following,
@@ -730,16 +778,16 @@ These can be used as types in annotations and do not support ``[]``.
       Self = TypeVar("Self", bound="Foo")
 
       class Foo:
-         def return_self(self: Self) -> Self:
-            ...
-            return self
+          def return_self(self: Self) -> Self:
+              ...
+              return self
 
    In general if something currently follows the pattern of::
 
       class Foo:
-         def return_self(self) -> "Foo":
-            ...
-            return self
+          def return_self(self) -> "Foo":
+              ...
+              return self
 
    You should use :data:`Self` as calls to ``SubclassOfFoo.return_self`` would have
    ``Foo`` as the return type and not ``SubclassOfFoo``.
@@ -767,6 +815,15 @@ These can be used as types in annotations and do not support ``[]``.
 
    .. versionadded:: 3.10
 
+   .. deprecated:: 3.12
+      :data:`TypeAlias` is deprecated in favor of the :keyword:`type` statement,
+      which creates instances of :class:`TypeAliasType`.
+      Note that while :data:`TypeAlias` and :class:`TypeAliasType` serve
+      similar purposes and have similar names, they are distinct and the
+      latter is not the type of the former.
+      Removal of :data:`TypeAlias` is not currently planned, but users
+      are encouraged to migrate to :keyword:`type` statements.
+
 Special forms
 """""""""""""
 
@@ -1255,8 +1312,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
       from typing import TypedDict, Unpack
 
       class Movie(TypedDict):
-         name: str
-         year: int
+          name: str
+          year: int
 
       # This function expects two keyword arguments - `name` of type `str`
       # and `year` of type `int`.
@@ -1266,213 +1323,327 @@ These can be used as types in annotations using ``[]``, each having a unique syn
 
    .. versionadded:: 3.11
 
-Building generic types
-""""""""""""""""""""""
+Building generic types and type aliases
+"""""""""""""""""""""""""""""""""""""""
+
+The following objects are not used directly in annotations. Instead, they are building blocks
+for creating generic types and type aliases.
 
-These are not used in annotations. They are building blocks for creating generic types.
+These objects can be created through special syntax
+(:ref:`type parameter lists <type-params>` and the :keyword:`type` statement).
+For compatibility with Python 3.11 and earlier, they can also be created
+without the dedicated syntax, as documented below.
 
 .. class:: Generic
 
    Abstract base class for generic types.
 
-   A generic type is typically declared by inheriting from an
-   instantiation of this class with one or more type variables.
-   For example, a generic mapping type might be defined as::
+   A generic type is typically declared by adding a list of type parameters
+   after the class name::
 
-      class Mapping(Generic[KT, VT]):
+      class Mapping[KT, VT]:
           def __getitem__(self, key: KT) -> VT:
               ...
               # Etc.
 
-   This class can then be used as follows::
+   Such a class implicitly inherits from ``Generic``.
+   The runtime semantics of this syntax are discussed in the
+   :ref:`Language Reference <generic-classes>`.
 
-      X = TypeVar('X')
-      Y = TypeVar('Y')
+   This class can then be used as follows::
 
-      def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
+      def lookup_name[X, Y](mapping: Mapping[X, Y], key: X, default: Y) -> Y:
           try:
               return mapping[key]
           except KeyError:
               return default
 
-.. class:: TypeVar
+   Here the brackets after the function name indicate a
+   :ref:`generic function <generic-functions>`.
 
-    Type variable.
+   For backwards compatibility, generic classes can also be
+   declared by explicitly inheriting from
+   ``Generic``. In this case, the type parameters must be declared
+   separately::
 
-    Usage::
+      KT = TypeVar('KT')
+      VT = TypeVar('VT')
+
+      class Mapping(Generic[KT, VT]):
+          def __getitem__(self, key: KT) -> VT:
+              ...
+              # Etc.
+
+.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False)
+
+   Type variable.
+
+   The preferred way to construct a type variable is via the dedicated syntax
+   for :ref:`generic functions <generic-functions>`,
+   :ref:`generic classes <generic-classes>`, and
+   :ref:`generic type aliases <generic-type-aliases>`::
+
+      class Sequence[T]:  # T is a TypeVar
+          ...
+
+   This syntax can also be used to create bound and constrained type
+   variables::
+
+      class StrSequence[S: str]:  # S is a TypeVar bound to str
+          ...
+
+
+      class StrOrBytesSequence[A: (str, bytes)]:  # A is a TypeVar constrained to str or bytes
+          ...
+
+   However, if desired, reusable type variables can also be constructed manually, like so::
 
       T = TypeVar('T')  # Can be anything
       S = TypeVar('S', bound=str)  # Can be any subtype of str
       A = TypeVar('A', str, bytes)  # Must be exactly str or bytes
 
-    Type variables exist primarily for the benefit of static type
-    checkers.  They serve as the parameters for generic types as well
-    as for generic function definitions.  See :class:`Generic` for more
-    information on generic types.  Generic functions work as follows::
+   Type variables exist primarily for the benefit of static type
+   checkers.  They serve as the parameters for generic types as well
+   as for generic function and type alias definitions.
+   See :class:`Generic` for more
+   information on generic types.  Generic functions work as follows::
+
+      def repeat[T](x: T, n: int) -> Sequence[T]:
+          """Return a list containing n references to x."""
+          return [x]*n
+
+
+      def print_capitalized[S: str](x: S) -> S:
+          """Print x capitalized, and return x."""
+          print(x.capitalize())
+          return x
 
-       def repeat(x: T, n: int) -> Sequence[T]:
-           """Return a list containing n references to x."""
-           return [x]*n
 
+      def concatenate[A: (str, bytes)](x: A, y: A) -> A:
+          """Add two strings or bytes objects together."""
+          return x + y
+
+   Note that type variables can be *bound*, *constrained*, or neither, but
+   cannot be both bound *and* constrained.
+
+   The variance of type variables is inferred by type checkers when they are created
+   through the :ref:`type parameter syntax <type-params>` or when
+   ``infer_variance=True`` is passed.
+   Manually created type variables may be explicitly marked covariant or contravariant by passing
+   ``covariant=True`` or ``contravariant=True``.
+   By default, manually created type variables are invariant.
+   See :pep:`484` and :pep:`695` for more details.
+
+   Bound type variables and constrained type variables have different
+   semantics in several important ways. Using a *bound* type variable means
+   that the ``TypeVar`` will be solved using the most specific type possible::
+
+      x = print_capitalized('a string')
+      reveal_type(x)  # revealed type is str
+
+      class StringSubclass(str):
+          pass
+
+      y = print_capitalized(StringSubclass('another string'))
+      reveal_type(y)  # revealed type is StringSubclass
+
+      z = print_capitalized(45)  # error: int is not a subtype of str
+
+   Type variables can be bound to concrete types, abstract types (ABCs or
+   protocols), and even unions of types::
+
+      # Can be anything with an __abs__ method
+      def print_abs[T: SupportsAbs](arg: T) -> None:
+          print("Absolute value:", abs(arg))
+
+      U = TypeVar('U', bound=str|bytes)  # Can be any subtype of the union str|bytes
+      V = TypeVar('V', bound=SupportsAbs)  # Can be anything with an __abs__ method
+
+   .. _typing-constrained-typevar:
+
+   Using a *constrained* type variable, however, means that the ``TypeVar``
+   can only ever be solved as being exactly one of the constraints given::
+
+      a = concatenate('one', 'two')
+      reveal_type(a)  # revealed type is str
+
+      b = concatenate(StringSubclass('one'), StringSubclass('two'))
+      reveal_type(b)  # revealed type is str, despite StringSubclass being passed in
+
+      c = concatenate('one', b'two')  # error: type variable 'A' can be either str or bytes in a function call, but not both
+
+   At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.
+
+   .. attribute:: __name__
+
+      The name of the type variable.
 
-       def print_capitalized(x: S) -> S:
-           """Print x capitalized, and return x."""
-           print(x.capitalize())
-           return x
+   .. attribute:: __covariant__
 
+      Whether the type var has been explicitly marked as covariant.
 
-       def concatenate(x: A, y: A) -> A:
-           """Add two strings or bytes objects together."""
-           return x + y
+   .. attribute:: __contravariant__
 
-    Note that type variables can be *bound*, *constrained*, or neither, but
-    cannot be both bound *and* constrained.
+      Whether the type var has been explicitly marked as contravariant.
 
-    Bound type variables and constrained type variables have different
-    semantics in several important ways. Using a *bound* type variable means
-    that the ``TypeVar`` will be solved using the most specific type possible::
+   .. attribute:: __infer_variance__
 
-       x = print_capitalized('a string')
-       reveal_type(x)  # revealed type is str
+      Whether the type variable's variance should be inferred by type checkers.
 
-       class StringSubclass(str):
-           pass
+      .. versionadded:: 3.12
 
-       y = print_capitalized(StringSubclass('another string'))
-       reveal_type(y)  # revealed type is StringSubclass
+   .. attribute:: __bound__
 
-       z = print_capitalized(45)  # error: int is not a subtype of str
+      The bound of the type variable, if any.
 
-    Type variables can be bound to concrete types, abstract types (ABCs or
-    protocols), and even unions of types::
+      .. versionchanged:: 3.12
 
-       U = TypeVar('U', bound=str|bytes)  # Can be any subtype of the union str|bytes
-       V = TypeVar('V', bound=SupportsAbs)  # Can be anything with an __abs__ method
+         For type variables created through :ref:`type parameter syntax <type-params>`,
+         the bound is evaluated only when the attribute is accessed, not when
+         the type variable is created (see :ref:`lazy-evaluation`).
 
-.. _typing-constrained-typevar:
+   .. attribute:: __constraints__
 
-    Using a *constrained* type variable, however, means that the ``TypeVar``
-    can only ever be solved as being exactly one of the constraints given::
+      A tuple containing the constraints of the type variable, if any.
 
-       a = concatenate('one', 'two')
-       reveal_type(a)  # revealed type is str
+      .. versionchanged:: 3.12
 
-       b = concatenate(StringSubclass('one'), StringSubclass('two'))
-       reveal_type(b)  # revealed type is str, despite StringSubclass being passed in
+         For type variables created through :ref:`type parameter syntax <type-params>`,
+         the constraints are evaluated only when the attribute is accessed, not when
+         the type variable is created (see :ref:`lazy-evaluation`).
 
-       c = concatenate('one', b'two')  # error: type variable 'A' can be either str or bytes in a function call, but not both
+   .. versionchanged:: 3.12
+
+      Type variables can now be declared using the
+      :ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
+      The ``infer_variance`` parameter was added.
+
+.. class:: TypeVarTuple(name)
+
+   Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
+   that enables *variadic* generics.
+
+   Type variable tuples can be declared in :ref:`type parameter lists <type-params>`
+   using a single asterisk (``*``) before the name::
 
-    At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.  In general,
-    :func:`isinstance` and :func:`issubclass` should not be used with types.
+      def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
+          return (*tup[1:], tup[0])
 
-    Type variables may be marked covariant or contravariant by passing
-    ``covariant=True`` or ``contravariant=True``.  See :pep:`484` for more
-    details.  By default, type variables are invariant.
+   Or by explicitly invoking the ``TypeVarTuple`` constructor::
 
-.. class:: TypeVarTuple
+      T = TypeVar("T")
+      Ts = TypeVarTuple("Ts")
+
+      def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
+          return (*tup[1:], tup[0])
 
-    Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
-    that enables *variadic* generics.
+   A normal type variable enables parameterization with a single type. A type
+   variable tuple, in contrast, allows parameterization with an
+   *arbitrary* number of types by acting like an *arbitrary* number of type
+   variables wrapped in a tuple. For example::
 
-    A normal type variable enables parameterization with a single type. A type
-    variable tuple, in contrast, allows parameterization with an
-    *arbitrary* number of types by acting like an *arbitrary* number of type
-    variables wrapped in a tuple. For example::
+      # T is bound to int, Ts is bound to ()
+      # Return value is (1,), which has type tuple[int]
+      move_first_element_to_last(tup=(1,))
 
-        T = TypeVar('T')
-        Ts = TypeVarTuple('Ts')
+      # T is bound to int, Ts is bound to (str,)
+      # Return value is ('spam', 1), which has type tuple[str, int]
+      move_first_element_to_last(tup=(1, 'spam'))
 
-        def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
-            return (*tup[1:], tup[0])
+      # T is bound to int, Ts is bound to (str, float)
+      # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
+      move_first_element_to_last(tup=(1, 'spam', 3.0))
 
-        # T is bound to int, Ts is bound to ()
-        # Return value is (1,), which has type tuple[int]
-        move_first_element_to_last(tup=(1,))
+      # This fails to type check (and fails at runtime)
+      # because tuple[()] is not compatible with tuple[T, *Ts]
+      # (at least one element is required)
+      move_first_element_to_last(tup=())
 
-        # T is bound to int, Ts is bound to (str,)
-        # Return value is ('spam', 1), which has type tuple[str, int]
-        move_first_element_to_last(tup=(1, 'spam'))
+   Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
+   Conceptually, you can think of ``Ts`` as a tuple of type variables
+   ``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
+   ``tuple[T, *(T1, T2, ...)]``, which is equivalent to
+   ``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
+   see this written using :data:`Unpack <Unpack>` instead, as
+   ``Unpack[Ts]``.)
 
-        # T is bound to int, Ts is bound to (str, float)
-        # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int]
-        move_first_element_to_last(tup=(1, 'spam', 3.0))
+   Type variable tuples must *always* be unpacked. This helps distinguish type
+   variable tuples from normal type variables::
 
-        # This fails to type check (and fails at runtime)
-        # because tuple[()] is not compatible with tuple[T, *Ts]
-        # (at least one element is required)
-        move_first_element_to_last(tup=())
+      x: Ts          # Not valid
+      x: tuple[Ts]   # Not valid
+      x: tuple[*Ts]  # The correct way to do it
 
-    Note the use of the unpacking operator ``*`` in ``tuple[T, *Ts]``.
-    Conceptually, you can think of ``Ts`` as a tuple of type variables
-    ``(T1, T2, ...)``. ``tuple[T, *Ts]`` would then become
-    ``tuple[T, *(T1, T2, ...)]``, which is equivalent to
-    ``tuple[T, T1, T2, ...]``. (Note that in older versions of Python, you might
-    see this written using :data:`Unpack <Unpack>` instead, as
-    ``Unpack[Ts]``.)
+   Type variable tuples can be used in the same contexts as normal type
+   variables. For example, in class definitions, arguments, and return types::
 
-    Type variable tuples must *always* be unpacked. This helps distinguish type
-    variable tuples from normal type variables::
+      class Array[*Shape]:
+          def __getitem__(self, key: tuple[*Shape]) -> float: ...
+          def __abs__(self) -> "Array[*Shape]": ...
+          def get_shape(self) -> tuple[*Shape]: ...
 
-        x: Ts          # Not valid
-        x: tuple[Ts]   # Not valid
-        x: tuple[*Ts]  # The correct way to do it
+   Type variable tuples can be happily combined with normal type variables::
 
-    Type variable tuples can be used in the same contexts as normal type
-    variables. For example, in class definitions, arguments, and return types::
+      DType = TypeVar('DType')
 
-        Shape = TypeVarTuple('Shape')
-        class Array(Generic[*Shape]):
-            def __getitem__(self, key: tuple[*Shape]) -> float: ...
-            def __abs__(self) -> "Array[*Shape]": ...
-            def get_shape(self) -> tuple[*Shape]: ...
+      class Array[DType, *Shape]:  # This is fine
+          pass
 
-    Type variable tuples can be happily combined with normal type variables::
+      class Array2[*Shape, DType]:  # This would also be fine
+          pass
 
-        DType = TypeVar('DType')
+      float_array_1d: Array[float, Height] = Array()     # Totally fine
+      int_array_2d: Array[int, Height, Width] = Array()  # Yup, fine too
 
-        class Array(Generic[DType, *Shape]):  # This is fine
-            pass
+   However, note that at most one type variable tuple may appear in a single
+   list of type arguments or type parameters::
 
-        class Array2(Generic[*Shape, DType]):  # This would also be fine
-            pass
+      x: tuple[*Ts, *Ts]            # Not valid
+      class Array[*Shape, *Shape]:  # Not valid
+          pass
 
-        float_array_1d: Array[float, Height] = Array()     # Totally fine
-        int_array_2d: Array[int, Height, Width] = Array()  # Yup, fine too
+   Finally, an unpacked type variable tuple can be used as the type annotation
+   of ``*args``::
 
-    However, note that at most one type variable tuple may appear in a single
-    list of type arguments or type parameters::
+      def call_soon[*Ts](
+               callback: Callable[[*Ts], None],
+               *args: *Ts
+      ) -> None:
+          ...
+          callback(*args)
 
-        x: tuple[*Ts, *Ts]                     # Not valid
-        class Array(Generic[*Shape, *Shape]):  # Not valid
-            pass
+   In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
+   which would specify that *all* arguments are ``int`` - ``*args: *Ts``
+   enables reference to the types of the *individual* arguments in ``*args``.
+   Here, this allows us to ensure the types of the ``*args`` passed
+   to ``call_soon`` match the types of the (positional) arguments of
+   ``callback``.
 
-    Finally, an unpacked type variable tuple can be used as the type annotation
-    of ``*args``::
+   See :pep:`646` for more details on type variable tuples.
+
+   .. attribute:: __name__
 
-        def call_soon(
-                callback: Callable[[*Ts], None],
-                *args: *Ts
-        ) -> None:
-            ...
-            callback(*args)
+      The name of the type variable tuple.
 
-    In contrast to non-unpacked annotations of ``*args`` - e.g. ``*args: int``,
-    which would specify that *all* arguments are ``int`` - ``*args: *Ts``
-    enables reference to the types of the *individual* arguments in ``*args``.
-    Here, this allows us to ensure the types of the ``*args`` passed
-    to ``call_soon`` match the types of the (positional) arguments of
-    ``callback``.
+   .. versionadded:: 3.11
 
-    See :pep:`646` for more details on type variable tuples.
+   .. versionchanged:: 3.12
 
-    .. versionadded:: 3.11
+      Type variable tuples can now be declared using the
+      :ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
 
 .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
 
    Parameter specification variable.  A specialized version of
    :class:`type variables <TypeVar>`.
 
-   Usage::
+   In :ref:`type parameter lists <type-params>`, parameter specifications
+   can be declared with two asterisks (``**``)::
+
+      type IntFunc[**P] = Callable[P, int]
+
+   For compatibility with Python 3.11 and earlier, ``ParamSpec`` objects
+   can also be created as follows::
 
       P = ParamSpec('P')
 
@@ -1489,13 +1660,9 @@ These are not used in annotations. They are building blocks for creating generic
    new callable returned by it have inter-dependent type parameters::
 
       from collections.abc import Callable
-      from typing import TypeVar, ParamSpec
       import logging
 
-      T = TypeVar('T')
-      P = ParamSpec('P')
-
-      def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+      def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]:
           '''A type-safe decorator to add logging to a function.'''
           def inner(*args: P.args, **kwargs: P.kwargs) -> T:
               logging.info(f'{f.__name__} was called')
@@ -1530,6 +1697,10 @@ These are not used in annotations. They are building blocks for creating generic
       ``P.args`` and ``P.kwargs`` are instances respectively of
       :class:`ParamSpecArgs` and :class:`ParamSpecKwargs`.
 
+   .. attribute:: __name__
+
+      The name of the parameter specification.
+
    Parameter specification variables created with ``covariant=True`` or
    ``contravariant=True`` can be used to declare covariant or contravariant
    generic types.  The ``bound`` argument is also accepted, similar to
@@ -1538,6 +1709,11 @@ These are not used in annotations. They are building blocks for creating generic
 
    .. versionadded:: 3.10
 
+   .. versionchanged:: 3.12
+
+      Parameter specifications can now be declared using the
+      :ref:`type parameter <type-params>` syntax introduced by :pep:`695`.
+
    .. note::
       Only parameter specification variables defined in global scope can
       be pickled.
@@ -1565,6 +1741,67 @@ These are not used in annotations. They are building blocks for creating generic
    .. versionadded:: 3.10
 
 
+.. class:: TypeAliasType(name, value, *, type_params=())
+
+   The type of type aliases created through the :keyword:`type` statement.
+
+   Example::
+
+      >>> type Alias = int
+      >>> type(Alias)
+      <class 'typing.TypeAliasType'>
+
+   .. versionadded:: 3.12
+
+   .. attribute:: __name__
+
+      The name of the type alias::
+
+         >>> type Alias = int
+         >>> Alias.__name__
+         'Alias'
+
+   .. attribute:: __module__
+
+      The module in which the type alias was defined::
+
+         >>> type Alias = int
+         >>> Alias.__module__
+         '__main__'
+
+   .. attribute:: __type_params__
+
+      The type parameters of the type alias, or an empty tuple if the alias is
+      not generic:
+
+      .. doctest::
+
+         >>> type ListOrSet[T] = list[T] | set[T]
+         >>> ListOrSet.__type_params__
+         (T,)
+         >>> type NotGeneric = int
+         >>> NotGeneric.__type_params__
+         ()
+
+   .. attribute:: __value__
+
+      The type alias's value. This is :ref:`lazily evaluated <lazy-evaluation>`,
+      so names used in the definition of the alias are not resolved until the
+      ``__value__`` attribute is accessed:
+
+      .. doctest::
+
+         >>> type Mutually = Recursive
+         >>> type Recursive = Mutually
+         >>> Mutually
+         Mutually
+         >>> Recursive
+         Recursive
+         >>> Mutually.__value__
+         Recursive
+         >>> Recursive.__value__
+         Mutually
+
 Other special directives
 """"""""""""""""""""""""
 
@@ -1613,12 +1850,18 @@ These are not used in annotations. They are building blocks for declaring types.
 
    ``NamedTuple`` subclasses can be generic::
 
-      class Group(NamedTuple, Generic[T]):
+      class Group[T](NamedTuple):
           key: T
           group: list[T]
 
    Backward-compatible usage::
 
+       # For creating a generic NamedTuple on Python 3.11 or lower
+       class Group(NamedTuple, Generic[T]):
+           key: T
+           group: list[T]
+
+       # A functional syntax is also supported
        Employee = NamedTuple('Employee', [('name', str), ('id', int)])
 
    .. versionchanged:: 3.6
@@ -1692,6 +1935,15 @@ These are not used in annotations. They are building blocks for declaring types.
 
    Protocol classes can be generic, for example::
 
+      class GenProto[T](Protocol):
+          def meth(self) -> T:
+              ...
+
+   In code that needs to be compatible with Python 3.11 or older, generic
+   Protocols can be written as follows::
+
+      T = TypeVar("T")
+
       class GenProto(Protocol[T]):
           def meth(self) -> T:
               ...
@@ -1887,6 +2139,13 @@ These are not used in annotations. They are building blocks for declaring types.
 
    A ``TypedDict`` can be generic::
 
+      class Group[T](TypedDict):
+          key: T
+          group: list[T]
+
+   To create a generic ``TypedDict`` that is compatible with Python 3.11
+   or lower, inherit from :class:`Generic` explicitly::
+
       class Group(TypedDict, Generic[T]):
           key: T
           group: list[T]
@@ -1985,12 +2244,10 @@ Corresponding to built-in types
 
    This type may be used as follows::
 
-      T = TypeVar('T', int, float)
-
-      def vec2(x: T, y: T) -> List[T]:
+      def vec2[T: (int, float)](x: T, y: T) -> List[T]:
           return [x, y]
 
-      def keep_positives(vector: Sequence[T]) -> List[T]:
+      def keep_positives[T: (int, float)](vector: Sequence[T]) -> List[T]:
           return [item for item in vector if item > 0]
 
    .. deprecated:: 3.9
@@ -2189,8 +2446,8 @@ Corresponding to collections in :mod:`collections.abc`
    A generic version of :class:`collections.abc.Mapping`.
    This type can be used as follows::
 
-     def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
-         return word_list[word]
+      def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
+          return word_list[word]
 
    .. deprecated:: 3.9
       :class:`collections.abc.Mapping` now supports subscripting (``[]``).
@@ -3006,3 +3263,5 @@ convenience. This is subject to change, and not all deprecations are listed.
 |  ``typing.Hashable`` and         | 3.12          | Undecided         | :gh:`94309`    |
 |  ``typing.Sized``                |               |                   |                |
 +----------------------------------+---------------+-------------------+----------------+
+|  ``typing.TypeAlias``            | 3.12          | Undecided         | :pep:`695`     |
++----------------------------------+---------------+-------------------+----------------+
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 9d1e5b6c596d..6d30eccab199 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -1206,7 +1206,7 @@ A function definition defines a user-defined function object (see section
 :ref:`types`):
 
 .. productionlist:: python-grammar
-   funcdef: [`decorators`] "def" `funcname` "(" [`parameter_list`] ")"
+   funcdef: [`decorators`] "def" `funcname` [`type_params`] "(" [`parameter_list`] ")"
           : ["->" `expression`] ":" `suite`
    decorators: `decorator`+
    decorator: "@" `assignment_expression` NEWLINE
@@ -1256,6 +1256,15 @@ except that the original function is not temporarily bound to the name ``func``.
    :token:`~python-grammar:assignment_expression`. Previously, the grammar was
    much more restrictive; see :pep:`614` for details.
 
+A list of :ref:`type parameters <type-params>` may be given in square brackets
+between the function's name and the opening parenthesis for its parameter list.
+This indicates to static type checkers that the function is generic. At runtime,
+the type parameters can be retrieved from the function's ``__type_params__``
+attribute. See :ref:`generic-functions` for more.
+
+.. versionchanged:: 3.12
+   Type parameter lists are new in Python 3.12.
+
 .. index::
    triple: default; parameter; value
    single: argument; function definition
@@ -1378,7 +1387,7 @@ Class definitions
 A class definition defines a class object (see section :ref:`types`):
 
 .. productionlist:: python-grammar
-   classdef: [`decorators`] "class" `classname` [`inheritance`] ":" `suite`
+   classdef: [`decorators`] "class" `classname` [`type_params`] [`inheritance`] ":" `suite`
    inheritance: "(" [`argument_list`] ")"
    classname: `identifier`
 
@@ -1434,6 +1443,15 @@ decorators.  The result is then bound to the class name.
    :token:`~python-grammar:assignment_expression`. Previously, the grammar was
    much more restrictive; see :pep:`614` for details.
 
+A list of :ref:`type parameters <type-params>` may be given in square brackets
+immediately after the class's name.
+This indicates to static type checkers that the class is generic. At runtime,
+the type parameters can be retrieved from the class's ``__type_params__``
+attribute. See :ref:`generic-classes` for more.
+
+.. versionchanged:: 3.12
+   Type parameter lists are new in Python 3.12.
+
 **Programmer's note:** Variables defined in the class definition are class
 attributes; they are shared by instances.  Instance attributes can be set in a
 method with ``self.name = value``.  Both class and instance attributes are
@@ -1589,6 +1607,228 @@ body of a coroutine function.
       The proposal that made coroutines a proper standalone concept in Python,
       and added supporting syntax.
 
+.. _type-params:
+
+Type parameter lists
+====================
+
+.. versionadded:: 3.12
+
+.. index::
+   single: type parameters
+
+.. productionlist:: python-grammar
+   type_params: "[" `type_param` ("," `type_param`)* "]"
+   type_param: `typevar` | `typevartuple` | `paramspec`
+   typevar: `identifier` (":" `expression`)?
+   typevartuple: "*" `identifier`
+   paramspec: "**" `identifier`
+
+:ref:`Functions <def>` (including :ref:`coroutines <async def>`),
+:ref:`classes <class>` and :ref:`type aliases <type>` may
+contain a type parameter list::
+
+   def max[T](args: list[T]) -> T:
+       ...
+
+   async def amax[T](args: list[T]) -> T:
+       ...
+
+   class Bag[T]:
+       def __iter__(self) -> Iterator[T]:
+           ...
+
+       def add(self, arg: T) -> None:
+           ...
+
+   type ListOrSet[T] = list[T] | set[T]
+
+Semantically, this indicates that the function, class, or type alias is
+generic over a type variable. This information is primarily used by static
+type checkers, and at runtime, generic objects behave much like their
+non-generic counterparts.
+
+Type parameters are declared in square brackets (``[]``) immediately
+after the name of the function, class, or type alias. The type parameters
+are accessible within the scope of the generic object, but not elsewhere.
+Thus, after a declaration ``def func[T](): pass``, the name ``T`` is not available in
+the module scope. Below, the semantics of generic objects are described
+with more precision. The scope of type parameters is modeled with a special
+function (technically, an :ref:`annotation scope <annotation-scopes>`) that
+wraps the creation of the generic object.
+
+Generic functions, classes, and type aliases have a :attr:`!__type_params__`
+attribute listing their type parameters.
+
+Type parameters come in three kinds:
+
+* :data:`typing.TypeVar`, introduced by a plain name (e.g., ``T``). Semantically, this
+  represents a single type to a type checker.
+* :data:`typing.TypeVarTuple`, introduced by a name prefixed with a single
+  asterisk (e.g., ``*Ts``). Semantically, this stands for a tuple of any
+  number of types.
+* :data:`typing.ParamSpec`, introduced by a name prefixed with two asterisks
+  (e.g., ``**P``). Semantically, this stands for the parameters of a callable.
+
+:data:`typing.TypeVar` declarations can define *bounds* and *constraints* with
+a colon (``:``) followed by an expression. A single expression after the colon
+indicates a bound (e.g. ``T: int``). Semantically, this means
+that the :data:`!typing.TypeVar` can only represent types that are a subtype of
+this bound. A parenthesized tuple of expressions after the colon indicates a
+set of constraints (e.g. ``T: (str, bytes)``). Each member of the tuple should be a
+type (again, this is not enforced at runtime). Constrained type variables can only
+take on one of the types in the list of constraints.
+
+For :data:`!typing.TypeVar`\ s declared using the type parameter list syntax,
+the bound and constraints are not evaluated when the generic object is created,
+but only when the value is explicitly accessed through the attributes ``__bound__``
+and ``__constraints__``. To accomplish this, the bounds or constraints are
+evaluated in a separate :ref:`annotation scope <annotation-scopes>`.
+
+:data:`typing.TypeVarTuple`\ s and :data:`typing.ParamSpec`\ s cannot have bounds
+or constraints.
+
+The following example indicates the full set of allowed type parameter declarations::
+
+   def overly_generic[
+      SimpleTypeVar,
+      TypeVarWithBound: int,
+      TypeVarWithConstraints: (str, bytes),
+      *SimpleTypeVarTuple,
+      **SimpleParamSpec,
+   ](
+      a: SimpleTypeVar,
+      b: TypeVarWithBound,
+      c: Callable[SimpleParamSpec, TypeVarWithConstraints],
+      *d: SimpleTypeVarTuple,
+   ): ...
+
+.. _generic-functions:
+
+Generic functions
+-----------------
+
+Generic functions are declared as follows::
+
+   def func[T](arg: T): ...
+
+This syntax is equivalent to::
+
+   annotation-def TYPE_PARAMS_OF_func():
+       T = typing.TypeVar("T")
+       def func(arg: T): ...
+       func.__type_params__ = (T,)
+       return func
+   func = TYPE_PARAMS_OF_func()
+
+Here ``annotation-def`` indicates an :ref:`annotation scope <annotation-scopes>`,
+which is not actually bound to any name at runtime. (One
+other liberty is taken in the translation: the syntax does not go through
+attribute access on the :mod:`typing` module, but creates an instance of
+:data:`typing.TypeVar` directly.)
+
+The annotations of generic functions are evaluated within the annotation scope
+used for declaring the type parameters, but the function's defaults and
+decorators are not.
+
+The following example illustrates the scoping rules for these cases,
+as well as for additional flavors of type parameters::
+
+   @decorator
+   def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
+       ...
+
+Except for the :ref:`lazy evaluation <lazy-evaluation>` of the
+:class:`~typing.TypeVar` bound, this is equivalent to::
+
+   DEFAULT_OF_arg = some_default
+
+   annotation-def TYPE_PARAMS_OF_func():
+
+       annotation-def BOUND_OF_T():
+           return int
+       # In reality, BOUND_OF_T() is evaluated only on demand.
+       T = typing.TypeVar("T", bound=BOUND_OF_T())
+
+       Ts = typing.TypeVarTuple("Ts")
+       P = typing.ParamSpec("P")
+
+       def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg):
+           ...
+
+       func.__type_params__ = (T, Ts, P)
+       return func
+   func = decorator(TYPE_PARAMS_OF_func())
+
+The capitalized names like ``DEFAULT_OF_arg`` are not actually
+bound at runtime.
+
+.. _generic-classes:
+
+Generic classes
+---------------
+
+Generic classes are declared as follows::
+
+   class Bag[T]: ...
+
+This syntax is equivalent to::
+
+   annotation-def TYPE_PARAMS_OF_Bag():
+       T = typing.TypeVar("T")
+       class Bag(typing.Generic[T]):
+           __type_params__ = (T,)
+           ...
+       return Bag
+   Bag = TYPE_PARAMS_OF_Bag()
+
+Here again ``annotation-def`` (not a real keyword) indicates an
+:ref:`annotation scope <annotation-scopes>`, and the name
+``TYPE_PARAMS_OF_Bag`` is not actually bound at runtime.
+
+Generic classes implicitly inherit from :data:`typing.Generic`.
+The base classes and keyword arguments of generic classes are
+evaluated within the type scope for the type parameters,
+and decorators are evaluated outside that scope. This is illustrated
+by this example::
+
+   @decorator
+   class Bag(Base[T], arg=T): ...
+
+This is equivalent to::
+
+   annotation-def TYPE_PARAMS_OF_Bag():
+       T = typing.TypeVar("T")
+       class Bag(Base[T], typing.Generic[T], arg=T):
+           __type_params__ = (T,)
+           ...
+       return Bag
+   Bag = decorator(TYPE_PARAMS_OF_Bag())
+
+.. _generic-type-aliases:
+
+Generic type aliases
+--------------------
+
+The :keyword:`type` statement can also be used to create a generic type alias::
+
+   type ListOrSet[T] = list[T] | set[T]
+
+Except for the :ref:`lazy evaluation <lazy-evaluation>` of the value,
+this is equivalent to::
+
+   annotation-def TYPE_PARAMS_OF_ListOrSet():
+       T = typing.TypeVar("T")
+
+       annotation-def VALUE_OF_ListOrSet():
+           return list[T] | set[T]
+       # In reality, the value is lazily evaluated
+       return typing.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
+   ListOrSet = TYPE_PARAMS_OF_ListOrSet()
+
+Here, ``annotation-def`` (not a real keyword) indicates an
+:ref:`annotation scope <annotation-scopes>`. The capitalized names
+like ``TYPE_PARAMS_OF_ListOrSet`` are not actually bound at runtime.
 
 .. rubric:: Footnotes
 
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index c0734e49f291..e8f9775dd33c 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -499,6 +499,7 @@ Callable types
          single: __globals__ (function attribute)
          single: __annotations__ (function attribute)
          single: __kwdefaults__ (function attribute)
+         single: __type_params__ (function attribute)
          pair: global; namespace
 
       +-------------------------+-------------------------------+-----------+
@@ -561,6 +562,12 @@ Callable types
       | :attr:`__kwdefaults__`  | A dict containing defaults    | Writable  |
       |                         | for keyword-only parameters.  |           |
       +-------------------------+-------------------------------+-----------+
+      | :attr:`__type_params__` | A tuple containing the        | Writable  |
+      |                         | :ref:`type parameters         |           |
+      |                         | <type-params>` of a           |           |
+      |                         | :ref:`generic function        |           |
+      |                         | <generic-functions>`.         |           |
+      +-------------------------+-------------------------------+-----------+
 
       Most of the attributes labelled "Writable" check the type of the assigned value.
 
@@ -837,6 +844,7 @@ Custom classes
       single: __bases__ (class attribute)
       single: __doc__ (class attribute)
       single: __annotations__ (class attribute)
+      single: __type_params__ (class attribute)
 
    Special attributes:
 
@@ -863,6 +871,10 @@ Custom classes
          working with :attr:`__annotations__`, please see
          :ref:`annotations-howto`.
 
+      :attr:`__type_params__`
+         A tuple containing the :ref:`type parameters <type-params>` of
+         a :ref:`generic class <generic-classes>`.
+
 Class instances
    .. index::
       pair: object; class instance
diff --git a/Doc/reference/executionmodel.rst b/Doc/reference/executionmodel.rst
index 8917243999d3..cea3a56ba516 100644
--- a/Doc/reference/executionmodel.rst
+++ b/Doc/reference/executionmodel.rst
@@ -71,6 +71,8 @@ The following constructs bind names:
   + in a capture pattern in structural pattern matching
 
 * :keyword:`import` statements.
+* :keyword:`type` statements.
+* :ref:`type parameter lists <type-params>`.
 
 The :keyword:`!import` statement of the form ``from ... import *`` binds all
 names defined in the imported module, except those beginning with an underscore.
@@ -149,7 +151,8 @@ a global statement, the free variable is treated as a global.
 The :keyword:`nonlocal` statement causes corresponding names to refer
 to previously bound variables in the nearest enclosing function scope.
 :exc:`SyntaxError` is raised at compile time if the given name does not
-exist in any enclosing function scope.
+exist in any enclosing function scope. :ref:`Type parameters <type-params>`
+cannot be rebound with the :keyword:`!nonlocal` statement.
 
 .. index:: pair: module; __main__
 
@@ -163,14 +166,119 @@ These references follow the normal rules for name resolution with an exception
 that unbound local variables are looked up in the global namespace.
 The namespace of the class definition becomes the attribute dictionary of
 the class. The scope of names defined in a class block is limited to the
-class block; it does not extend to the code blocks of methods -- this includes
-comprehensions and generator expressions since they are implemented using a
-function scope.  This means that the following will fail::
+class block; it does not extend to the code blocks of methods. This includes
+comprehensions and generator expressions, but it does not include
+:ref:`annotation scopes <annotation-scopes>`,
+which have access to their enclosing class scopes.
+This means that the following will fail::
 
    class A:
        a = 42
        b = list(a + i for i in range(10))
 
+However, the following will succeed::
+
+   class A:
+       type Alias = Nested
+       class Nested: pass
+
+   print(A.Alias.__value__)  # <type 'A.Nested'>
+
+.. _annotation-scopes:
+
+Annotation scopes
+-----------------
+
+:ref:`Type parameter lists <type-params>` and :keyword:`type` statements
+introduce *annotation scopes*, which behave mostly like function scopes,
+but with some exceptions discussed below. :term:`Annotations <annotation>`
+currently do not use annotation scopes, but they are expected to use
+annotation scopes in Python 3.13 when :pep:`649` is implemented.
+
+Annotation scopes are used in the following contexts:
+
+* Type parameter lists for :ref:`generic type aliases <generic-type-aliases>`.
+* Type parameter lists for :ref:`generic functions <generic-functions>`.
+  A generic function's annotations are
+  executed within the annotation scope, but its defaults and decorators are not.
+* Type parameter lists for :ref:`generic classes <generic-classes>`.
+  A generic class's base classes and
+  keyword arguments are executed within the annotation scope, but its decorators are not.
+* The bounds and constraints for type variables
+  (:ref:`lazily evaluated <lazy-evaluation>`).
+* The value of type aliases (:ref:`lazily evaluated <lazy-evaluation>`).
+
+Annotation scopes differ from function scopes in the following ways:
+
+* Annotation scopes have access to their enclosing class namespace.
+  If an annotation scope is immediately within a class scope, or within another
+  annotation scope that is immediately within a class scope, the code in the
+  annotation scope can use names defined in the class scope as if it were
+  executed directly within the class body. This contrasts with regular
+  functions defined within classes, which cannot access names defined in the class scope.
+* Expressions in annotation scopes cannot contain :keyword:`yield`, ``yield from``,
+  :keyword:`await`, or :token:`:= <python-grammar:assignment_expression>`
+  expressions. (These expressions are allowed in other scopes contained within the
+  annotation scope.)
+* Names defined in annotation scopes cannot be rebound with :keyword:`nonlocal`
+  statements in inner scopes. This includes only type parameters, as no other
+  syntactic elements that can appear within annotation scopes can introduce new names.
+* While annotation scopes have an internal name, that name is not reflected in the
+  :term:`__qualname__ <qualified name>` of objects defined within the scope.
+  Instead, the :attr:`!__qualname__`
+  of such objects is as if the object were defined in the enclosing scope.
+
+.. versionadded:: 3.12
+   Annotation scopes were introduced in Python 3.12 as part of :pep:`695`.
+
+.. _lazy-evaluation:
+
+Lazy evaluation
+---------------
+
+The values of type aliases created through the :keyword:`type` statement are
+*lazily evaluated*. The same applies to the bounds and constraints of type
+variables created through the :ref:`type parameter syntax <type-params>`.
+This means that they are not evaluated when the type alias or type variable is
+created. Instead, they are only evaluated when doing so is necessary to resolve
+an attribute access.
+
+Example:
+
+.. doctest::
+
+   >>> type Alias = 1/0
+   >>> Alias.__value__
+   Traceback (most recent call last):
+     ...
+   ZeroDivisionError: division by zero
+   >>> def func[T: 1/0](): pass
+   >>> T = func.__type_params__[0]
+   >>> T.__bound__
+   Traceback (most recent call last):
+     ...
+   ZeroDivisionError: division by zero
+
+Here the exception is raised only when the ``__value__`` attribute
+of the type alias or the ``__bound__`` attribute of the type variable
+is accessed.
+
+This behavior is primarily useful for references to types that have not
+yet been defined when the type alias or type variable is created. For example,
+lazy evaluation enables creation of mutually recursive type aliases::
+
+   from typing import Literal
+
+   type SimpleExpr = int | Parenthesized
+   type Parenthesized = tuple[Literal["("], Expr, Literal[")"]]
+   type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr]
+
+Lazily evaluated values are evaluated in :ref:`annotation scope <annotation-scopes>`,
+which means that names that appear inside the lazily evaluated value are looked up
+as if they were used in the immediately enclosing scope.
+
+.. versionadded:: 3.12
+
 .. _restrict_exec:
 
 Builtins and restricted execution
diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst
index 757f887caa40..47062f86810e 100644
--- a/Doc/reference/lexical_analysis.rst
+++ b/Doc/reference/lexical_analysis.rst
@@ -361,15 +361,19 @@ Soft Keywords
 .. versionadded:: 3.10
 
 Some identifiers are only reserved under specific contexts. These are known as
-*soft keywords*.  The identifiers ``match``, ``case`` and ``_`` can
-syntactically act as keywords in contexts related to the pattern matching
-statement, but this distinction is done at the parser level, not when
-tokenizing.
+*soft keywords*.  The identifiers ``match``, ``case``, ``type`` and ``_`` can
+syntactically act as keywords in certain contexts,
+but this distinction is done at the parser level, not when tokenizing.
 
-As soft keywords, their use with pattern matching is possible while still
-preserving compatibility with existing code that uses ``match``, ``case`` and ``_`` as
+As soft keywords, their use in the grammar is possible while still
+preserving compatibility with existing code that uses these names as
 identifier names.
 
+``match``, ``case``, and ``_`` are used in the :keyword:`match` statement.
+``type`` is used in the :keyword:`type` statement.
+
+.. versionchanged:: 3.12
+   ``type`` is now a soft keyword.
 
 .. index::
    single: _, identifiers
diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst
index f7a8b44d1954..662a4b643c43 100644
--- a/Doc/reference/simple_stmts.rst
+++ b/Doc/reference/simple_stmts.rst
@@ -28,6 +28,7 @@ simple statements is:
               : | `future_stmt`
               : | `global_stmt`
               : | `nonlocal_stmt`
+              : | `type_stmt`
 
 
 .. _exprstmts:
@@ -1012,3 +1013,48 @@ pre-existing bindings in the local scope.
 
    :pep:`3104` - Access to Names in Outer Scopes
       The specification for the :keyword:`nonlocal` statement.
+
+.. _type:
+
+The :keyword:`!type` statement
+==============================
+
+.. index:: pair: statement; type
+
+.. productionlist:: python-grammar
+   type_stmt: 'type' `identifier` [`type_params`] "=" `expression`
+
+The :keyword:`!type` statement declares a type alias, which is an instance
+of :class:`typing.TypeAliasType`.
+
+For example, the following statement creates a type alias::
+
+   type Point = tuple[float, float]
+
+This code is roughly equivalent to::
+
+   annotation-def VALUE_OF_Point():
+       return tuple[float, float]
+   Point = typing.TypeAliasType("Point", VALUE_OF_Point())
+
+``annotation-def`` indicates an :ref:`annotation scope <annotation-scopes>`, which behaves
+mostly like a function, but with several small differences.
+
+The value of the
+type alias is evaluated in the annotation scope. It is not evaluated when the
+type alias is created, but only when the value is accessed through the type alias's
+:attr:`!__value__` attribute (see :ref:`lazy-evaluation`).
+This allows the type alias to refer to names that are not yet defined.
+
+Type aliases may be made generic by adding a :ref:`type parameter list <type-params>`
+after the name. See :ref:`generic-type-aliases` for more.
+
+:keyword:`!type` is a :ref:`soft keyword <soft-keywords>`.
+
+.. versionadded:: 3.12
+
+.. seealso::
+
+   :pep:`695` - Type Parameter Syntax
+      Introduced the :keyword:`!type` statement and syntax for
+      generic classes and functions.
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index ff86cf7eba4d..3e924a5da6f5 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -74,6 +74,8 @@ New typing features:
 
 * :pep:`688`: Making the buffer protocol accessible in Python
 
+* :ref:`whatsnew312-pep695`
+
 * :ref:`whatsnew312-pep692`
 
 * :pep:`698`: Override Decorator for Static Typing
@@ -272,6 +274,70 @@ See :pep:`692` for more details.
 
 (PEP written by Franek Magiera)
 
+.. _whatsnew312-pep695:
+
+PEP 695: Type Parameter Syntax
+------------------------------
+
+Generic classes and functions under :pep:`484` were declared using a verbose syntax
+that left the scope of type parameters unclear and required explicit declarations of
+variance.
+
+:pep:`695` introduces a new, more compact and explicit way to create
+:ref:`generic classes <generic-classes>` and :ref:`functions <generic-functions>`::
+
+   def max[T](args: Iterable[T]) -> T:
+       ...
+
+   class list[T]:
+       def __getitem__(self, index: int, /) -> T:
+           ...
+
+       def append(self, element: T) -> None:
+           ...
+
+In addition, the PEP introduces a new way to declare :ref:`type aliases <type-aliases>`
+using the :keyword:`type` statement, which creates an instance of
+:class:`~typing.TypeAliasType`::
+
+   type Point = tuple[float, float]
+
+Type aliases can also be :ref:`generic <generic-type-aliases>`::
+
+   type Point[T] = tuple[T, T]
+
+The new syntax allows declaring :class:`~typing.TypeVarTuple`
+and :class:`~typing.ParamSpec` parameters, as well as :class:`~typing.TypeVar`
+parameters with bounds or constraints::
+
+   type IntFunc[**P] = Callable[P, int]  # ParamSpec
+   type LabeledTuple[*Ts] = tuple[str, *Ts]  # TypeVarTuple
+   type HashableSequence[T: Hashable] = Sequence[T]  # TypeVar with bound
+   type IntOrStrSequence[T: (int, str)] = Sequence[T]  # TypeVar with constraints
+
+The value of type aliases and the bound and constraints of type variables
+created through this syntax are evaluated only on demand (see
+:ref:`lazy-evaluation`). This means type aliases are able to refer to other
+types defined later in the file.
+
+Type parameters declared through a type parameter list are visible within the
+scope of the declaration and any nested scopes, but not in the outer scope. For
+example, they can be used in the type annotations for the methods of a generic
+class or in the class body. However, they cannot be used in the module scope after
+the class is defined. See :ref:`type-params` for a detailed description of the
+runtime semantics of type parameters.
+
+In order to support these scoping semantics, a new kind of scope is introduced,
+the :ref:`annotation scope <annotation-scopes>`. Annotation scopes behave for the
+most part like function scopes, but interact differently with enclosing class scopes.
+In Python 3.13, :term:`annotations <annotation>` will also be evaluated in
+annotation scopes.
+
+See :pep:`695` for more details.
+
+(PEP written by Eric Traut. Implementation by Jelle Zijlstra, Eric Traut,
+and others in :gh:`103764`.)
+
 Other Language Changes
 ======================
 
@@ -806,14 +872,19 @@ Optimizations
 CPython bytecode changes
 ========================
 
-* Removed the :opcode:`LOAD_METHOD` instruction. It has been merged into
+* Remove the :opcode:`LOAD_METHOD` instruction. It has been merged into
   :opcode:`LOAD_ATTR`. :opcode:`LOAD_ATTR` will now behave like the old
   :opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set.
   (Contributed by Ken Jin in :gh:`93429`.)
 
-* Removed the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP`
+* Remove the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP`
   instructions. (Contributed by Irit Katriel in :gh:`102859`.)
 
+* Add the :opcode:`LOAD_FROM_DICT_OR_DEREF`, :opcode:`LOAD_FROM_DICT_OR_GLOBALS`,
+  and :opcode:`LOAD_LOCALS` opcodes as part of the implementation of :pep:`695`.
+  Remove the :opcode:`!LOAD_CLASSDEREF` opcode, which can be replaced with
+  :opcode:`LOAD_LOCALS` plus :opcode:`LOAD_FROM_DICT_OR_DEREF`. (Contributed
+  by Jelle Zijlstra in :gh:`103764`.)
 
 Demos and Tools
 ===============
diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c
index 6aa0d8a3bc53..0b7d84c706d9 100644
--- a/Objects/typevarobject.c
+++ b/Objects/typevarobject.c
@@ -443,45 +443,38 @@ static PyMethodDef typevar_methods[] = {
 PyDoc_STRVAR(typevar_doc,
 "Type variable.\n\
 \n\
-Usage::\n\
+The preferred way to construct a type variable is via the dedicated syntax\n\
+for generic functions, classes, and type aliases:\n\
 \n\
-  T = TypeVar('T')  # Can be anything\n\
-  A = TypeVar('A', str, bytes)  # Must be str or bytes\n\
+    class Sequence[T]:  # T is a TypeVar\n\
+        ...\n\
 \n\
-Type variables exist primarily for the benefit of static type\n\
-checkers.  They serve as the parameters for generic types as well\n\
-as for generic function definitions.  See class Generic for more\n\
-information on generic types.  Generic functions work as follows:\n\
-\n\
-  def repeat(x: T, n: int) -> List[T]:\n\
-      '''Return a list containing n references to x.'''\n\
-      return [x]*n\n\
+This syntax can also be used to create bound and constrained type\n\
+variables:\n\
 \n\
-  def longest(x: A, y: A) -> A:\n\
-      '''Return the longest of two strings.'''\n\
-      return x if len(x) >= len(y) else y\n\
+    class StrSequence[S: str]:  # S is a TypeVar bound to str\n\
+        ...\n\
 \n\
-The latter example's signature is essentially the overloading\n\
-of (str, str) -> str and (bytes, bytes) -> bytes.  Also note\n\
-that if the arguments are instances of some subclass of str,\n\
-the return type is still plain str.\n\
+    class StrOrBytesSequence[A: (str, bytes)]:  # A is a TypeVar constrained to str or bytes\n\
+        ...\n\
 \n\
-At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.\n\
+However, if desired, reusable type variables can also be constructed\n\
+manually, like so:\n\
 \n\
-Type variables defined with covariant=True or contravariant=True\n\
-can be used to declare covariant or contravariant generic types.\n\
-See PEP 484 for more details. By default generic types are invariant\n\
-in all type variables.\n\
+   T = TypeVar('T')  # Can be anything\n\
+   S = TypeVar('S', bound=str)  # Can be any subtype of str\n\
+   A = TypeVar('A', str, bytes)  # Must be exactly str or bytes\n\
 \n\
-Type variables can be introspected. e.g.:\n\
-\n\
-  T.__name__ == 'T'\n\
-  T.__constraints__ == ()\n\
-  T.__covariant__ == False\n\
-  T.__contravariant__ = False\n\
-  A.__constraints__ == (str, bytes)\n\
+Type variables exist primarily for the benefit of static type\n\
+checkers.  They serve as the parameters for generic types as well\n\
+as for generic function and type alias definitions.\n\
 \n\
-Note that only type variables defined in global scope can be pickled.\n\
+The variance of type variables is inferred by type checkers when they are created\n\
+through the type parameter syntax and when ``infer_variance=True`` is passed.\n\
+Manually created type variables may be explicitly marked covariant or\n\
+contravariant by passing ``covariant=True`` or ``contravariant=True``.\n\
+By default, manually created type variables are invariant. See PEP 484\n\
+and PEP 695 for more details.\n\
 ");
 
 static PyType_Slot typevar_slots[] = {
@@ -942,7 +935,14 @@ static PyMethodDef paramspec_methods[] = {
 PyDoc_STRVAR(paramspec_doc,
 "Parameter specification variable.\n\
 \n\
-Usage::\n\
+The preferred way to construct a parameter specification is via the dedicated syntax\n\
+for generic functions, classes, and type aliases, where\n\
+the use of '**' creates a parameter specification:\n\
+\n\
+    type IntFunc[**P] = Callable[P, int]\n\
+\n\
+For compatibility with Python 3.11 and earlier, ParamSpec objects\n\
+can also be created as follows:\n\
 \n\
     P = ParamSpec('P')\n\
 \n\
@@ -952,12 +952,9 @@ callable to another callable, a pattern commonly found in higher order\n\
 functions and decorators.  They are only valid when used in ``Concatenate``,\n\
 or as the first argument to ``Callable``, or as parameters for user-defined\n\
 Generics.  See class Generic for more information on generic types.  An\n\
-example for annotating a decorator::\n\
-\n\
-    T = TypeVar('T')\n\
-    P = ParamSpec('P')\n\
+example for annotating a decorator:\n\
 \n\
-    def add_logging(f: Callable[P, T]) -> Callable[P, T]:\n\
+    def add_logging[**P, T](f: Callable[P, T]) -> Callable[P, T]:\n\
         '''A type-safe decorator to add logging to a function.'''\n\
         def inner(*args: P.args, **kwargs: P.kwargs) -> T:\n\
             logging.info(f'{f.__name__} was called')\n\
@@ -969,17 +966,9 @@ example for annotating a decorator::\n\
         '''Add two numbers together.'''\n\
         return x + y\n\
 \n\
-Parameter specification variables defined with covariant=True or\n\
-contravariant=True can be used to declare covariant or contravariant\n\
-generic types.  These keyword arguments are valid, but their actual semantics\n\
-are yet to be decided.  See PEP 612 for details.\n\
-\n\
 Parameter specification variables can be introspected. e.g.:\n\
 \n\
     P.__name__ == 'P'\n\
-    P.__bound__ == None\n\
-    P.__covariant__ == False\n\
-    P.__contravariant__ == False\n\
 \n\
 Note that only parameter specification variables defined in global scope can\n\
 be pickled.\n\
@@ -1175,9 +1164,18 @@ static PyMethodDef typevartuple_methods[] = {
 };
 
 PyDoc_STRVAR(typevartuple_doc,
-"Type variable tuple.\n\
+"Type variable tuple. A specialized form of type variable that enables\n\
+variadic generics.\n\
+\n\
+The preferred way to construct a type variable tuple is via the dedicated syntax\n\
+for generic functions, classes, and type aliases, where a single\n\
+'*' indicates a type variable tuple:\n\
+\n\
+    def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:\n\
+        return (*tup[1:], tup[0])\n\
 \n\
-Usage:\n\
+For compatibility with Python 3.11 and earlier, TypeVarTuple objects\n\
+can also be created as follows:\n\
 \n\
   Ts = TypeVarTuple('Ts')  # Can be given any name\n\
 \n\
@@ -1185,7 +1183,7 @@ Just as a TypeVar (type variable) is a placeholder for a single type,\n\
 a TypeVarTuple is a placeholder for an *arbitrary* number of types. For\n\
 example, if we define a generic class using a TypeVarTuple:\n\
 \n\
-  class C(Generic[*Ts]): ...\n\
+  class C[*Ts]: ...\n\
 \n\
 Then we can parameterize that class with an arbitrary number of type\n\
 arguments:\n\
@@ -1441,6 +1439,23 @@ PyDoc_STRVAR(typealias_doc,
 Type aliases are created through the type statement:\n\
 \n\
   type Alias = int\n\
+\n\
+In this example, Alias and int will be treated equivalently by static\n\
+type checkers.\n\
+\n\
+At runtime, Alias is an instance of TypeAliasType. The __name__ attribute\n\
+holds the name of the type alias. The value of the type\n\
+alias is stored in the __value__ attribute. It is evaluated lazily, so\n\
+the value is computed only if the attribute is accessed.\n\
+\n\
+Type aliases can also be generic:\n\
+\n\
+  type ListOrSet[T] = list[T] | set[T]\n\
+\n\
+In this case, the type parameters of the alias are stored in the\n\
+__type_params__ attribute.\n\
+\n\
+See PEP 695 for more information.\n\
 ");
 
 static PyNumberMethods typealias_as_number = {
@@ -1489,14 +1504,14 @@ PyDoc_STRVAR(generic_doc,
 \n\
 A generic type is typically declared by inheriting from\n\
 this class parameterized with one or more type variables.\n\
-For example, a generic mapping type might be defined as::\n\
+For example, a generic mapping type might be defined as:\n\
 \n\
     class Mapping(Generic[KT, VT]):\n\
         def __getitem__(self, key: KT) -> VT:\n\
             ...\n\
         # Etc.\n\
 \n\
-This class can then be used as follows::\n\
+This class can then be used as follows:\n\
 \n\
     def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:\n\
         try:\n\



More information about the Python-checkins mailing list