Add the ability for functions to inherit another functions' parameters.
Review Request #14507 — Created July 14, 2025 and updated
One of the big missing gaps in the world of Python's type annotation
support is a way for a function to reference another function's
arguments in a signature. It's common for a function to take*args
and/or**kwargsand pass them somewhere else, but doing this loses all
typing.The "official" way to deal with this is to effectively make all
arguments keyword-only and to define them in a function and in a
TypedDict, and use that for all wrapping functions. This is good in
many cases, but is not always a suitable approach.Instead, this change introduces some new utilities for typing the
*argsand**kwargsbased on another function. The way it works is
through a combination of a decorator and a "ParamsFrom" class.The "ParamsFrom" classes take in a callable, reference its
ParamSpec,
and mixes it into a signature in that class's__call__. This gives us
a newParamSpecthat we can then reference in the decorator.The decorator takes in the "ParamsFrom" class as a
Callable[TParams, Any]. That gives the decorator access to the
modified signature. It can then return a variation on the decorated
function that uses that signature.We complete that by fixing up the annotations and signature to use the
right typing for the*argsand**kwargs, allowing those to pass
through.This has some initial startup cost (though minimal) and no runtime cost
when calling functions.The end result is that any calls to the function will properly type the
parameters going in, based on that signature.In practice, there are a few constraints to be aware of:
ParamSpecdoesn't do well with positional-only parameters. A
function that has aselfand then references positional-only
parameters seems to reliably break when trying to match the type for
the decorated function. This is solved with a# type: ignore, and
does not appear to affect calls to the function.Function signatures need to be compatible between the
decorated/signature and the referenced function. There's no going
from keyword-only to positional-or-keyword, for example.Pylance/Pyright (and Visual Studio Code) have known issues around
choosing the wrong docstrings when decorating a function and
referencing another function as a parameter. This means that if
hovering over a call to a function in any IDE using these as a basis
for a LSP, we're going to get the wrong docstring for the function.
There are many tickets/threads about this, but this is the most
recent with any promising discussion:
https://github.com/microsoft/pylance-release/issues/5840This does nothing useful in PyCharm, which doesn't handle
ParamSpec
fully. This notably is no worse than the behavior functions would
have by default.There's thorough documentation within the codebase docs, but a guide to
using this will be coming separately.This is all very experimental, but types correctly in every major type
checker currently in use except for PyCharm. The hope is that some
wonderful person will design a PEP that makes this entire change
outdated.
Unit tests pass on all supported versions of Python.
Mypy, Pyright, and Pylance report zero typing issues.
| Summary | ID |
|---|---|
| 9bf005a86732050309da70d001b088d1559a2034 |
| Description | From | Last Updated |
|---|---|---|
|
line too long (80 > 79 characters) Column: 80 Error code: E501 |
|
|
|
line too long (80 > 79 characters) Column: 80 Error code: E501 |
|
|
|
line too long (82 > 79 characters) Column: 80 Error code: E501 |
|
|
|
line too long (81 > 79 characters) Column: 80 Error code: E501 |
|