• 
      

    Add the ability for functions to inherit another functions' parameters.

    Review Request #14507 — Created July 14, 2025 and submitted — Latest diff uploaded

    Information

    typelets
    main

    Reviewers

    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 **kwargs and 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
    *args and **kwargs based 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 new ParamSpec that 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 *args and **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:

    1. ParamSpec doesn't do well with positional-only parameters. A
      function that has a self and 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.

    2. 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.

    3. 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/5840

    4. This 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.

    Commits

    Files