Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typing for decorators transforming input arguments to new type #7603

Closed
rggjan opened this issue Oct 2, 2019 · 3 comments
Closed

Typing for decorators transforming input arguments to new type #7603

rggjan opened this issue Oct 2, 2019 · 3 comments

Comments

@rggjan
Copy link

rggjan commented Oct 2, 2019

Please forgive me if this issue is already solved or if it is part of #1317, but I'm not sure this is already covered with the existing issues. I'm trying to find a solution for the following use case:

from collections import Callable
from typing import Any, Tuple


def decorator(func: 'Callable[..., None]') -> 'Callable[..., None]':
    def wrapper(*args: Tuple[Any], **kwargs: Tuple[Any]) -> None:
        new_args = [arg[0] for arg in args]
        new_kwargs = {key: kwargs[key][0] for key in kwargs}
        func(*new_args, **new_kwargs)

    return wrapper


@decorator
def f1(a: str) -> None:
    print('f1')
    print(a)


@decorator
def f2(a: str, b: int) -> None:
    print('f2')
    print(a)
    print(b)


@decorator
def f3(a: str, b: int = 3) -> None:
    print('f3')
    print(a)
    print(b)


# This works as expected
f1(('abc',)) # Good 1
f1(a=('keyword_abc',)) # Good 2
f2(('def',), (3,)) # Good 3
f3(('ghi',)) # Good 4

# I would like these to generate an error
f1('abc') # Bad 1
f1(a='abc') # Bad 2
f2('def', 3) # Bad 3
f3('ghi') # Bad 4

I have a decorator, which changes functions to accept all arguments inside a Tuple. The current solution works and doesn't give any mypy errors, because the type of the decorated functions is

'def (*Any, **Any)'

and basically looses all typing information.

However, after the decorator is applied, the four last lines should give an error, since the decorated function only accepts tuples now.

Is or will there be a way type decorator so that these cases can be solved? I tried different solutions with generics and overloads, but didn't manage to improve upon Callable[..., None].

Or, in other words, I would like to add more typing information to decorator to allow mypy to convert decorated functions from Callable[[T1, T2, T3], None] to Callable[[Tuple[T1], Tuple[T2], Tuple[T3]], None]

@rggjan
Copy link
Author

rggjan commented Oct 2, 2019

For example, one solution approach would be to use:

def decorator(func: 'Callable[[T], None]') -> 'Callable[[Tuple[T]], None]':

which correctly handles Good 1 and Bad 1, but only works for functions with one argument (like f1) and also fails when calling the decorated function with keyword arguments (Good 2)

@msullivan
Copy link
Collaborator

Yeah, this is an unfortunate shortcoming that is covered in #1317 and maybe others (python/typing#193).

We're hoping to improve on this, though the timeframe is somewhat unclear. Currently the best solution is to write a bunch of overloads (for 0-6 arguments, for example), though that won't work for arbitrarily named keyword arguments.

@rggjan
Copy link
Author

rggjan commented Oct 7, 2019

I see, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants