python / python/mypy

mypy incorrectly complains about incompatible type error

Aperta
#18,267 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

bug topic-type-context
Lingua principale
Python
Stelle
20.6k
Fork
3.3k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Bug Report

mypy is incorrectly complaining that the arguments to a function have an incompatible type, when used in the context of a class attribute.

In the example below, we are creating a library of fields (StringField, IntegerField, etc.), to be used in a runtime schema validation library. In addition, we create a OneOfField that takes a list of sub-fields via the with_ function. Creating a OneOfField works fine by itself, but when assigned to a class attribute, mypy incorrectly complains about a type error.

from typing import Generic, TypeVar, Union, assert_type, cast, overload, Self

InputType = TypeVar("InputType")
OutputType = TypeVar("OutputType")


class Field(Generic[InputType, OutputType]):
    # We want both InputType and OutputType to be used in functions like the
    # ones below, so they can't be marked covariant or contravariant.
    def validate(self, input: Any) -> InputType:
        return cast(InputType, input)
    
    def render(self, input: InputType) -> OutputType:
        return cast(OutputType, input)
    
    def serialize(self, output: OutputType) -> str:
        return ""


class StringField(Field[str, str]):
    pass


class IntegerField(Field[int, int]):
    pass


class BooleanField(Field[bool, bool]):
    pass


def input_type(field: Field[InputType, OutputType]) -> InputType:
    return cast(InputType, field)


OneOfInputType = TypeVar("OneOfInputType")
OneOfOutputType = TypeVar("OneOfOutputType")

AInputType = TypeVar("AInputType")
AOutputType = TypeVar("AOutputType")

BInputType = TypeVar("BInputType")
BOutputType = TypeVar("BOutputType")

CInputType = TypeVar("CInputType")
COutputType = TypeVar("COutputType")


class OneOfField(
    Generic[OneOfInputType, OneOfOutputType],
    Field[OneOfInputType, OneOfOutputType],
):
    fields: list[Field]

    def __init__(self) -> None:
        super().__init__()
        self.fields = []

    @overload
    def with_(
        self,
        a: Field[AInputType, AOutputType],
        b: Field[BInputType, BOutputType],
    ) -> "OneOfField[Union[AInputType, BInputType], Union[AOutputType, BOutputType]]": ...

    @overload
    def with_(
        self,
        a: Field[AInputType, AOutputType],
        b: Field[BInputType, BOutputType],
        c: Field[CInputType, COutputType],
    ) -> "OneOfField[Union[AInputType, BInputType, CInputType], Union[AOutputType, BOutputType, COutputType]]": ...

    def with_(
        self,
        a: Field[AInputType, AOutputType],
        b: Field[BInputType, BOutputType],
        c: Field[CInputType, COutputType] | None = None,
    ) -> "OneOfField":
        if c is None:
            self.fields = [a, b]
        else:
            self.fields = [a, b, c]
        return self

    def return_self(self) -> Self:
        return self

# This works:
field = OneOfField().with_(StringField(), IntegerField())
assert_type(field, OneOfField[Union[str, int], Union[str, int]])
assert_type(input_type(field), Union[str, int])


# This doesn't work:
class MyModel:
    # error: Argument 1 to "with_" of "OneOfField" has incompatible type "StringField"; expected "Field[str | int, str | int]"  [arg-type]
    # error: Argument 2 to "with_" of "OneOfField" has incompatible type "IntegerField"; expected "Field[str | int, str | int]"  [arg-type]
    foo: str | int = input_type(OneOfField().with_(StringField(), IntegerField()))
    
# This works?
class MyModel2:
    foo: str | int = input_type(OneOfField().with_(StringField(), IntegerField()).return_self())

To Reproduce

https://gist.github.com/mypy-play/4619ce6d001d7c7c6994d2b6c912424a

This does not repro in Pyright: link

Expected Behavior

No errors.

Actual Behavior

mypy raises an invalid error saying the argument to with_ is invalid.

Your Environment

  • Mypy version used: 1.13.0
  • Mypy command-line flags: N/A
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.13

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia eseguendo l'esempio Python dell'issue o la relativa riproduzione mypy-play collegata con mypy 1.13.0, concentrandoti sull'espressione dell'attributo di classe e sulla chiamata concatenata a with_. Traccia quindi come vengono gestiti il tipaggio contestuale e i metodi generici sovraccaricati, poi aggiungi un test di regressione che mostri che l'espressione non produce errori, preservando al contempo il comportamento autonomo esistente.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
devtools
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.