Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Type hint generic function with arguments of passed function

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

# 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='')

MyPy playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading