Skip to content

1.7.0: Overloaded function with decorator using ParamSpec does not type check #16474

Closed as not planned
@thomascellerier

Description

@thomascellerier

Bug Report

Code with overloads with a decorator using a ParamSpec was typechecking fine with mypy 1.6.1 but not with mypy 1.7.0.

To Reproduce

See the following reproducer loosely based on an API client library where I hit this problem.
The library allows optionally parsing the response body into a model class

test_overload.py

import typing

P = typing.ParamSpec("P")
R = typing.TypeVar("R")


def handle_exception(
    func: typing.Callable[P, typing.Awaitable[R]]
) -> typing.Callable[P, typing.Awaitable[R]]:
    return func


class Response:
    pass


class ResponseModel:
    pass


ResponseT = typing.TypeVar("ResponseT", bound=ResponseModel)


@typing.overload
async def request(response_model: type[ResponseT]) -> ResponseT:
    ...

@typing.overload
async def request(response_model: None = ...) -> Response:
    ...


@handle_exception
async def request(
    response_model: type[ResponseT] | None = None,
) -> ResponseT | Response:
    raise NotImplementedError()

It can be reproduced without async too.

test_overload_sync.py

import typing

P = typing.ParamSpec("P")
R = typing.TypeVar("R")


def handle_exception(
    func: typing.Callable[P, R]
) -> typing.Callable[P, R]:
    return func


class Response:
    pass


class ResponseModel:
    pass


ResponseT = typing.TypeVar("ResponseT", bound=ResponseModel)


@typing.overload
def request(response_model: type[ResponseT]) -> ResponseT:
    ...

@typing.overload
def request(response_model: None = ...) -> Response:
    ...


@handle_exception
def request(
    response_model: type[ResponseT] | None = None,
) -> ResponseT | Response:
    raise NotImplementedError()

Expected Behavior

No mypy errors.

This works with 1.6.1:

$ mypy --version
mypy 1.6.1 (compiled: yes)
$ mypy test_overload.py 
Success: no issues found in 1 source file

Actual Behavior

It fails to type check:

$ mypy test_overload.py 
test_overload.py:33: error: Argument 1 to "handle_exception" has incompatible type "Callable[[type[ResponseT] | None], Coroutine[Any, Any, ResponseT | Response]]"; expected "Callable[[VarArg(Never), KwArg(Never)], Awaitable[Never]]"  [arg-type]
test_overload.py:33: error: Overloaded function implementation does not accept all possible arguments of signature 1  [misc]
test_overload.py:33: error: Overloaded function implementation cannot produce return type of signature 1  [misc]
test_overload.py:33: error: Overloaded function implementation does not accept all possible arguments of signature 2  [misc]
test_overload.py:33: error: Overloaded function implementation cannot produce return type of signature 2  [misc]
Found 5 errors in 1 file (checked 1 source file)

Sync version:

$ python3 -m mypy test_overload_sync.py 
test_overload.py:33: error: Argument 1 to "handle_exception" has incompatible type "Callable[[type[ResponseT] | None], ResponseT | Response]"; expected "Callable[[VarArg(Never), KwArg(Never)], Never]"  [arg-type]
test_overload.py:33: error: Overloaded function implementation does not accept all possible arguments of signature 1  [misc]
test_overload.py:33: error: Overloaded function implementation does not accept all possible arguments of signature 2  [misc]

Your Environment

  • Mypy version used: 1.7.0 (compiled)
  • Mypy command-line flags: mypy test_overload.py, tried with and without a clean cache
  • Python version: 3.10.2, 3.11.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-paramspecPEP 612, ParamSpec, Concatenate

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions