From 18d63f2525a27a540ab82114b3a82d481883b9fa Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 30 Dec 2020 01:29:43 +0800 Subject: [PATCH 1/7] docs for pep612 --- Doc/library/stdtypes.rst | 6 ++ Doc/library/typing.rst | 168 ++++++++++++++++++++++++++++++++++++++- Doc/whatsnew/3.10.rst | 22 +++++ Lib/typing.py | 15 ++-- 4 files changed, 203 insertions(+), 8 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 2869378bbdaf0e..7bd3e5a77e1cb3 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4959,6 +4959,12 @@ All parameterized generics implement special read-only attributes. (~T,) + .. note:: + A ``GenericAlias`` object with :class:`typing.ParamSpec` parameters may not + have correct parameters returned by ``__parameters__`` after substitution + because :class:`typing.ParamSpec` is intended primarily for static type + checking. + .. seealso:: * :pep:`585` -- "Type Hinting Generics In Standard Collections" diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index b07bb8943d16fd..7417629f92098b 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -18,7 +18,8 @@ -------------- This module provides runtime support for type hints as specified by -:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, and :pep:`613`. +:pep:`484`, :pep:`526`, :pep:`544`, :pep:`586`, :pep:`589`, :pep:`591`, +:pep:`612` and :pep:`613`. The most fundamental support consists of the types :data:`Any`, :data:`Union`, :data:`Tuple`, :data:`Callable`, :class:`TypeVar`, and :class:`Generic`. For full specification please see :pep:`484`. For @@ -171,6 +172,22 @@ It is possible to declare the return type of a callable without specifying the call signature by substituting a literal ellipsis for the list of arguments in the type hint: ``Callable[..., ReturnType]``. +Callables which take other callables as arguments may indicate that their +parameter types are dependent on each other using :class:`ParamSpec`. +Additionally, if that callable adds or remove arguments from other +callables, the :data:`Concatenate` operator may be appropriate. They +take the form ``Callable[ParamSpecVariable, ReturnType]`` and +``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` +respectively. + +.. versionchanged:: 3.10 + ``Callable`` now supports :class:`ParamSpec` and :data:`Concatenate`. + See :pep:`612` for more information. + +.. seealso:: + The documentation for :class:`ParamSpec` and :class:`Concatenate` provide + examples of usage in ``Callable``. + .. _generics: Generics @@ -316,6 +333,38 @@ User defined generic type aliases are also supported. Examples:: .. versionchanged:: 3.7 :class:`Generic` no longer has a custom metaclass. +User-defined generics for parameter expressions are also supported via parameter +specification variables in the form ``Generic[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 lists of types can be used to substitute a ``ParamSpec``:: + + >>> from typing import Generic, ParamSpec, TypeVar + + >>> T = TypeVar('T') + >>> P = ParamSpec('P') + + >>> class Z(Generic[T, P]): ... + ... + >>> Z[int, [dict, float]] + __main__.Z[int, (, )] + + +Furthermore, ``Z[int, str, bool]`` is internally converted to +``Z[[int, str, bool]]`` and is thus equivalent. This is a conscious design choice to improve the aesthetics of +substitution:: + + assert Z[[int, str, bool]] == Z[int, str, bool] + + +Do note that generics with :class:`ParamSpec` may not have correct parameters +returned by ``__parameters__`` after substitution in some cases because they +are intended primarily for static type checking. + +.. versionchanged:: 3.10 + :class:`Generic` can now be parameterized over parameter expressions. + See :class:`ParamSpec` and :pep:`612` for more details. + A user-defined generic class can have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing generics is cached, and most types in the typing module are hashable and @@ -602,10 +651,70 @@ These can be used as types in annotations using ``[]``, each having a unique syn ``Callable[..., Any]``, and in turn to :class:`collections.abc.Callable`. + Callables which take other callables as arguments may indicate that their + parameter types are dependent on each other using :class:`ParamSpec`. + Additionally, if that callable adds or remove arguments from other + callables, the :data:`Concatenate` operator may be appropriate. They + take the form ``Callable[ParamSpecVariable, ReturnType]`` and + ``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` + respectively. + .. deprecated:: 3.9 :class:`collections.abc.Callable` now supports ``[]``. See :pep:`585` and :ref:`types-genericalias`. + .. versionchanged:: 3.10 + ``Callable`` now supports :class:`ParamSpec` and :data:`Concatenate`. + See :pep:`612` for more information. + + .. seealso:: + The documentation for :class:`ParamSpec` and :class:`Concatenate` provide + examples of usage with ``Callable``. + +.. data:: Concatenate + + Type annotates a higher order callable which adds, removes, or transforms + parameters of another callable. Usage is in the form + ``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate`` + is currently only valid when used in conjunction with :data:`Callable` and + :class:`ParamSpec`. The last parameter to ``Concatenate`` must be a + :class:`ParamSpec`. + + For example, to indicate a decorator ``with_lock`` which provides a + :class:`threading.Lock` to the decorated function:: + + from collections.abc import Callable + from threading import Lock + from typing import Any, Concatenate, ParamSpec + + P = ParamSpec('P') + R = ParamSpec('R') + my_lock = Lock() + + def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]: + '''A type-safe decorator which provides a lock.''' + global my_lock + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + # provide the lock as the first argument + return f(my_lock, *args, **kwargs) + return inner + + @with_lock + def sum_threadsafe(lock: Lock, numbers: list[float]) -> float: + '''Add a list of numbers together, thread-safe version.''' + with lock: + return sum(numbers) + + +.. versionadded:: 3.10 + +.. seealso:: + + * :pep:`612` -- Parameter Specification Variables (the PEP which introduced + ``Concatenate``). + * :class:`ParamSpec` and :class:`Callable`. + + .. class:: Type(Generic[CT_co]) A variable annotated with ``C`` may accept a value of type ``C``. In @@ -876,6 +985,63 @@ These are not used in annotations. They are building blocks for creating generic for the type variable must be a subclass of the boundary type, see :pep:`484`. +.. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) + + Parameter specification variable. A specialized version of type variables. + + Usage:: + + P = ParamSpec('P') + + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable -- a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or as the first argument to ``Callable``, or as parameters for user-defined + Generics. See :class:`Generic` for more information on generic types. + + For example, to add basic logging to a function, one can create a decorator + ``add_logging`` to log function calls. The parameter specification variable + tells the type checker that the ``inner`` function in ``add_logging`` has + arguments which are dependent on the outer function:: + + 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]: + '''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') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + 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 + :class:`TypeVar`. However, although these keyword arguments are valid, + their actual semantics are yet to be decided. + + .. versionadded:: 3.10 + + .. note:: + Only parameter specification variables defined in global scope can + be pickled. + + .. seealso:: + * :pep:`612` -- Parameter Specification Variables (the PEP which introduced + ``Concatenate``). + * :class:`Callable` and :class:`Concatenate`. + .. data:: AnyStr ``AnyStr`` is a type variable defined as diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index b5fb1e9a629c1c..3b13bbace2e9a9 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -144,6 +144,28 @@ See :pep:`604` for more details. (Contributed by Maggie Moss and Philippe Prados in :issue:`41428`.) +PEP 612: Parameter Specification Variables +------------------------------------------ + +Two new options to improve the information provided to static type checkers for +:pep:`484`\ 's ``Callable`` have been added to the :mod:`typing` module. + +The first is the parameter specification variable. They are used to forward the +parameter types of one callable to another callable -- a pattern commonly +found in higher order functions and decorators. Examples of usage can be found +in :class:`typing.ParamSpec`. Previously, there was no easy way to type annotate +dependency of parameter types in such a precise manner. + +The second option is the new ``Concatenate`` operator. It's used in conjunction +with parameter specification variables to type annotate a higher order callable +which adds, or removes parameters of another callable. Examples of usage can +be found in :class:`typing.Concatenate`. + +See :class:`typing.Callable`, :class:`typing.ParamSpec`, +:class:`typing.Concatenate` and :pep:`612` for more details. + +(Contributed by Ken Jin in :issue:`41559`.) + Other Language Changes ====================== diff --git a/Lib/typing.py b/Lib/typing.py index b140b0e6696266..88d0d623a421f2 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -544,8 +544,9 @@ def TypeAlias(self, parameters): @_SpecialForm def Concatenate(self, parameters): - """Used in conjunction with ParamSpec and Callable to represent a higher - order function which adds, removes or transforms parameters of a Callable. + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. For example:: @@ -735,11 +736,11 @@ class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True): Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one - Callable to another Callable, a pattern commonly found in higher order - functions and decorators. They are only valid when used in Concatenate, or - as the first argument to Callable, or as parameters for user-defined Generics. - See class Generic for more information on generic types. An example for - annotating a decorator:: + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``, or as parameters for user-defined + Generics. See class Generic for more information on generic types. An + example for annotating a decorator:: T = TypeVar('T') P = ParamSpec('P') From a2fc0d13b70562e31fc3eab908be9aeb52d175fc Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:16:11 +0800 Subject: [PATCH 2/7] polish up docs --- Doc/library/typing.rst | 65 ++++++++++++++++++++++++++++-------------- Doc/whatsnew/3.10.rst | 2 +- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 7417629f92098b..60d7f1b5cd4b41 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -337,7 +337,7 @@ User-defined generics for parameter expressions are also supported via parameter specification variables in the form ``Generic[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 lists of types can be used to substitute a ``ParamSpec``:: +to this is that a list of types can be used to substitute a :class:`ParamSpec`:: >>> from typing import Generic, ParamSpec, TypeVar @@ -350,12 +350,14 @@ to this is that lists of types can be used to substitute a ``ParamSpec``:: __main__.Z[int, (, )] -Furthermore, ``Z[int, str, bool]`` is internally converted to -``Z[[int, str, bool]]`` and is thus equivalent. This is a conscious design choice to improve the aesthetics of -substitution:: - - assert Z[[int, str, bool]] == Z[int, str, bool] +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 +to the former and are thus equivalent:: + >>> class X(Generic[P]): ... + ... + >>> assert X[[int, str, bool]] == X[int, str, bool] Do note that generics with :class:`ParamSpec` may not have correct parameters returned by ``__parameters__`` after substitution in some cases because they @@ -673,15 +675,20 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. data:: Concatenate - Type annotates a higher order callable which adds, removes, or transforms - parameters of another callable. Usage is in the form + Used with :data:`Callable` and :class:`ParamSpec` to type annotates a higher + order callable which adds, removes, or transforms parameters of another + callable. Usage is in the form ``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate`` - is currently only valid when used in conjunction with :data:`Callable` and - :class:`ParamSpec`. The last parameter to ``Concatenate`` must be a - :class:`ParamSpec`. + is currently only valid when used as the first argument to a :data:`Callable`. + The last parameter to ``Concatenate`` must be a :class:`ParamSpec`. - For example, to indicate a decorator ``with_lock`` which provides a - :class:`threading.Lock` to the decorated function:: + For example, to annotate a decorator ``with_lock`` which provides a + :class:`threading.Lock` to the decorated function, ``Concatenate`` can be + used to indicate that ``with_lock`` expects a callable which takes in a + ``Lock`` as the first argument, and returns a callable with a different type + signature. In this case, the :class:`ParamSpec` indicates that the returned + callable's parameter types are dependent on the parameter types of the + callable being passed in:: from collections.abc import Callable from threading import Lock @@ -689,19 +696,22 @@ These can be used as types in annotations using ``[]``, each having a unique syn P = ParamSpec('P') R = ParamSpec('R') + + # Use this lock to ensure that only one thread is executing a function + # at any time. my_lock = Lock() def with_lock(f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]: '''A type-safe decorator which provides a lock.''' global my_lock def inner(*args: P.args, **kwargs: P.kwargs) -> T: - # provide the lock as the first argument + # Provide the lock as the first argument. return f(my_lock, *args, **kwargs) return inner @with_lock def sum_threadsafe(lock: Lock, numbers: list[float]) -> float: - '''Add a list of numbers together, thread-safe version.''' + '''Add a list of numbers together in a thread-safe manner.''' with lock: return sum(numbers) @@ -987,13 +997,13 @@ These are not used in annotations. They are building blocks for creating generic .. class:: ParamSpec(name, *, bound=None, covariant=False, contravariant=False) - Parameter specification variable. A specialized version of type variables. + Parameter specification variable. A specialized version of + :class:`type variables `. Usage:: P = ParamSpec('P') - Parameter specification variables exist primarily for the benefit of static type checkers. They are used to forward the parameter types of one callable to another callable -- a pattern commonly found in higher order @@ -1003,8 +1013,9 @@ These are not used in annotations. They are building blocks for creating generic For example, to add basic logging to a function, one can create a decorator ``add_logging`` to log function calls. The parameter specification variable - tells the type checker that the ``inner`` function in ``add_logging`` has - arguments which are dependent on the outer function:: + tells the type checker that the parameter types of the callable returned by + the ``add_logging`` function are dependent on the parameter types of the + callable passed in:: from collections.abc import Callable from typing import TypeVar, ParamSpec @@ -1025,11 +1036,23 @@ These are not used in annotations. They are building blocks for creating generic '''Add two numbers together.''' return x + y + + .. attribute:: args + .. attribute:: kwargs + + Since ``ParamSpec`` captures both positional and keyword parameters, + ``P.args`` and ``P.kwargs`` can be used to split a ``ParamSpec`` into its + components. ``P.args`` represents the tuple of positional parameters in a + given call and should only be used to annotate ``*args``. ``P.kwargs`` + represents the mapping of keyword parameters to their values in a given call, + and should be only be used to annotate ``**kwargs`` or ``**kwds``. Both + attributes require the annotated parameter to be in scope. + 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 - :class:`TypeVar`. However, although these keyword arguments are valid, - their actual semantics are yet to be decided. + :class:`TypeVar`. However the actual semantics of these keywords are yet to + be decided. .. versionadded:: 3.10 diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index 3b13bbace2e9a9..690c2919f0b8db 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -158,7 +158,7 @@ dependency of parameter types in such a precise manner. The second option is the new ``Concatenate`` operator. It's used in conjunction with parameter specification variables to type annotate a higher order callable -which adds, or removes parameters of another callable. Examples of usage can +which adds or removes parameters of another callable. Examples of usage can be found in :class:`typing.Concatenate`. See :class:`typing.Callable`, :class:`typing.ParamSpec`, From bebe2503ec9cf02fc1a3abdbce7a96c6d867399b Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:20:35 +0800 Subject: [PATCH 3/7] remove repetitive statements --- Doc/library/stdtypes.rst | 5 ++--- Doc/library/typing.rst | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 7bd3e5a77e1cb3..2331849c02e982 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -4961,9 +4961,8 @@ All parameterized generics implement special read-only attributes. .. note:: A ``GenericAlias`` object with :class:`typing.ParamSpec` parameters may not - have correct parameters returned by ``__parameters__`` after substitution - because :class:`typing.ParamSpec` is intended primarily for static type - checking. + have correct ``__parameters__`` after substitution because + :class:`typing.ParamSpec` is intended primarily for static type checking. .. seealso:: diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 60d7f1b5cd4b41..cff46c3871b3c3 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -174,7 +174,7 @@ for the list of arguments in the type hint: ``Callable[..., ReturnType]``. Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using :class:`ParamSpec`. -Additionally, if that callable adds or remove arguments from other +Additionally, if that callable adds or removes arguments from other callables, the :data:`Concatenate` operator may be appropriate. They take the form ``Callable[ParamSpecVariable, ReturnType]`` and ``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` @@ -359,8 +359,8 @@ to the former and are thus equivalent:: ... >>> assert X[[int, str, bool]] == X[int, str, bool] -Do note that generics with :class:`ParamSpec` may not have correct parameters -returned by ``__parameters__`` after substitution in some cases because they +Do note that generics with :class:`ParamSpec` may not have correct +``__parameters__`` after substitution in some cases because they are intended primarily for static type checking. .. versionchanged:: 3.10 @@ -655,7 +655,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using :class:`ParamSpec`. - Additionally, if that callable adds or remove arguments from other + Additionally, if that callable adds or removes arguments from other callables, the :data:`Concatenate` operator may be appropriate. They take the form ``Callable[ParamSpecVariable, ReturnType]`` and ``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` From 2e13c999c931f612068f424201616e0a821f1854 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 30 Dec 2020 23:01:42 +0800 Subject: [PATCH 4/7] polish flow --- Doc/library/typing.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cff46c3871b3c3..1149f3cc4417c8 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -675,7 +675,7 @@ These can be used as types in annotations using ``[]``, each having a unique syn .. data:: Concatenate - Used with :data:`Callable` and :class:`ParamSpec` to type annotates a higher + Used with :data:`Callable` and :class:`ParamSpec` to type annotate a higher order callable which adds, removes, or transforms parameters of another callable. Usage is in the form ``Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]``. ``Concatenate`` @@ -1013,9 +1013,8 @@ These are not used in annotations. They are building blocks for creating generic For example, to add basic logging to a function, one can create a decorator ``add_logging`` to log function calls. The parameter specification variable - tells the type checker that the parameter types of the callable returned by - the ``add_logging`` function are dependent on the parameter types of the - callable passed in:: + tells the type checker that the callable passed into the decorator and the + new callable returned by it have inter-dependent type parameters:: from collections.abc import Callable from typing import TypeVar, ParamSpec From 21f9958438bb79faa57706b8710d5a511d8ff4de Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 31 Dec 2020 12:57:18 +0800 Subject: [PATCH 5/7] use suggestions Co-Authored-By: Guido van Rossum --- Doc/library/typing.rst | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 1149f3cc4417c8..0a52f3f5e7a343 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -175,9 +175,9 @@ for the list of arguments in the type hint: ``Callable[..., ReturnType]``. Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using :class:`ParamSpec`. Additionally, if that callable adds or removes arguments from other -callables, the :data:`Concatenate` operator may be appropriate. They +callables, the :data:`Concatenate` operator may be used. They take the form ``Callable[ParamSpecVariable, ReturnType]`` and -``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` +``Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]`` respectively. .. versionchanged:: 3.10 @@ -357,7 +357,10 @@ to the former and are thus equivalent:: >>> class X(Generic[P]): ... ... - >>> assert X[[int, str, bool]] == X[int, str, bool] + >>> X[int, str] + __main__.X[(, )] + >>> X[[int, str]] + __main__.X[(, )] Do note that generics with :class:`ParamSpec` may not have correct ``__parameters__`` after substitution in some cases because they @@ -656,9 +659,9 @@ These can be used as types in annotations using ``[]``, each having a unique syn Callables which take other callables as arguments may indicate that their parameter types are dependent on each other using :class:`ParamSpec`. Additionally, if that callable adds or removes arguments from other - callables, the :data:`Concatenate` operator may be appropriate. They + callables, the :data:`Concatenate` operator may be used. They take the form ``Callable[ParamSpecVariable, ReturnType]`` and - ``Callable[Concatenate[Arg1Type, ..., ParamSpecVariable], ReturnType]`` + ``Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]`` respectively. .. deprecated:: 3.9 @@ -715,13 +718,15 @@ These can be used as types in annotations using ``[]``, each having a unique syn with lock: return sum(numbers) + # We don't need to pass in the lock ourselves thanks to the decorator. + sum_threadsafe([1.1, 2.2, 3.3]) .. versionadded:: 3.10 .. seealso:: * :pep:`612` -- Parameter Specification Variables (the PEP which introduced - ``Concatenate``). + ``ParamSpec`` and ``Concatenate``). * :class:`ParamSpec` and :class:`Callable`. @@ -1035,6 +1040,17 @@ These are not used in annotations. They are building blocks for creating generic '''Add two numbers together.''' return x + y + Without ``ParamSpec``, the simplest way to annotate this previously was to + use a ``TypeVar`` with bound ``Callable[..., Any]``. However this causes two + problems: + + 1. The type checker will type check the inner function, but allow + incorrectly typed arguments to be passed into ``add_two``, causing a + ``TypeError`` during runtime because the ``...`` tells the type checker + to not perform validation on argument types. + 2. :func:`~cast` may be required in the body of the ``add_logging`` + decorator when using more complex types, or type checking has to be + disabled for the decorator entirely. .. attribute:: args .. attribute:: kwargs @@ -1061,7 +1077,7 @@ These are not used in annotations. They are building blocks for creating generic .. seealso:: * :pep:`612` -- Parameter Specification Variables (the PEP which introduced - ``Concatenate``). + ``ParamSpec`` and ``Concatenate``). * :class:`Callable` and :class:`Concatenate`. .. data:: AnyStr From 2ea0761c4f1ec8dc386de0693a779f27fc411408 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 31 Dec 2020 13:19:42 +0800 Subject: [PATCH 6/7] fix up problems --- Doc/library/typing.rst | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0a52f3f5e7a343..21e96a562e60ee 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1041,16 +1041,14 @@ These are not used in annotations. They are building blocks for creating generic return x + y Without ``ParamSpec``, the simplest way to annotate this previously was to - use a ``TypeVar`` with bound ``Callable[..., Any]``. However this causes two - problems: + use a :class:`TypeVar` with bound ``Callable[..., Any]``. However this + causes two problems: - 1. The type checker will type check the inner function, but allow - incorrectly typed arguments to be passed into ``add_two``, causing a - ``TypeError`` during runtime because the ``...`` tells the type checker - to not perform validation on argument types. + 1. The type checker can't type check the ``inner`` function because + ``*args`` and ``**kwargs`` have to be :data:`Any`. 2. :func:`~cast` may be required in the body of the ``add_logging`` - decorator when using more complex types, or type checking has to be - disabled for the decorator entirely. + decorator when returning the ``inner`` function, or the static type + checker must be told to ignore the ``return inner``. .. attribute:: args .. attribute:: kwargs From 4eb116bcc901f099f2fa2aab343bcb9a045e7be8 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 31 Dec 2020 13:21:28 +0800 Subject: [PATCH 7/7] small nit --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 21e96a562e60ee..d74f8bcc27a20a 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -1045,7 +1045,7 @@ These are not used in annotations. They are building blocks for creating generic causes two problems: 1. The type checker can't type check the ``inner`` function because - ``*args`` and ``**kwargs`` have to be :data:`Any`. + ``*args`` and ``**kwargs`` have to be typed :data:`Any`. 2. :func:`~cast` may be required in the body of the ``add_logging`` decorator when returning the ``inner`` function, or the static type checker must be told to ignore the ``return inner``.