Skip to content
40 changes: 40 additions & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ callables, the :data:`Concatenate` operator may be used. They
take the form ``Callable[ParamSpecVariable, ReturnType]`` and
``Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]``
respectively.
To copy the call from one function to another use :func:`copy_func_params`.

.. versionchanged:: 3.10
``Callable`` now supports :class:`ParamSpec` and :data:`Concatenate`.
Expand Down Expand Up @@ -2864,6 +2865,45 @@ Functions and decorators
runtime we intentionally don't check anything (we want this
to be as fast as possible).

.. decorator:: copy_func_params(source_func)

Cast the decorated function's call signature to the *source_func*'s.

Use this decorator enhancing an upstream function while keeping its
call signature.
Returns the original function with the *source_func*'s call signature.

Usage::

from typing import copy_func_params, Any

def upstream_func(a: int, b: float, *, double: bool = False) -> float:
...

@copy_func_params(upstream_func)
def enhanced(
a: int, b: float, *args: Any, double: bool = False, **kwargs: Any
) -> str:
...

.. note::

Include ``*args`` and ``**kwargs`` in the signature of the decorated
function in order to avoid a :py:class:`TypeError` when the call signature of
*source_func* changes.

.. versionadded:: 3.14


.. decorator:: copy_method_params(source_method)

Cast the decorated method's call signature to the source_method's

Same as :py:func:`copy_func_params` but intended to be used with methods.
It keeps the first argument (``self``/``cls``) of the decorated method.

.. versionadded:: 3.14

.. function:: assert_type(val, typ, /)

Ask a static type checker to confirm that *val* has an inferred type of *typ*.
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,10 @@ symtable

(Contributed by Bénédikt Tran in :gh:`120029`.)

typing
------
* Add :func:`~typing.copy_func_params` that copies/applies
the :class:`~typing.ParamSpec` from one function to another.

sys
---
Expand Down
57 changes: 57 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@
'assert_never',
'cast',
'clear_overloads',
'copy_func_params',
'copy_method_params',
'dataclass_transform',
'evaluate_forward_ref',
'final',
Expand Down Expand Up @@ -3752,6 +3754,61 @@ def get_protocol_members(tp: type, /) -> frozenset[str]:
return frozenset(tp.__protocol_attrs__)


def copy_func_params[**Param, RV](
source_func: Callable[Param, Any]
) -> Callable[[Callable[..., RV]], Callable[Param, RV]]:
"""Cast the decorated function's call signature to the source_func's.

Use this decorator enhancing an upstream function while keeping its
call signature.
Returns the original function with the source_func's call signature.

Usage::

from typing import copy_func_params, Any

def upstream_func(a: int, b: float, *, double: bool = False) -> float:
...

@copy_func_params(upstream_func)
def enhanced(
a: int, b: float, *args: Any, double: bool = False, **kwargs: Any
) -> str:
...

.. note::

Include ``*args`` and ``**kwargs`` in the signature of the decorated
function in order to avoid TypeErrors when the call signature of
*source_func* changes.
"""

def return_func(func: Callable[..., RV]) -> Callable[Param, RV]:
return cast(Callable[Param, RV], func)

return return_func


def copy_method_params[**Param, Arg1, RV](
source_method: Callable[Concatenate[Any, Param], Any]
) -> Callable[
[Callable[Concatenate[Arg1, ...], RV]],
Callable[Concatenate[Arg1, Param], RV]
]:
"""Cast the decorated method's call signature to the source_method's.

Same as :func:`copy_func_params` but intended to be used with methods.
It keeps the first argument (``self``/``cls``) of the decorated method.
"""

def return_func(
func: Callable[Concatenate[Arg1, ...], RV]
) -> Callable[Concatenate[Arg1, Param], RV]:
return cast(Callable[Concatenate[Arg1, Param], RV], func)

return return_func


def __getattr__(attr):
"""Improve the import time of the typing module.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Add :func:`~typing.copy_func_params` and :func:`~typing.copy_func_params`
to :mod:`typing` that copies/applies the
:class:`~typing.ParamSpec` from one function/method to another.
Patch by Carli Freudenberg.
Loading