I would like to write type hints that allow type checkers to recognize if a wrong argument is passed to a function I call "meta
" in the example code below:
I have got the following functions:
- a function
meta
that takes a function and arguments for that function as its arguments, - an example function
example_function
with which I demonstrate configurations that should produce warnings / errors (PEP484):
from typing import TypeVar, Callable
def example_function(f: str, g: int) -> None:
assert isinstance(f, str)
assert isinstance(g, int)
In = TypeVar('In')
Out = TypeVar('Out')
def meta(func: Callable[In, Out], *args, **kwargs) -> Out:
a = func(*args, **kwargs)
print(f'{func.__name__} executed' )
return a
The following calls should be fine:
# ok
meta(example_function, f='', g=0)
The following calls should produce warnings:
# arg for f already provided
try:
meta(example_function, '', f='', g=0)
except:
...
# too many *args
try:
meta(example_function, '', 0, 1)
except:
...
# too many **kwargs
try:
meta(example_function, f='', g=0, k='diff')
except:
...
# invalid kwarg / arg
try:
meta(example_function, 0, g='')
except:
...
Is what I want to achieve possible with a mixture of arguments and keyword arguments?
If not, is it possible with keyboard arguments / arguments only?
>Solution :
You can use ParamSpec
to match the *args
and **kwargs
from the func
(emphasis mine):
Parameter specification variables exist primarily for the benefit of
static type checkers. They are used to forward the parameter types of
one callable to another callable – a pattern commonly found in higher
order functions and decorators.
In use:
T = TypeVar('T')
P = ParamSpec('P')
def meta(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
a = func(*args, **kwargs)
print(f'{func.__name__} executed' )
return a
This errors on all of the cases you mention:
# OK
meta(example_function, f='', g=0)
# main.py:18: error: "meta" gets multiple values for keyword argument "f" [misc]
meta(example_function, '', f='', g=0)
# main.py:19: error: Too many arguments for "meta" [call-arg]
meta(example_function, '', 0, 1)
# main.py:20: error: Unexpected keyword argument "k" for "meta" [call-arg]
meta(example_function, f='', g=0, k='diff')
# main.py:21: error: Argument 2 to "meta" has incompatible type "int"; expected "str" [arg-type]
# main.py:21: error: Argument "g" to "meta" has incompatible type "str"; expected "int" [arg-type]
meta(example_function, 0, g='')