Thursday, 3 January 2019

python 3 typing varadic "apply" style definitions

I've been struggling to write "varadic" argument lists type definitions.

for example, giving types to:

def foo(fn, *args):
    return fn(*args)

the best I've been able to do is using a suggestion from here:

from typing import overload, Callable, TypeVar

A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')
R = TypeVar('R')

@overload
def foo(fn: Callable[[A], R], a: A) -> R: ...
@overload
def foo(fn: Callable[[A, B], R], a: A, b: B) -> R: ...
@overload
def foo(fn: Callable[[A, B, C], R], a: A, b: B, c: C) -> R: ...

def foo(fn, *args):
    return fn(*args)

which mostly does the right thing… for example, given:

def bar(i: int, j: int) -> None:
    print(i)

the following succeeds:

foo(bar, 10, 12)

while these fail:

foo(bar, 10)
foo(bar, 10, 'a')
foo(bar, 10, 12) + 1

but if I check with mypy --strict I get:

test.py:15: error: Function is missing a type annotation

(which is saying that the final foo definition doesn't have any types itself)

I can redefine foo to be:

def foo(fn: Callable[..., R], *args: Any) -> R:
    return fn(*args)

but then when I run mypy --strict I get:

test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 1
test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 2
test.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 3

which I don't really understand.

if anyone can suggest a better way of giving types to this sort of function it would be greatly appreciated! if I could also do this without listing lots of overloads that would be nice, the real definitions also have a few "keyword only" arguments that would be nice not to have to repeat each time



from python 3 typing varadic "apply" style definitions

No comments:

Post a Comment