python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.5k stars 2.83k forks source link

a decorator that modifies another decorator gives an incorrect type error #9265

Open glyph opened 4 years ago

glyph commented 4 years ago

Please provide more information to help us understand the issue:

Bug.

from typing import Any, Callable, TypeVar

_FirstDecoratee = TypeVar("_FirstDecoratee", bound=Callable[..., Any])
_SecondDecoratee = TypeVar("_SecondDecoratee", bound=Callable[..., Any])

def basic_decorator(decoratee: _FirstDecoratee) -> _FirstDecoratee:
    "decorate a function"
    return decoratee

def decorator_transformer(
    decorator: Callable[[_SecondDecoratee], _SecondDecoratee]
) -> Callable[[_SecondDecoratee], _SecondDecoratee]:
    "take a decorator and return a version of it with the same signature"
    def modified_decorator(decoratee: _SecondDecoratee) -> _SecondDecoratee:
        return decoratee
    return modified_decorator

@basic_decorator
def unstacked() -> None:
    "no decorator stack: no error"

@decorator_transformer(basic_decorator)
def stacked() -> None:
    "decorator stack: this should be the same, but it's an error"
t.py:26: error: Argument 1 has incompatible type "Callable[[], None]"; expected "_FirstDecoratee"

No error.

mypy 0.782 Python 3.8.5

Yes (0.790+dev.ffd9d1cdff4af3b482d4dd1f871fd1dc5b39eebb)

warn_redundant_casts=True
warn_unused_ignores=True
strict_optional=True
strict_equality=True
no_implicit_optional=True
disallow_untyped_defs=True
disallow_any_generics=True
glyph commented 4 years ago

Is this a duplicate of https://github.com/python/mypy/issues/3924 ? Or the issue Jukka alluded to in https://github.com/python/mypy/issues/8978#issuecomment-643375262, "a known very old bug that generic functions when passed as arguments to another generic functions don't work"?

glyph commented 4 years ago

The workaround seems to be

def decorator_transformer(
    decorator: Callable[[_FirstDecoratee], _FirstDecoratee]
) -> Callable[[_SecondDecoratee], _SecondDecoratee]:

which sacrifices a little bit of strictness but manages to mostly maintain the desired shape