Skip to content

Spurious error with partial and ParamSpec #15215

Closed

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

Activity

  1. added a commit that references this issue on Jun 5, 2025
    f902275
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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