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

Can't type a decorator factory that preserves signature #2657

Closed
JelleZijlstra opened this issue Jan 8, 2017 · 2 comments
Closed

Can't type a decorator factory that preserves signature #2657

JelleZijlstra opened this issue Jan 8, 2017 · 2 comments

Comments

@JelleZijlstra
Copy link
Member

JelleZijlstra commented Jan 8, 2017

There doesn't appear to be a way to type a decorator factory that preserves its arguments (similar to @functools.wraps(). This is related to #1927 and the doc example in http://mypy.readthedocs.io/en/latest/generics.html#declaring-decorators but covers a slightly more complicated case.

Example code:

$ cat decorator_factory.py 
from typing import Any, Awaitable, Callable, cast, Dict, Tuple, TypeVar

FuncType = Callable[..., Any]
F = TypeVar('F', bound=FuncType)


def memoize() -> Callable[[F], F]:
    def decorator(fn: F) -> F:
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            return fn(args, **kwargs)
        return cast(F, wrapper)

    reveal_type(decorator)
    return cast(Callable[[F], F], decorator)

reveal_type(memoize)
reveal_type(memoize())


@memoize()
def f() -> None:
    pass
$ python3 -m mypy decorator_factory.py 
decorator_factory.py: note: In function "memoize":
decorator_factory.py:13: error: Revealed type is 'def (fn: F`-1) -> F`-1'
decorator_factory.py: note: At top level:
decorator_factory.py:16: error: Revealed type is 'def [F <: def (*Any, **Any) -> Any] () -> def (F`-1) -> F`-1'
decorator_factory.py:17: error: Revealed type is 'def (builtins.None) -> builtins.None'
decorator_factory.py:20: error: Argument 1 has incompatible type Callable[[], None]; expected None

I'm guessing mypy doesn't see the F typevars on the outer and inner functions as the same type. Strangely, it looks like the typevar is instantiated as None, even though None is outside its bounds.

I haven't been able to think of a workaround that preserves the signature correctly; any suggestions would be appreciated.

@roganov
Copy link

roganov commented Jan 8, 2017

dupe of #1551

I haven't been able to think of a workaround that preserves the signature correctly; any suggestions would be appreciated.

There is a workaround: #1551 (comment)

@gvanrossum
Copy link
Member

Yeah, this problem is getting annoying (though it is remarkably hard to fix). But we only need one issue to track it.

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

3 participants