Skip to content

mypy failed to return correct overloaded func type when decorate func accepts more than one parameter #18167

Open
@uriyyo

Description

@uriyyo

Bug Report

mypy failed to return the correct overloaded func type when decorate func accepts more than one parameter.

To Reproduce

from functools import wraps
from typing import overload, Any, TypeVar, Callable, Type

from typing_extensions import ParamSpec, reveal_type


@overload
def foo(a: str, /) -> str:
    pass


@overload
def foo(a: int, /) -> int:
    pass


def foo(a: Any) -> Any:
    return a


P = ParamSpec("P")
T = TypeVar("T")
R = TypeVar("R")


def forbid_return_type(
    func: Callable[P, T],
    tp: Type[Any],
    /,
) -> Callable[P, T]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        result = func(*args, **kwargs)

        if isinstance(result, tp):
            raise TypeError(f"Return type {tp} is forbidden")

        return result

    return wrapper


foo_as_int = forbid_return_type(foo, str)

reveal_type(foo_as_int(1))
reveal_type(foo_as_int("1"))

Expected Behavior

note: Revealed type is "builtins.int"
note: Revealed type is "builtins.str"

Actual Behavior

note: Revealed type is "builtins.str"
error: Argument 1 has incompatible type "int"; expected "str"  [arg-type]
note: Revealed type is "builtins.str"

Your Environment

  • Mypy version used: mypy 1.14.0+dev
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: 3.8.19

Both codes bellow work as expected:

from functools import wraps
from typing import overload, Any, TypeVar, Callable

from typing_extensions import ParamSpec, reveal_type


@overload
def foo(a: str, /) -> str:
    pass


@overload
def foo(a: int, /) -> int:
    pass


def foo(a: Any) -> Any:
    return a


P = ParamSpec("P")
T = TypeVar("T")
R = TypeVar("R")


def forbid_return_type(
    func: Callable[P, T],
) -> Callable[P, T]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        result = func(*args, **kwargs)
        return result

    return wrapper


foo_as_int = forbid_return_type(foo)

reveal_type(foo_as_int(1))
reveal_type(foo_as_int("1"))
from functools import wraps
from typing import Any, TypeVar, Callable, Type, Union

from typing_extensions import ParamSpec, reveal_type


def foo(a: Union[int, str]) -> Union[int, str]:
    return a


P = ParamSpec("P")
T = TypeVar("T")
R = TypeVar("R")


def forbid_return_type(
    func: Callable[P, T],
    tp: Type[Any],
    /,
) -> Callable[P, T]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        result = func(*args, **kwargs)

        if isinstance(result, tp):
            raise TypeError(f"Return type {tp} is forbidden")

        return result

    return wrapper


foo_as_int = forbid_return_type(foo, str)

reveal_type(foo_as_int(1))
reveal_type(foo_as_int("1"))

So it's not working only when the function accepts an overloaded function and another argument.

I checked with pyright and it correctly handles code.

  /Users/yuriikarabas/projects/mypy/temp.py:45:13 - information: Type of "foo_as_int(1)" is "int"
  /Users/yuriikarabas/projects/mypy/temp.py:46:13 - information: Type of "foo_as_int("1")" is "str"
0 errors, 0 warnings, 2 informations 

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions