Lewati ke isi

Mypy

Pydantic bekerja dengan baik dengan mypy langsung dari kotaknya.

Namun, Pydantic juga dilengkapi dengan plugin mypy yang menambahkan sejumlah fitur penting khusus pydantic ke mypy yang meningkatkan kemampuannya untuk memeriksa tipe kode Anda.

Misalnya, perhatikan skrip berikut:

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

Tanpa konfigurasi khusus apa pun, mypy tidak menangkap anotasi bidang model yang hilang dan memperingatkan tentang argumen list_of_ints yang diurai oleh Pydantic dengan benar:

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]

Tetapi dengan plugin yang diaktifkan , ini memberikan kesalahan yang benar:

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]

Dengan plugin pydantic mypy, Anda tanpa rasa takut dapat memfaktorkan ulang model Anda karena mengetahui mypy akan menangkap kesalahan apa pun jika nama atau jenis bidang Anda berubah.

Ada manfaat lainnya juga! Lihat di bawah untuk lebih jelasnya.

Menggunakan mypy tanpa plugin

Anda dapat menjalankan kode Anda melalui mypy dengan:

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

Opsional Ketat

Agar kode Anda dapat lolos dengan --strict-optional , Anda perlu menggunakan Optional[] atau alias Optional[] untuk semua bidang dengan None sebagai default. (Ini standar dengan mypy.)

Antarmuka Pydantic lainnya

Kelas data Pydantic dan dekorator validate_call juga harus bekerja dengan baik dengan mypy.

Kemampuan Plugin Mypy

Hasilkan tanda tangan untuk Model.__init__

  • Bidang wajib apa pun yang tidak memiliki alias yang ditentukan secara dinamis akan disertakan sebagai argumen kata kunci wajib.
  • Jika Config.populate_by_name=True , tanda tangan yang dihasilkan akan menggunakan nama bidang, bukan alias.
  • Jika Config.extra='forbid' dan Anda tidak menggunakan alias yang ditentukan secara dinamis, tanda tangan yang dihasilkan tidak akan mengizinkan masukan yang tidak diharapkan.
  • Opsional: Jika pengaturan plugin init_forbid_extra diatur ke True , masukan tak terduga ke __init__ akan menimbulkan kesalahan meskipun Config.extra bukan 'forbid' .
  • Opsional: Jika pengaturan plugin init_typed diatur ke True , tanda tangan yang dihasilkan akan menggunakan jenis bidang model (jika tidak, maka tanda tangan tersebut akan diberi anotasi Any untuk memungkinkan penguraian).

Hasilkan tanda tangan yang diketik untuk Model.model_construct

  • Metode model_construct merupakan alternatif dari __init__ ketika data masukan diketahui valid dan tidak boleh diurai. Karena metode ini tidak melakukan validasi runtime, pemeriksaan statis penting untuk mendeteksi kesalahan.

Hormati Config.frozen

  • Jika Config.frozen adalah True , Anda akan mendapatkan kesalahan mypy jika mencoba mengubah nilai bidang model; lih. kekekalan palsu .

Hasilkan tanda tangan untuk dataclasses

  • kelas dihiasi dengan @pydantic.dataclasses.dataclass tipenya diperiksa sama dengan kelas data Python standar
  • Itu @pydantic.dataclasses.dataclass dekorator menerima argumen kata kunci config yang memiliki arti yang sama dengan subkelas Config .

Hormati jenis Field default dan default_factory

  • Bidang dengan default dan default_factory akan menghasilkan kesalahan selama pemeriksaan statis.
  • Jenis nilai default dan default_factory harus kompatibel dengan salah satu bidang.

Peringatkan tentang penggunaan bidang yang tidak diketik

  • Anda akan mendapatkan kesalahan mypy setiap kali Anda menetapkan atribut publik pada model tanpa memberi anotasi pada tipenya
  • Jika tujuan Anda adalah menetapkan ClassVar, Anda harus secara eksplisit memberi anotasi pada bidang tersebut menggunakan mengetik.ClassVar

