Skip to content

TypeVar to represent a Callable's arguments #3028

Description

@JelleZijlstra

This is an idea for a new type system feature that can help more accurately type some decorators and wrapper functions, like @async() from https://github.com/quora/asynq.

I'd like to see something like this work:

from mypy_extensions import ArgumentsVar
from typing import TypeVar, Awaitable

_ArgsT = ArgumentsVar('_ArgsT')
_RetT = TypeVar('_RetT')

def make_awaitable(fn: Callable[_ArgsT, _RetT]) -> Callable[_ArgsT, Awaitable[_RetT]]:
    async def wrapper(**args: _ArgsT, **kwargs: _ArgsT) -> Awaitable[_RetT]:
        result = fn(*args, **kwargs)
        return result
    return wrapper

@make_awaitable
def f(x: int, y: str) -> int:
    return 3

reveal_type(f(1, 'x'))  # Awaitable[int]
f(1, 1)  # error, second argument must be str
f(1, z=3)  # error, z is not a valid kwarg

Having a function's args and kwargs annotated with an ArgumentsVar would mean that it takes the arguments that the ArgumentsVar resolves to. As an extension, we could also make something like def f(x: int, *args: _ArgsT, **kwargs: _ArgsT) -> _T: ... work to indicate that a function takes an argument called x plus the arguments specified in _ArgsT.

This could also improve some types in the standard library. For example, the annotations for functools.singledispatch currently don't check function arguments. With ArgumentsVar, it could be typed as follows:

_T = TypeVar('_T')
_ArgsT = ArgumentsVar('_ArgsT')

class _SingleDispatchCallable(Generic[_ArgsT, _T]):
    registry = ...  # type: Mapping[Any, Callable[_ArgsT, _T]]
    def dispatch(self, cls: Any) -> Callable[_ArgsT, _T]: ...
    @overload
    def register(self, cls: Any) -> Callable[[Callable[_ArgsT, _T]], Callable[_ArgsT, _T]]: ...
    @overload
    def register(self, cls: Any, func: Callable[_ArgsT, _T]) -> Callable[_ArgsT, _T]: ...
    def _clear_cache(self) -> None: ...
    def __call__(self, *args: _ArgsT, **kwargs: _ArgsT) -> _T: ...

def singledispatch(func: Callable[_ArgsT, _T]) -> _SingleDispatchCallable[_ArgsT, _T]: ...

This kind of thing has come up before, but I'm not sure a concrete solution has been proposed. I'll try to implement this to see how well it works.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions