]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-103921: Improve typing documentation (GH-104642) (#105007)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Sat, 27 May 2023 23:30:41 +0000 (16:30 -0700)
committerGitHub <noreply@github.com>
Sat, 27 May 2023 23:30:41 +0000 (16:30 -0700)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Doc/library/typing.rst
Lib/typing.py

index bcf30c50d50bec62d27e5c767a8d7cacc59b22fa..d2acac87b880ce85c3eb1cf9badf262cd3b42bce 100644 (file)
@@ -136,6 +136,13 @@ 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)``.
 
+Type aliases may be marked with :data:`TypeAlias` to make it explicit that
+the statement is a type alias declaration, not a normal variable assignment::
+
+   from typing import TypeAlias
+
+   Vector: TypeAlias = list[float]
+
 .. _distinct:
 
 NewType
@@ -367,15 +374,15 @@ You can use multiple inheritance with :class:`Generic`::
    class LinkedList(Sized, Generic[T]):
        ...
 
-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
+   from collections.abc import Mapping
+   from typing import TypeVar
 
-    T = TypeVar('T')
+   T = TypeVar('T')
 
-    class MyDict(Mapping[str, T]):
-        ...
+   class MyDict(Mapping[str, T]):
+       ...
 
 In this case ``MyDict`` has a single parameter, ``T``.
 
@@ -387,7 +394,7 @@ 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
@@ -423,7 +430,6 @@ to this is that a list of types can be used to substitute a :class:`ParamSpec`::
    >>> Z[int, [dict, float]]
    __main__.Z[int, (<class 'dict'>, <class 'float'>)]
 
-
 Furthermore, 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
@@ -665,20 +671,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
 
