Ga naar inhoud

Mypy

Pydantic werkt direct uit de doos goed met mypy .

Pydantic wordt echter ook geleverd met een mypy-plug-in die een aantal belangrijke pydantic-specifieke functies aan mypy toevoegt die de mogelijkheid om uw code te controleren verbeteren.

Neem bijvoorbeeld het volgende script:

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

Zonder enige speciale configuratie vangt mypy de ontbrekende modelveldannotatie niet op en waarschuwt voor het list_of_ints argument dat Pydantic correct parseert:

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]

Maar als de plug-in is ingeschakeld , geeft deze de juiste foutmelding:

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]

Met de pydantic mypy-plug-in kunt u uw modellen onbevreesd herstructureren, wetende dat mypy fouten zal ontdekken als uw veldnamen of typen veranderen.

Er zijn ook andere voordelen! Zie hieronder voor meer details.

Mypy gebruiken zonder de plug-in

U kunt uw code via mypy uitvoeren met:

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

Strikt optioneel

Om ervoor te zorgen dat uw code wordt doorgegeven met --strict-optional , moet u Optional[] of een alias Optional[] gebruiken voor alle velden met None als standaard. (Dit is standaard bij mypy.)

Andere Pydantic-interfaces

Pydantic dataclasses en de validate_call decorateur zouden ook goed moeten werken met mypy.

Mogelijkheden voor Mypy-plug-ins

Genereer een handtekening voor Model.__init__

  • Alle verplichte velden die geen dynamisch bepaalde aliassen hebben, worden opgenomen als vereiste trefwoordargumenten.
  • Als Config.populate_by_name=True zal de gegenereerde handtekening de veldnamen gebruiken in plaats van aliassen.
  • Als Config.extra='forbid' en u geen gebruik maakt van dynamisch bepaalde aliassen, zal de gegenereerde handtekening geen onverwachte invoer toestaan.
  • Optioneel: Als de init_forbid_extra plugin-instelling is ingesteld op True , zullen onverwachte invoer naar __init__ fouten veroorzaken, zelfs als Config.extra niet 'forbid' is.
  • Optioneel: Als de plug-ininstelling init_typed is ingesteld op True , gebruikt de gegenereerde handtekening de typen modelvelden (anders worden ze geannoteerd als Any om parseren mogelijk te maken).

Genereer een getypte handtekening voor Model.model_construct

  • De model_construct methode is een alternatief voor __init__ wanneer bekend is dat invoergegevens geldig zijn en niet mogen worden geparseerd. Omdat deze methode geen runtime-validatie uitvoert, is statische controle belangrijk om fouten op te sporen.

Respecteer Config.frozen

  • Als Config.frozen True is, krijgt u een mypy-fout als u de waarde van een modelveld probeert te wijzigen; vgl. valse onveranderlijkheid .

Genereer een handtekening voor dataclasses

  • klassen versierd met @pydantic.dataclasses.dataclass worden op dezelfde manier gecontroleerd als standaard Python-dataklassen
  • De @pydantic.dataclasses.dataclass decorateur accepteert een config trefwoordargument dat dezelfde betekenis heeft als de Config -subklasse .

Respecteer het type default en default_factory van het Field

  • Velden met zowel een default als een default_factory resulteren in een fout tijdens de statische controle.
  • Het type van de waarde default en default_factory moet compatibel zijn met die van het veld.

Waarschuw voor het gebruik van niet-getypeerde velden

  • Elke keer dat u een openbaar kenmerk aan een model toewijst zonder het type ervan te annoteren, krijgt u een mypy-fout
  • Als het uw doel is om een ClassVar in te stellen, moet u het veld expliciet annoteren met behulp van typing.ClassVar

Optionele mogelijkheden:

Voorkom het gebruik van vereiste dynamische aliassen

  • Als de plugin-instelling warn_required_dynamic_aliases is ingesteld op True , krijgt u elke keer dat u een dynamisch bepaalde alias of aliasgenerator gebruikt op een model met Config.populate_by_name=False een mypy-fout.
  • Dit is belangrijk omdat als dergelijke aliassen aanwezig zijn, mypy geen check-aanroepen naar __init__ kan typen. In dit geval worden alle argumenten standaard als optioneel behandeld.

De plug-in inschakelen

Om de plug-in in te schakelen, voegt u gewoon pydantic.mypy toe aan de lijst met plug-ins in uw mypy-configuratiebestand (dit kan mypy.ini , pyproject.toml of setup.cfg zijn).

Om aan de slag te gaan, hoeft u alleen maar een mypy.ini bestand te maken met de volgende inhoud:

[mypy]
plugins = pydantic.mypy

!!! opmerking Als u pydantic.v1 -modellen gebruikt, moet u pydantic.v1.mypy toevoegen aan uw lijst met plug-ins.

De plug-in is compatibel met mypy-versies >=0.930 .

Zie de plug-inconfiguratiedocumentatie voor meer details.

De plug-in configureren

Om de waarden van de plug-in-instellingen te wijzigen, maakt u een sectie in uw mypy-configuratiebestand met de naam [pydantic-mypy] en voegt u eventuele sleutel-waardeparen toe voor de instellingen die u wilt overschrijven.

Een mypy.ini bestand waarin alle striktheidsvlaggen van de plug-in zijn ingeschakeld (en ook enkele andere striktheidsvlaggen van mypy) kan er als volgt uitzien:

[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

Vanaf mypy>=0.900 kan mypy config ook worden opgenomen in het bestand pyproject.toml in plaats van mypy.ini . Dezelfde configuratie als hierboven zou zijn:

[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

Opmerking over --disallow-any-explicit

Als u de --disallow-any-explicit mypy config-instelling gebruikt (of andere instellingen die Any verbieden), kunt u no-any-explicit fouten tegenkomen bij het uitbreiden BaseModel . Dit komt omdat mypy -plug-in van Pydantic standaard een __init__ methode toevoegt met een handtekening zoals def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):

!!! note "Waarom de extra handtekening?" De Pydantic mypy -plug-in voegt een __init__ methode toe met een handtekening zoals def __init__(self, field_1: Any, field_2: Any, **kwargs: Any): om typefouten te voorkomen bij het initialiseren van modellen met typen die niet overeenkomen met de veldannotaties. Model(date='2024-01-01') zou bijvoorbeeld een typefout opleveren zonder deze Any -handtekening, maar Pydantic heeft de mogelijkheid om de string '2024-01-01' te ontleden in een datetime.date type.

Om dit probleem op te lossen, moet u strikte modusinstellingen inschakelen voor de Pydantic mypy-plug-in. Voeg specifiek deze opties toe aan uw [pydantic-mypy] sectie:

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

Met init_forbid_extra = True worden de **kwargs verwijderd uit de gegenereerde __init__ handtekening. Met init_typed = True worden de velden Any types vervangen door hun daadwerkelijke typehints.

Met deze configuratie kunt u --disallow-any-explicit gebruiken zonder fouten te krijgen op uw Pydantic-modellen. Houd er echter rekening mee dat deze strengere controle sommige geldige Pydantic-gebruiksscenario's (zoals het doorgeven van een string voor een datetime-veld) als typefouten kan markeren.


本文总阅读量