Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to annotate the type of arguments forwarded to another function?

Let's say we have a trivial function that calls open() but with a fixed argument:

def open_for_writing(*args, **kwargs):
    kwargs['mode'] = 'w'
    return open(*args, **kwargs)

If I now try to call open_for_writing(some_fake_arg = 123), no type checker (e.g. mypy) can tell that this is an incorrect invocation: it's missing the required file argument, and is adding another argument that isn't part of the open signature.

How can I tell the type checker that *args and **kwargs must be a subset of the open parameter spec? I realise Python 3.10 has the new ParamSpec type, but it doesn't seem to apply here because you can't get the ParamSpec of a concrete function like open.

like image 394
Migwell Avatar asked Dec 04 '25 13:12

Migwell


1 Answers

I think out of the box this is not possible. However, you could write a decorator that takes the function that contains the arguments you want to get checked for (open in your case) as an input and returns the decorated function, i.e. open_for_writing in your case. This of course only works with python 3.10 or using typing_extensions as it makes use of ParamSpec

from typing import TypeVar, ParamSpec, Callable, Optional

T = TypeVar('T')
P = ParamSpec('P')


def take_annotation_from(this: Callable[P, Optional[T]]) -> Callable[[Callable], Callable[P, Optional[T]]]:
    def decorator(real_function: Callable) -> Callable[P, Optional[T]]:
        def new_function(*args: P.args, **kwargs: P.kwargs) -> Optional[T]:
            return real_function(*args, **kwargs)

        return new_function
    return decorator

@take_annotation_from(open)
def open_for_writing(*args, **kwargs):
    kwargs['mode'] = 'w'
    return open(*args, **kwargs)


open_for_writing(some_fake_arg=123)
open_for_writing(file='')

As shown here, mypy complains now about getting an unknown argument.

like image 192
Simon Hawe Avatar answered Dec 07 '25 14:12

Simon Hawe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!