Skip to content

Running mypy with --disallow-untyped-decorators reports an error for overloaded decorators #4191

@joshstaiger

Description

@joshstaiger

Running mypy --disallow-untyped-decorators with a decorator that defines overloads always seems to report:

Untyped decorator makes function "xxx" untyped

Even though reveal_type(...) calls on the decorated functions seem to indicate type information.

Consider the following code.

Running mypy --disallow-untyped-decorators here yields the following (mypy 0.540):

dec_issue.py:19: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
dec_issue.py:35: error: Untyped decorator makes function "default" untyped
dec_issue.py:41: error: Revealed type is 'def (*Any, **Any) -> builtins.int*'

The first error seems to be covered by other bug reports here (though, for all I know it may be related).

However, the second error seems to be contradicted by the type_reveal, which indicates at least the return type of the decorated "default" function is being picked up.

I may be missing something, but at very least, it seems like a more descriptive error may be warranted?

from typing import *

R = TypeVar("R")


def _make_decorator(arg: str) -> Callable[[Callable[..., R]], Callable[..., R]]:
    def dec_(fn: Callable[..., R]) -> Callable[..., R]:
        def wrapped(*args: Tuple[Any, ...],
                    **kwargs: Dict[str, Any]) -> R:
            print(f"Prefix {arg}!")
            return fn(*args, **kwargs)

        return wrapped

    return dec_


@overload
def dec(x: Callable[..., R]) -> Callable[..., R]: ...


@overload
def dec(x: str) -> Callable[[Callable[..., R]], Callable[..., R]]: ...


def dec(x: Any) -> Any:
    if isinstance(x, str):
        return _make_decorator(x)
    elif callable(x):
        return _make_decorator("default")(x)
    else:
        raise Exception("unhandled")


@dec
def default(name: str) -> int:
    print(f"Hello, {name}!")
    return 0


reveal_type(default)


@dec("arg")
def arg(name: str) -> int:
    print(f"Hello, {name}!")
    return 0


if __name__ == "__main__":
    default("Bob")
    arg("Bob")

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions