Zum Inhalt

Mypy

Pydantic funktioniert sofort gut mit mypy .

Pydantic wird jedoch auch mit einem Mypy-Plugin ausgeliefert, das mypy eine Reihe wichtiger pydantic-spezifischer Funktionen hinzufügt, die die Fähigkeit zur Typprüfung Ihres Codes verbessern.

Betrachten Sie beispielsweise das folgende Skript:

from datetime import datetime
from typing import List, Optional

from pydantic import BaseModel


class Model(BaseModel):
    age: int
    first_name = 'John'
    last_name: Optional[str] = None
    signup_ts: Optional[datetime] = None
    list_of_ints: List[int]


m = Model(age=42, list_of_ints=[1, '2', b'3'])
print(m.middle_name)  # not a model field!
Model()  # will raise a validation error for age and list_of_ints

Ohne spezielle Konfiguration erkennt mypy die fehlende Modellfeldanmerkung nicht und warnt vor dem Argument list_of_ints , das Pydantic korrekt analysiert:

test.py:15: error: List item 1 has incompatible type "str"; expected "int"  [list-item]
test.py:15: error: List item 2 has incompatible type "bytes"; expected "int"  [list-item]
test.py:16: error: "Model" has no attribute "middle_name"  [attr-defined]
test.py:17: error: Missing named argument "age" for "Model"  [call-arg]
test.py:17: error: Missing named argument "list_of_ints" for "Model"  [call-arg]

Aber wenn das Plugin aktiviert ist , wird der richtige Fehler angezeigt:

9: error: Untyped fields disallowed  [pydantic-field]
16: error: "Model" has no attribute "middle_name"  [attr-defined]
17: error: Missing named argument "age" for "Model"  [call-arg]
17: error: Missing named argument "list_of_ints" for "Model"  [call-arg]

Mit dem pydantic mypy-Plugin können Sie Ihre Modelle furchtlos umgestalten, da Sie wissen, dass mypy alle Fehler erkennt, wenn sich Ihre Feldnamen oder -typen ändern.

Es gibt noch weitere Vorteile! Weitere Einzelheiten finden Sie weiter unten.

Verwendung von mypy ohne das Plugin

Sie können Ihren Code über mypy ausführen mit:

mypy \
  --ignore-missing-imports \
  --follow-imports=skip \
  --strict-optional \
  pydantic_mypy_test.py

Streng optional

Damit Ihr Code mit --strict-optional übergeben werden kann, müssen Sie Optional[] oder einen Alias von Optional[] für alle Felder mit None als Standard verwenden. (Dies ist Standard bei mypy.)

Andere Pydantic-Schnittstellen

Pydantic- Datenklassen und der validate_call -Dekorator sollten auch gut mit mypy funktionieren.

Funktionen des Mypy-Plugins

Generieren Sie eine Signatur für Model.__init__

  • Alle erforderlichen Felder, die keine dynamisch ermittelten Aliase haben, werden als erforderliche Schlüsselwortargumente einbezogen.
  • Wenn Config.populate_by_name=True , verwendet die generierte Signatur die Feldnamen anstelle von Aliasnamen.
  • Wenn Config.extra='forbid' und Sie keine dynamisch ermittelten Aliase verwenden, lässt die generierte Signatur keine unerwarteten Eingaben zu.
  • Optional: Wenn die Plugin-Einstellung init_forbid_extra auf True gesetzt ist, lösen unerwartete Eingaben in __init__ Fehler aus, auch wenn Config.extra nicht 'forbid' ist.
  • Optional: Wenn die Plugin-Einstellung init_typed auf True gesetzt ist, verwendet die generierte Signatur die Typen der Modellfelder (andernfalls werden sie als Any annotiert, um das Parsen zu ermöglichen).

Generieren Sie eine typisierte Signatur für Model.model_construct

  • Die model_construct -Methode ist eine Alternative zu __init__ wenn bekannt ist, dass Eingabedaten gültig sind und nicht analysiert werden sollten. Da diese Methode keine Laufzeitvalidierung durchführt, ist eine statische Überprüfung wichtig, um Fehler zu erkennen.

Respektieren Sie Config.frozen

  • Wenn Config.frozen True ist, erhalten Sie einen Mypy-Fehler, wenn Sie versuchen, den Wert eines Modellfelds zu ändern. vgl. falsche Unveränderlichkeit .

Generieren Sie eine Signatur für dataclasses

Beachten Sie den Typ des Field default und default_factory

  • Ein Feld mit sowohl einem default als auch einem default_factory führt bei der statischen Prüfung zu einem Fehler.
  • Der Typ des default und default_factory -Werts muss mit dem des Felds kompatibel sein.

