Skip to content

Spurious error with partial and ParamSpec #15215

Closed
@sterliakov

Description

@sterliakov

Bug Report

When type checking the code below mypy produces a bunch of weird errors, most interesting one being

Argument 1 to "partial" has incompatible type "Callable[[Callable[_P, _T], NamedArg(str, 'name')], Task[_P, _T]]"; expected "Callable[..., Task[_P, _T]]"  [arg-type]

I'm not sure, but isn't Callable[[Something], Task[_P, _T]] always compatible with Callable[..., Task[_P, _T]]?

To Reproduce

You can also see this in playground.

from __future__ import annotations

from dataclasses import dataclass
from functools import partial
from typing import Any, TypeVar, ParamSpec, Generic, Protocol, Callable


_T = TypeVar("_T")
_P = ParamSpec("_P")


@dataclass(kw_only=True, repr=True)
class Task(Generic[_P, _T]):
    name: str
    function: Callable[_P, _T]


class _TaskRegistrator(Protocol):
    def __call__(self, func: Callable[_P, _T], /, *, name: str = ...) -> Task[_P, _T]:
        ...


class TaskQueue:
    def _register_task(self, func: Callable[_P, _T], *, name: str) -> Task[_P, _T]:
        # Actual implementation omitted
        return Task(name=name, function=func)

    def task(self, name: str) -> _TaskRegistrator:
        return partial(self._register_task, name=name)  # E: see below (L29)
        
    def task2(self, name: str) -> Callable[[Callable[_P, _T]], Task[_P, _T]]:
        return partial(self._register_task, name=name)  # E: see below (L32)


# And now we see that the problem is even unrelated to `self`:

def _register_task(func: Callable[_P, _T], *, name: str) -> Task[_P, _T]:
    # Actual implementation omitted
    return Task(name=name, function=func)

def task(name: str) -> _TaskRegistrator:
    return partial(_register_task, name=name)  # E: see below (L42)

Expected Behavior

From my understanding, no mypy errors should be pointed out. pyright agrees with me (just to test on another typechecker) - all green - but I'm not ready to switch from convenient mypy to pyright (I personally dislike that project for being non-python in python ecosystem, plus my current project relies heavily on Django plugin).

Actual Behavior

Several errors, all located on lines with partial(...) calls.

main.py:29: error: Argument 1 to "partial" has incompatible type "Callable[[Callable[_P, _T], NamedArg(str, 'name')], Task[_P, _T]]"; expected "Callable[..., Task[_P, _T]]"  [arg-type]
main.py:32: error: Incompatible return value type (got "partial[Task[_P, _T]]", expected "Callable[[Callable[_P, _T]], Task[_P, _T]]")  [return-value]
main.py:32: note: "partial[Task[_P, _T]].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Task[_P, _T]]"
main.py:32: error: Argument 1 to "partial" has incompatible type "Callable[[Callable[_P, _T], NamedArg(str, 'name')], Task[_P, _T]]"; expected "Callable[..., Task[_P, _T]]"  [arg-type]
main.py:42: error: Argument 1 to "partial" has incompatible type "Callable[[Callable[_P, _T], NamedArg(str, 'name')], Task[_P, _T]]"; expected "Callable[..., Task[_P, _T]]"  [arg-type]
Found 4 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.2.0, then master and all versions since 0.990.
  • Mypy command-line flags: none (with and without --strict result is the same)
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.10

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