Kemampuan Opsional:

Cegah penggunaan alias dinamis yang diperlukan

  • Jika pengaturan plugin warn_required_dynamic_aliases diatur ke True , Anda akan mendapatkan kesalahan mypy setiap kali Anda menggunakan alias atau generator alias yang ditentukan secara dinamis pada model dengan Config.populate_by_name=False .
  • Hal ini penting karena jika alias tersebut ada, mypy tidak dapat mengetikkan panggilan cek ke __init__ dengan benar. Dalam hal ini, secara default akan memperlakukan semua argumen sebagai opsional.

Mengaktifkan Plugin

Untuk mengaktifkan plugin, cukup tambahkan pydantic.mypy ke daftar plugin di file konfigurasi mypy Anda (bisa berupa mypy.ini , pyproject.toml , atau setup.cfg ).

Untuk memulai, yang perlu Anda lakukan hanyalah membuat file mypy.ini dengan konten berikut:

[mypy]
plugins = pydantic.mypy

!!! catatan Jika Anda menggunakan model pydantic.v1 , Anda harus menambahkan pydantic.v1.mypy ke daftar plugin Anda.

Plugin ini kompatibel dengan versi mypy >=0.930 .

Lihat dokumen konfigurasi plugin untuk lebih jelasnya.

Mengonfigurasi Plugin

Untuk mengubah nilai pengaturan plugin, buat bagian di file konfigurasi mypy Anda yang disebut [pydantic-mypy] , dan tambahkan pasangan nilai kunci apa pun untuk pengaturan yang ingin Anda ganti.

File mypy.ini dengan semua tanda keketatan plugin diaktifkan (dan beberapa tanda keketatan mypy lainnya juga) mungkin terlihat seperti:

[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

Mulai mypy>=0.900 , konfigurasi mypy juga dapat disertakan dalam file pyproject.toml daripada mypy.ini . Konfigurasi yang sama seperti di atas adalah:

[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

Catatan tentang --disallow-any-explicit

Jika Anda menggunakan pengaturan konfigurasi mypy --disallow-any-explicit (atau pengaturan lain yang melarang Any ), Anda mungkin mengalami kesalahan no-any-explicit saat memperluas BaseModel . Ini karena secara default, plugin mypy Pydantic menambahkan metode __init__ dengan tanda tangan seperti def __init__(self, field_1: Any, field_2: Any, **kwargs: Any):

!!! catatan "Mengapa tanda tangan tambahan?" Plugin Pydantic mypy menambahkan metode __init__ dengan tanda tangan seperti def __init__(self, field_1: Any, field_2: Any, **kwargs: Any): untuk menghindari kesalahan ketik saat menginisialisasi model dengan tipe yang tidak cocok dengan anotasi bidang. Misalnya Model(date='2024-01-01') akan memunculkan kesalahan tipe tanpa tanda tangan Any ini, tetapi Pydantic memiliki kemampuan untuk mengurai string '2024-01-01' menjadi tipe datetime.date .

Untuk mengatasi masalah ini, Anda perlu mengaktifkan pengaturan mode ketat untuk plugin Pydantic mypy. Secara khusus, tambahkan opsi ini ke bagian [pydantic-mypy] Anda:

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

Dengan init_forbid_extra = True , **kwargs dihapus dari tanda tangan __init__ yang dihasilkan. Dengan init_typed = True , bidang Tipe Any diganti dengan petunjuk tipe sebenarnya.

Konfigurasi ini memungkinkan Anda menggunakan --disallow-any-explicit tanpa mendapatkan kesalahan pada model Pydantic Anda. Namun, perlu diketahui bahwa pemeriksaan yang lebih ketat ini mungkin menandai beberapa kasus penggunaan Pydantic yang valid (seperti meneruskan string untuk bidang datetime) sebagai kesalahan tipe.


本文总阅读量