How to type check use of a python type as a function?

Viewed 560

I am new to using type checking with python, and want to create a function that takes a single value, and either returns None if that value is None, or returns the result of applying a function to it. It works, but doesn't type check when passing a type as a function, such as int.

1st attempt:

from typing import Callable, Optional, TypeVar, Union

A = TypeVar('A')
B = TypeVar('B')

def maybe(val: A, fn: Callable[[A], B]) -> Optional[B]:
    if val is not None:
        return fn(val)
    return None

myVar: Optional[Union[str, int]] = "42"
ret = maybe(myVar, int)

That appeared to work fine and type check OK when I called maybe(myVar, myFn) and myFn was a normal function or lambda. However, if I try maybe(myVar, int), then mypy complains that int has the wrong type:

test.py:12: error: Argument 2 to "maybe" has incompatible type "Type[int]"; expected "Callable[[Union[str, int, None]], int]"

Although the documentation describes int() as a built-in function, it actually has the type <class 'type'>, so the error kind of makes sense. However, although the error is raised about the second argument (int), mypy only complains when the first argument is Optional. If myVar is a plain str or int, it type checks OK.

2nd attempt

I tried changing the function signature to accept types as well as callables:

from typing import Callable, Optional, Type, TypeVar, Union

A = TypeVar('A')
B = TypeVar('B')

def maybe(val: A, fn: Union[Callable[[A], B], Type[B]]) -> Optional[B]:
    if val is not None:
        return fn(val)
    return None

myVar: Optional[Union[str, int]] = "42"
ret = maybe(myVar, int)

That fixed the errors when calling maybe(). Instead, at the line return fn(val), mypy gives an error I don't understand:

test.py:8: error: Too many arguments for "object"

How can I fix this error? Is there a more pythonic way of doing what I want?

Versions

Python 3.7.7
mypy 0.770
0 Answers
Related