mypy icon indicating copy to clipboard operation
mypy copied to clipboard

`functools.partial` plugin is only triggered on calls

Open nerodono opened this issue 1 year ago • 1 comments

Bug Report

partial support is too fragile and easy to fool.

To Reproduce

import typing as t
from functools import partial

# 1.
# uncomment this to get type error
# partial(lambda x, y: x + y, 10)()

# 2.
# But this silly little thing passes type-check
silly: t.Callable[[], int] = partial(lambda x, y: x + y, 10)
silly()

Expected Behavior

Fail to coerce partial into incompatible callable type.

Actual Behavior

Success: no issues found in 1 source file

Your Environment

  • Mypy version used: master
  • Mypy command-line flags: no
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.12

Looks like we carry information about needed arguments somewhere anyways, since 1 fails type check due to not enough arguments, so why we allow to coerce that partial into incompatible callable? Perhaps this is due to this definition of partial:

    def __call__(self, /, *args: Any, **kwargs: Any) -> _T: ...

But, for example, pyright takes that into account and fails as expected:

[nix-shell:/tmp]$ pyright issue.py
/tmp/issue.py
  /tmp/issue.py:10:30 - error: Expression of type "partial[Unknown]" is incompatible with declared type "() -> int"
    Type "partial[Unknown]" is incompatible with type "() -> int"
      Extra parameter "y" (reportAssignmentType)
1 error, 0 warnings, 0 informations

I think it worth fixing, since we support partial and it is useful to rely on that support. It can be fixed, for example, as viewing partial result internally not as partial[int] type, but as something like

class FakePartial(Protocol[P, Ret]):
    @property
    def args(self) -> tuple[Any, ...]: ...
    
    @property
    def keywords(self) -> dict[any, ...]: ...

    @property
    def func(self) -> t.Callable[..., Ret]: ...

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Ret: ...

nerodono avatar Aug 01 '24 21:08 nerodono

Thanks, I think this comment from Ivan would be a way to address this: https://github.com/python/mypy/pull/17425#discussion_r1650028681

hauntsaninja avatar Oct 08 '24 18:10 hauntsaninja