Warnen Sie vor der Verwendung untypisierter Felder

  • Sie erhalten jedes Mal einen Mypy-Fehler, wenn Sie einem Modell ein öffentliches Attribut zuweisen, ohne seinen Typ zu kommentieren
  • Wenn Sie eine ClassVar festlegen möchten, sollten Sie das Feld mithilfe von typing.ClassVar explizit mit Anmerkungen versehen

Optionale Funktionen:

Verhindern Sie die Verwendung erforderlicher dynamischer Aliase

  • Wenn die Plugin-Einstellung warn_required_dynamic_aliases auf True gesetzt ist, erhalten Sie jedes Mal einen Mypy-Fehler, wenn Sie einen dynamisch ermittelten Alias oder Aliasgenerator für ein Modell mit Config.populate_by_name=False verwenden.
  • Dies ist wichtig, denn wenn solche Aliase vorhanden sind, kann mypy Prüfaufrufe an __init__ nicht richtig eingeben. In diesem Fall werden alle Argumente standardmäßig als optional behandelt.

Aktivieren des Plugins

Um das Plugin zu aktivieren, fügen Sie einfach pydantic.mypy zur Liste der Plugins in Ihrer mypy-Konfigurationsdatei hinzu (dies könnte mypy.ini , pyproject.toml oder setup.cfg sein).

Um zu beginnen, müssen Sie lediglich eine mypy.ini -Datei mit folgendem Inhalt erstellen:

[mypy]
plugins = pydantic.mypy

!!! Hinweis Wenn Sie pydantic.v1 -Modelle verwenden, müssen Sie pydantic.v1.mypy zu Ihrer Plugin-Liste hinzufügen.

Das Plugin ist mit den Mypy-Versionen >=0.930 kompatibel.

Weitere Informationen finden Sie in den Plugin- Konfigurationsdokumenten.

Konfigurieren des Plugins

Um die Werte der Plugin-Einstellungen zu ändern, erstellen Sie in Ihrer Mypy-Konfigurationsdatei einen Abschnitt mit dem Namen [pydantic-mypy] und fügen Sie alle Schlüssel-Wert-Paare für Einstellungen hinzu, die Sie überschreiben möchten.

Eine mypy.ini Datei mit allen aktivierten Plugin-Striktheitsflags (und auch einigen anderen Mypy-Striktheitsflags) könnte wie folgt aussehen:

[mypy]
plugins = pydantic.mypy

follow_imports = silent
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
check_untyped_defs = True
no_implicit_reexport = True

# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = True

[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True

Ab mypy>=0.900 kann die mypy-Konfiguration auch in der Datei pyproject.toml statt in mypy.ini enthalten sein. Die gleiche Konfiguration wie oben wäre:

[tool.mypy]
plugins = [
  "pydantic.mypy"
]

follow_imports = "silent"
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
check_untyped_defs = true
no_implicit_reexport = true

# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = true

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true

Hinweis zu --disallow-any-explicit

Wenn Sie die Mypy-Konfigurationseinstellung --disallow-any-explicit (oder andere Einstellungen, die Any verbieten) verwenden, können beim Erweitern von BaseModel no-any-explicit Fehler auftreten. Dies liegt daran, dass mypy -Plugin von Pydantic standardmäßig eine __init__ Methode mit einer Signatur wie „ hinzufügt def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):

!!! Hinweis „Warum die zusätzliche Signatur?“ Das Pydantic- mypy -Plugin fügt eine __init__ Methode mit einer Signatur wie hinzu def __init__(self, field_1: Any, field_2: Any, **kwargs: Any): um Typfehler zu vermeiden, wenn Modelle mit Typen initialisiert werden, die nicht mit den Feldanmerkungen übereinstimmen. Beispielsweise würde Model(date='2024-01-01') ohne diese Any Signatur einen Typfehler auslösen, aber Pydantic hat die Möglichkeit, die Zeichenfolge '2024-01-01' in einen datetime.date -Typ zu analysieren.

Um dieses Problem zu beheben, müssen Sie die Einstellungen für den strengen Modus für das Pydantic-Mypy-Plugin aktivieren. Fügen Sie insbesondere diese Optionen zu Ihrem Abschnitt [pydantic-mypy] hinzu:

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true

Mit init_forbid_extra = True werden die **kwargs aus der generierten __init__ Signatur entfernt. Mit init_typed = True werden die Any Typen für Felder durch ihre tatsächlichen Typhinweise ersetzt.

Mit dieser Konfiguration können Sie --disallow-any-explicit verwenden, ohne dass bei Ihren Pydantic-Modellen Fehler auftreten. Beachten Sie jedoch, dass diese strengere Prüfung einige gültige Pydantic-Anwendungsfälle (z. B. die Übergabe einer Zeichenfolge für ein Datum/Uhrzeit-Feld) als Typfehler markieren kann.


本文总阅读量