python / python/mypy

Generic callback protocols break with kwargs

Offen
#7,311 10 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

needs discussion topic-calls topic-protocols
Vorherrschende Sprache
Python
Sterne
20.6k
Forks
3.3k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

I'm getting an unexpected error when using callback protocols with kwargs

Normal callback with no args or kwargs works fine:

from typing import *

T1 = TypeVar('T1')
T2 = TypeVar('T2')

T_contra = TypeVar('T_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)

class Callback(Protocol[T_contra, T_co]):
  def __call__(self, __element: T_contra) -> T_co:
    pass

def applyit(func: Callback[T1, T2], y: T1) -> T2:
    return func(y)

def callback(x: str) -> int:
    pass

applyit(callback, 'foo')  # OK!

callback with args works fine:

from typing import *

T1 = TypeVar('T1')
T2 = TypeVar('T2')

T_contra = TypeVar('T_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)

class CallbackWithArgs(Protocol[T_contra, T_co]):
  def __call__(self, __element: T_contra, *args: Any) -> T_co:
    pass

def applyit_with_args(func: CallbackWithArgs[T1, T2], y: T1) -> T2:
    return func(y)

def callback_with_args(x: str, *args: Any) -> int:
    pass

applyit_with_args(callback_with_args, 'foo')  # OK!

But the same thing with kwargs produces an error:

from typing import *

T1 = TypeVar('T1')
T2 = TypeVar('T2')

T_contra = TypeVar('T_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)

class CallbackWithKwargs(Protocol[T_contra, T_co]):
  def __call__(self, __element: T_contra, **kwargs: Any) -> T_co:
    pass

def applyit_with_kwargs(func: CallbackWithKwargs[T1, T2], y: T1) -> T2:
    return func(y)

def callback_with_kwargs(x: str, **kwargs: Any) -> int:
    pass

# Argument 1 to "applyit_with_kwargs" has incompatible type "Callable[[str, KwArg(Any)], int]"; expected "CallbackWithKwargs[str, <nothing>]"
applyit_with_kwargs(callback_with_kwargs, 'foo')  # Error!

Perhaps this is expected behavior and I'm just overlooking some subtle interplay between the args in the last case, but it seems like a bug.

mypy 0.720

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

Führe den bereitgestellten Reproducer für das Callback-Protokoll mit dem gemeldeten Verhalten von mypy 0.720 aus und vergleiche die Fälle ohne Argumente, mit *args und mit **kwargs. Verfolge die Pfade der Typinferenz und der Callback-Kompatibilität, die für die Diagnose verantwortlich sind, und füge anschließend einen Regressionstest hinzu, der zeigt, dass der kwargs-Callback akzeptiert wird, ohne den Rückgabetyp zu verlieren.

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
35/100

Neue Issues direkt in Ihr Postfach

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