Skip to content

Using ParamSpec in decorator with new type inference results in Never populated as type arguments #16512

Closed
@flaeppe

Description

@flaeppe

Bug Report

Using a decorator including a ParamSpec variable on a function declaring multiple parameters from a generic protocol swaps any protocol argument to Never (pheuh, having a bit of trouble phrasing this decently..)

To Reproduce

I drilled down an issue in our code base, when running with 1.7.0, to the following script. This passes fine with 1.6.1. It might probably be reduced further.

While I was at it I ran git bisect with the script below and ended up at 93d4cb0 (#16345) (CC @ilevkivskyi).

I see that there's a flag to run with the old type inference to resolve things for now.

Playground link

from collections.abc import Callable
from typing import Concatenate, ParamSpec, Protocol, TypeVar

T = TypeVar("T")
P = ParamSpec("P")
V_co = TypeVar("V_co", covariant=True)


class Metric(Protocol[V_co]):
    def __call__(self) -> V_co:
        ...


def simple_metric(func: Callable[Concatenate[int, P], T]) -> Callable[P, T]:
    def inner(*args: P.args, **kwargs: P.kwargs) -> T:
        return func(*args, **kwargs)

    return inner


@simple_metric
def Negate(count: int, /, metric: Metric[float]) -> float:
    return metric() * -1


@simple_metric
def Combine(count: int, m1: Metric[T], m2: Metric[T], /, *more: Metric[T]) -> T:
    return m1()


reveal_type(simple_metric)
reveal_type(Negate)
reveal_type(Combine)

def m1() -> float:
    return 0.0
    
def m2() -> float:
    return 1.0

reveal_type(Combine(m1, m2))

Expected Behavior

There should be no Never populated as generic argument and script should pass.

Actual Behavior

Some strategic reveals in the script but the 2 errors it spits back are:

main.py:41: error: Argument 1 to "Combine" has incompatible type "Callable[[], float]"; expected "Metric[Never]"  [arg-type]
main.py:41: error: Argument 2 to "Combine" has incompatible type "Callable[[], float]"; expected "Metric[Never]"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

What's interesting is that if I only keep 1 Metric[T] argument e.g.

@simple_metric
def Combine(count: int, m1: Metric[T], /) -> T:
    return m1()

It seems to get things right.

Your Environment

  • Mypy version used: 1.7.0 (also master)
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: Python3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions