Copy *args, **kwargs type annotations from object constructor when passing them to the constructor

from typing import *


def my_func(arg1: int, arg2: str, *args, **kwargs) -> MyClass:
    a = MyClass(*args, **kwargs)
    ...  # Some code uses arg1 and arg2
    return a

my_func passes *args and **kwargs to the MyClass constructor. How to copy constructor’s arguments type annotations to the function for type checking (mypy)?

I don’t believe there’s any way to do this automatically. If you want mypy to be able to check the types, you’d need to define them.
mypy is a static tool it cannot and will not inspect your code to figure out where/how your variables may be used within the code at runtime. It only checks whether the types match where they’re defined (or, in simple cases, inferred).

Realistically, it looks like the example you’ve given would be better off just expecting a MyClass instance and the caller to pass one in. Especially because (at least in the example) there’s no preprocessing of the *args or **kwargs before instantiation.

EDIT:
Also, I would avoid using a from <whatever> import * pattern. It makes it unclear which names are in scope. And if you’ve got multiple, it can create unintended name collisions. And it’ll make it harder to read the code. In your example you don’t even seem to be using anything from the typing package. In any case, either import the package and use its members (a little more verbose and not too popular for typing) or import what you need from it explicitly (i.e from typing import Any).

This is simplified case, the real function adds some arguments to the constructor. Also what if MyClass is immutable and the given MyClass instance cannot be modified when constructed without required arguments?

On Stack Overflow: Copy type signature from another function, How to annotate the type of arguments forwarded to another function?, Annotate function that passes parameters to another function.

Also maybe some way to do this using Unpack? But how to deal with arguments which may be both positional and keyword?

  1. Can the third be extended to support any number of extra arguments (without creating special class for 1, 2 or 3, … extra arguments)?
  2. Will it work with keyword arguments, **args and **kwargs of object constructor
  3. Will it work with arguments which may be both positional and keyword?

For the first question, yes (change E to *Es), but could you express the other two in code, please?

I assume my_func is intended for some class you cannot modify? it really sounds like it should be a class method defined by the class, rather than a standalone function. Can you give a more concrete example of what my_func will do with arg1 and arg2 before calling MyClass to create the return value?

Yes, I cannot modify the class, it’s from library and not subclassable.

This works with one argument:

from typing import *


def same_signature_with_one_leading_extra[**P, T, E](
        _origin: Callable[P, T]
) -> Callable[
    [Callable[Concatenate[E, ...], Any]],
    Callable[Concatenate[E, P], T]
]:
    def decorator(target: Callable[Concatenate[E, ...], Any]) -> Callable[Concatenate[E, P], T]:
        return cast(Callable[Concatenate[E, P], T], target)

    return decorator


def inner(a: int, b: int, c: int) -> None:
    pass


@same_signature_with_one_leading_extra(inner)
def wrapper(arg1: list, *args, **kwargs) -> None:
    return inner(*args, **kwargs)


wrapper([], "hello", "world", "foo")

But reports an error after replacing E with *Es.

Also doesn’t work when arg1 is used as keyword:

wrapper("hello", "world", "foo", arg1=[])
#                                ^^^^^^^ Unexpected argument

How exactly to do it for any number of extra arguments? And can keyword argument support be added to this?

Sorry, Concatenate-ing a TypeVarTuple and a ParamSpec is apparently an unspecified behaviour and thus not supported. I guess the only way is to create multiple definitions.