@@ -712,9 +718,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,
@@ -725,16 +731,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``.
@@ -1249,7 +1255,8 @@ These can be used as types in annotations using ``[]``, each having a unique syn
 Building generic types
 """"""""""""""""""""""
 
-These are not used in annotations. They are building blocks for creating generic types.
+The following objects are not used directly in annotations. Instead, they are building blocks
+for creating generic types.
 
 .. class:: Generic
 
@@ -1275,177 +1282,204 @@ These are not used in annotations. They are building blocks for creating generic
           except KeyError:
               return default
 
-.. class:: TypeVar
+.. class:: TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False)
 
-    Type variable.
+   Type variable.
 
-    Usage::
+   Usage::
 
       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(x: T, n: int) -> Sequence[T]:
+          """Return a list containing n references to x."""
+          return [x]*n
+
+
+      def print_capitalized(x: S) -> S:
+          """Print x capitalized, and return x."""
+          print(x.capitalize())
+          return x
+
+
+      def concatenate(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.
 
-       def repeat(x: T, n: int) -> Sequence[T]:
-           """Return a list containing n references to x."""
-           return [x]*n
+   Created type variables may be explicitly marked covariant or contravariant by passing
+   ``covariant=True`` or ``contravariant=True``.
+   By default, 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::
 
-       def print_capitalized(x: S) -> S:
-           """Print x capitalized, and return x."""
-           print(x.capitalize())
-           return x
+      x = print_capitalized('a string')
+      reveal_type(x)  # revealed type is str
 
+      class StringSubclass(str):
+          pass
 
-       def concatenate(x: A, y: A) -> A:
-           """Add two strings or bytes objects together."""
-           return x + y
+      y = print_capitalized(StringSubclass('another string'))
+      reveal_type(y)  # revealed type is StringSubclass
 
-    Note that type variables can be *bound*, *constrained*, or neither, but
-    cannot be both bound *and* constrained.
+      z = print_capitalized(45)  # error: int is not a subtype of str
 
-    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::
+   Type variables can be bound to concrete types, abstract types (ABCs or
+   protocols), and even unions of types::
 
-       x = print_capitalized('a string')
-       reveal_type(x)  # revealed type is str
+      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
 
-       class StringSubclass(str):
-           pass
+   .. _typing-constrained-typevar:
 
-       y = print_capitalized(StringSubclass('another string'))
-       reveal_type(y)  # revealed type is StringSubclass
+   Using a *constrained* type variable, however, means that the ``TypeVar``
+   can only ever be solved as being exactly one of the constraints given::
 
-       z = print_capitalized(45)  # error: int is not a subtype of str
+      a = concatenate('one', 'two')
+      reveal_type(a)  # revealed type is str
 
-    Type variables can be bound to concrete types, abstract types (ABCs or
-    protocols), and even unions of types::
+      b = concatenate(StringSubclass('one'), StringSubclass('two'))
+      reveal_type(b)  # revealed type is str, despite StringSubclass being passed in
 
-       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
+      c = concatenate('one', b'two')  # error: type variable 'A' can be either str or bytes in a function call, but not both
 
-.. _typing-constrained-typevar:
+   At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.
 
-    Using a *constrained* type variable, however, means that the ``TypeVar``
-    can only ever be solved as being exactly one of the constraints given::
+   .. attribute:: __name__
+
+      The name of the type variable.
+
+   .. attribute:: __covariant__
+
+      Whether the type var has been marked as covariant.
+
+   .. attribute:: __contravariant__
 
-       a = concatenate('one', 'two')
-       reveal_type(a)  # revealed type is str
+      Whether the type var has been marked as contravariant.
 
-       b = concatenate(StringSubclass('one'), StringSubclass('two'))
-       reveal_type(b)  # revealed type is str, despite StringSubclass being passed in
+   .. attribute:: __bound__
 
-       c = concatenate('one', b'two')  # error: type variable 'A' can be either str or bytes in a function call, but not both
+      The bound of the type variable, if any.
 
-    At runtime, ``isinstance(x, T)`` will raise :exc:`TypeError`.  In general,
-    :func:`isinstance` and :func:`issubclass` should not be used with types.
+   .. attribute:: __constraints__
 
-    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.
+      A tuple containing the constraints of the type variable, if any.
+
+.. class:: TypeVarTuple(name)
+
+   Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
+   that enables *variadic* generics.
+
+   Usage::
 
-.. class:: TypeVarTuple
+      T = TypeVar("T")
+      Ts = TypeVarTuple("Ts")
 
-    Type variable tuple. A specialized form of :class:`type variable <TypeVar>`
-    that enables *variadic* generics.
+      def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]:
+          return (*tup[1:], tup[0])
 
-    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 = TypeVar('T')
-        Ts = TypeVarTuple('Ts')
+      # 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,))
 
-        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,)
+      # Return value is ('spam', 1), which has type tuple[str, int]
+      move_first_element_to_last(tup=(1, 'spam'))
 
-        # 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 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 (str,)
-        # Return value is ('spam', 1), which has type tuple[str, int]
-        move_first_element_to_last(tup=(1, 'spam'))
+      # 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, 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))
+   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]``.)
 
-        # 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=())
+   Type variable tuples must *always* be unpacked. This helps distinguish type
+   variable tuples from normal type variables::
 
-    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]``.)
+      x: Ts          # Not valid
+      x: tuple[Ts]   # Not valid
+      x: tuple[*Ts]  # The correct way to do it
 
-    Type variable tuples must *always* be unpacked. This helps distinguish type
-    variable tuples from 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::
 
-        x: Ts          # Not valid
-        x: tuple[Ts]   # Not valid
-        x: tuple[*Ts]  # The correct way to do it
+      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]: ...
 
-    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 can be happily combined with normal type variables::
 
-        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]: ...
+      DType = TypeVar('DType')
 
-    Type variable tuples can be happily combined with normal type variables::
+      class Array(Generic[DType, *Shape]):  # This is fine
+          pass
 
-        DType = TypeVar('DType')
+      class Array2(Generic[*Shape, DType]):  # This would also be fine
+          pass
 
-        class Array(Generic[DType, *Shape]):  # This is fine
-            pass
+      float_array_1d: Array[float, Height] = Array()     # Totally fine
+      int_array_2d: Array[int, Height, Width] = Array()  # Yup, fine too
 
-        class Array2(Generic[*Shape, DType]):  # This would also be fine
-            pass
+   However, note that at most one type variable tuple may appear in a single
+   list of type arguments or type parameters::
 
-        float_array_1d: Array[float, Height] = Array()     # Totally fine
-        int_array_2d: Array[int, Height, Width] = Array()  # Yup, fine too
+      x: tuple[*Ts, *Ts]                     # Not valid
+      class Array(Generic[*Shape, *Shape]):  # Not valid
+          pass
 
-    However, note that at most one type variable tuple may appear in a single
-    list of type arguments or type parameters::
+   Finally, an unpacked type variable tuple can be used as the type annotation
+   of ``*args``::
 
-        x: tuple[*Ts, *Ts]                     # Not valid
-        class Array(Generic[*Shape, *Shape]):  # Not valid
-            pass
+      def call_soon(
+               callback: Callable[[*Ts], None],
+               *args: *Ts
+      ) -> None:
+          ...
+          callback(*args)
 
-    Finally, an unpacked type variable tuple can be used as the type annotation
-    of ``*args``::
+   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``.
 
-        def call_soon(
-                callback: Callable[[*Ts], None],
-                *args: *Ts
-        ) -> None:
-            ...
-            callback(*args)
+   See :pep:`646` for more details on type variable tuples.
 
-    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``.
+   .. attribute:: __name__
 
-    See :pep:`646` for more details on type variable tuples.
+      The name of the type variable tuple.
 
-    .. versionadded:: 3.11
+   .. versionadded:: 3.11
 
 .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False)
 
@@ -1510,6 +1544,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
@@ -1672,6 +1710,8 @@ These are not used in annotations. They are building blocks for declaring types.
 
    Protocol classes can be generic, for example::
 
+      T = TypeVar("T")
+
       class GenProto(Protocol[T]):
           def meth(self) -> T:
               ...
@@ -2152,8 +2192,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 (``[]``).
index 814020b872f585afa27fbc182c8a4cd8562e579a..4695afca741257bfcd7d6a0dc8be8966c3f94913 100644 (file)
@@ -1177,17 +1177,9 @@ class ParamSpec(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin,
            '''Add two numbers together.'''
            return x + y
 
-    Parameter specification variables defined with covariant=True or
-    contravariant=True can be used to declare covariant or contravariant
-    generic types.  These keyword arguments are valid, but their actual semantics
-    are yet to be decided.  See PEP 612 for details.
-
     Parameter specification variables can be introspected. e.g.:
 
        P.__name__ == 'P'
-       P.__bound__ == None
-       P.__covariant__ == False
-       P.__contravariant__ == False
 
     Note that only parameter specification variables defined in global scope can
     be pickled.