python / python/mypy

`functools.partial` plugin is only triggered on calls

Offen
#17,620 1 Kommentar 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

bug topic-plugins
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

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: ...

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Reproduziere die beiden Beispiele mit Python 3.12 und mypy master und vergleiche anschließend das Verhalten mit der im Issue verlinkten typeshed functools.pyi-Definition. Verfolge die bestehende Unterstützung für functools.partial und deren Argumentinformationen. Die Aufgabe ist erledigt, wenn die inkompatible Zuweisung zu einem Callable ohne Argumente abgelehnt wird, während gültige partial-Aufrufe weiterhin die Typprüfung bestehen.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
python
Bereich
devtools
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
30/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.