What is the correct type-hinting for signature of func with variable amount of dynamically added params

Hello everyone!

Using VSCode:
Python 3.12.7
Pylance v2024.11.1
pyright v1.1.386 (from how I understand it Pylance CHANGELOG)

**EDIT: simplified question and example code

Trying to get correct type-hinting results for prepending a function signature with new parameters.

Example setup (simplified as much as I could):

from typing import cast, Callable

class MyBase: ...

class MyClass_1(MyBase): ...

class MyClass_2(MyBase): ...

def my_func(p: int) -> str: ...

The idea is to be able to call my_func with some predefined count and type of instances of subclasses of MyBase.

What I’m trying is to define a “decorator” that accepts at least one and var-count more types:

def my_wrapper[**P, R, B](
    a: Callable[P, R],
    b: type[B],
    *args: type[B],
):
    def wrapper(a: Callable[P, R], b: type[B]):
        def wrapped(b: B | None = None, /, *args: P.args, **kwargs: P.kwargs) -> R:
            # do something ...
            return cast(R, "some result")
        return wrapped

    all_args = [b, *args]
    all_args.reverse()

    res = wrapper(a, all_args[0])
    for arg in all_args[1:]:
        res = wrapper(res, arg)  # type: ignore
    return res

Expectation is having to call the “decorator” once with as many types for additional params as needed:

wrapped = my_wrapper(my_func, MyClass_1, MyClass_2)
wrapped(MyClass_1(), MyClass_2(), ... other args for my_func)

Works as expected, but type-hinting gets totally broken.

Is there any way of doing that, while preserving the correct and useful type-hinting?

I guess the issue is that every time the decorator is called, it appends a new param with a duplicate name of an existing param? Tried figuring out how to do that insane thing as well (dynamic name of a function parameter) but couldn’t figure out how to make it work, not to mention type it…

Any ideas, comments, suggestions how this or something similar can be achieved?
I don’t have issues with using any hacks inside the my_wrapper function, or # type: ignores etc.

I moved this to Python Help

Only thing I can think of that may help you is a TypeVarTuple, since that is what you generally should use for a variable number of positional only arguments, although I don’t think you can prepend a ParamSpec with a TypeVarTuple, because whether or not that is well-defined depends on the context.

What you want to do might not be possible currently, without making some sacrifices in either type safety or user ergonomics.