Ekstensi mypy dengan plugin

Selamat siang teman Dan kami terus meningkatkan intensitas peluncuran kursus baru dan sekarang dengan senang hati mengumumkan bahwa kelas-kelas di kursus "Web-developer in Python" akan dimulai pada akhir April. Dalam hal ini, kami secara tradisional membagikan terjemahan materi yang bermanfaat. Mari kita mulai.

Python dikenal sebagai bahasa pengetikan yang dinamis. Sangat mudah untuk menulis kerangka kerja DSL yang sulit diurai dengan alat pemeriksaan tipe statis. Meskipun demikian, dengan inovasi fungsional terbaru mypy , seperti protokol dan tipe literal , serta dukungan dasar untuk dukungan metaclasses dan deskriptor, kita sering dapat mendapatkan jenis yang tepat, tetapi masih sulit untuk menghindari positif palsu dan faktor negatif lainnya. Untuk mengatasi masalah ini dan menghindari kebutuhan untuk menyesuaikan sistem tipe untuk setiap kerangka kerja, mypy mendukung sistem plug-in . Plugin adalah modul dalam Python yang menyediakan kait plugin yang akan dipanggil mypy saat memeriksa jenis kelas dan fungsi yang berinteraksi dengan pustaka atau kerangka kerja. Dengan demikian, dimungkinkan untuk lebih akurat membedakan jenis fungsi yang dikembalikan, yang sebaliknya sangat sulit untuk diungkapkan, atau secara otomatis menghasilkan beberapa metode kelas untuk mencerminkan efek dari dekorator. Untuk mempelajari lebih lanjut tentang arsitektur sistem plug-in dan melihat daftar lengkap fitur, lihat dokumentasi .



Plugin terkait untuk pustaka standar

Mypy hadir dengan plugin default untuk mengimplementasikan fungsi dasar dan kelas, serta dataclasses ctypes , contextlib dan dataclasses . Ini juga termasuk plugin untuk attrs (secara historis telah menjadi plugin pihak ketiga pertama yang ditulis untuk mypy ). Plugin ini memungkinkan mypy untuk lebih akurat menentukan jenis dan dengan benar memeriksa kode untuk jenis menggunakan fungsi pustaka ini. Untuk menunjukkan ini dengan contoh, lihat cuplikan kode:

 from dataclasses import dataclass from typing import Generic, TypeVar @dataclass class TaggedVector(Generic[T]): data: List[T] tag: str position = TaggedVector([0, 0, 0], 'origin') 

Di atas, get_class_decorator_hook() dipanggil ketika kelas didefinisikan. Ini menambahkan metode yang dibuat secara otomatis, termasuk __init__() , ke badan fungsi. Mypy menggunakan konstruktor semacam itu untuk menghitung TaggedVector[int] sebagai tipe untuk position . Seperti yang Anda lihat dari contoh, plugin berfungsi bahkan dengan kelas generik.

Berikut ini sepotong kode lainnya:

 from contextlib import contextmanager @contextmanager def timer(title: str) -> Iterator[float]: ... with timer(9000) as tm: ... 

Di sini get_function_hook() memberikan jenis pengembalian yang tepat untuk dekorator get_function_hook() , sehingga panggilan ke fungsi yang didekorasi dapat diperiksa untuk kesesuaian dengan jenis tertentu. Sekarang mypy dapat mengenali kesalahan: argumen untuk timer() harus berupa string.

Kombinasi plugin dan bertopik

Selain menggunakan fungsi Python dinamis, kerangka kerja sering mengalami masalah memiliki API besar. Mypy membutuhkan file rintisan untuk pustaka untuk menguji kode yang menggunakan pustaka ini (hanya jika pustaka tidak berisi anotasi bawaan, yang tidak begitu umum). Mendistribusikan rintisan untuk kerangka kerja besar dengan typeshed bukanlah praktik yang umum:

  • Typeshed memiliki siklus rilis yang relatif lambat (dikirimkan bersama mypy ).
  • Rintisan yang tidak lengkap dapat menyebabkan panggilan palsu, yang akan sangat sulit untuk dihindari.
  • Jangan hanya mencampur bertopik dari versi pengetik berbeda.

Paket rintisan yang diperkenalkan dalam PEP 561 melakukan hal berikut:

  • Pengembang dapat merilis paket rintisan sesering yang mereka inginkan.
  • Pengguna yang belum memilih untuk menggunakan paket tidak akan melihat positif palsu.
  • Anda dapat dengan aman menginstal versi acak dari beberapa paket rintisan yang berbeda.

Selain itu, pip memungkinkan Anda untuk menggabungkan berbagai tulisan rintisan untuk perpustakaan dan plugin mypy yang sesuai ke dalam satu distribusi. Rintisan untuk kerangka mypy atau plugin yang sesuai dapat dengan mudah dikembangkan dan disatukan menjadi satu distribusi, yang sangat berguna karena plugin mengisi definisi yang hilang atau tidak akurat dalam rintisan.

Contoh terakhir dari paket tersebut adalah SQLAlchemy stubs and plugin , dengan rilis publik pertama versi 0.1, yang diterbitkan beberapa waktu lalu di PyPI. Terlepas dari kenyataan bahwa proyek ini dalam versi Alpha awal, kita dapat menggunakannya dengan aman di DropBox untuk meningkatkan pemeriksaan tipe. Plugin memahami deklarasi ORM dasar:

 from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) 

Dalam cuplikan kode di atas, plugin menggunakan get_dynamic_class_hook() untuk memberi tahu mypy bahwa Base adalah kelas dasar yang valid, bahkan jika itu tidak terlihat seperti itu. Kemudian get_base_class_hook() dipanggil untuk mendefinisikan Pengguna, dan menambahkan beberapa atribut yang dibuat secara otomatis. Selanjutnya, kami membuat instance dari model:

user = User(id=42, name=42)

get_function_hook() dipanggil, jadi mypy dapat mengindikasikan kesalahan: nilai integer diterima sebagai ganti nama pengguna.

Rintisan bertopik mendefinisikan Column sebagai deskriptor generik , sehingga atribut model mendapatkan tipe yang benar:

 id_col = User.id # Inferred type is "Column[int]" name = user.name # Inferred type is "Optional[str]" 

Kami menyambut PR yang menambahkan tipe yang lebih tepat ke bertopik (kemajuan untuk modul inti dilacak di sini ).

Berikut adalah beberapa jebakan yang kami temukan saat mengerjakan colokan:

  • Gunakan __getattr__() untuk menghindari kesalahan positif pada tahap awal ketika stubs tidak selesai (ini mencegah kesalahan mypy jika atribut modul hilang). Anda juga dapat menggunakan ini dalam file __init__.py jika ada submodula yang hilang.
  • Deskriptor sering membantu dengan definisi tipe yang lebih akurat untuk akses atribut khusus (seperti pada contoh Kolom yang kami ulas di atas). Menggunakan deskriptor baik-baik saja bahkan jika implementasi aktual runtime menggunakan mekanisme yang lebih kompleks, termasuk metaclass, misalnya.
  • Tanpa ragu, nyatakan kelas kerangka kerja sebagai digeneralisasi. Terlepas dari kenyataan bahwa mereka tidak seperti saat runtime, teknik ini memungkinkan Anda untuk lebih akurat menentukan jenis beberapa elemen kerangka kerja, sementara kesalahan runtime dapat dengan mudah dielakkan . (Kami berharap bahwa kerangka kerja akan secara bertahap menambahkan dukungan typing.Generic untuk tipe generik, secara eksplisit mewarisi kelas yang sesuai dari typing.Generic . typing.Generic .)

Plugin mypy baru-baru ini dirilis

Sudah ada beberapa plugin yang tersedia untuk kerangka kerja Python populer. Terlepas dari plugin SQLAlchemy yang disebutkan di atas, paket sampel penting lainnya dengan bertopik dan plugin mypy built-in termasuk bertopik untuk antarmuka Django dan Zope . Pekerjaan aktif sedang berlangsung pada proyek-proyek ini.

Menginstal dan menghubungkan paket rintisan dan plugin

Gunakan pip untuk menginstal paket plugin untuk mypy dan / atau rintisan ke lingkungan virtual di mana mypy sudah diinstal :

  $ pip install sqlalchemy-stubs 

Mypy akan secara otomatis mendeteksi bertopik yang diinstal. Untuk menghubungkan plugin yang diinstal, sertakan langsung di mypy.ini (atau dalam file konfigurasi pengguna):

 [mypy] plugins = sqlmypy, mypy_django_plugin.main 

Mengembangkan plugin mypy dan menulis tulisan rintisan

Jika Anda ingin mengembangkan paket stubs dan plugins untuk kerangka kerja yang Anda gunakan, kita bisa menggunakan repositori sqlalchemy-stubs sebagai templat. Ini termasuk setup.py , pengujian infrastruktur menggunakan tes berbasis data, dan contoh kelas plug-in dengan satu set kait untuk plug-in (kait plugin). Kami merekomendasikan menggunakan stubgen untuk secara otomatis menghasilkan stubgen yang datang dengan mypy untuk mulai menggunakannya. Stubgen telah mypy 0.670 meningkat di mypy 0.670 .

Periksa dokumentasi jika Anda ingin tahu lebih banyak tentang sistem plugin mypy . Anda juga dapat mencari kode sumber plugin yang dibahas di artikel di Internet. Jika Anda memiliki pertanyaan, Anda dapat menanyakannya di sini .

15 April akan menjadi webinar terbuka gratis di lapangan, yang akan diselenggarakan oleh salah satu penyelenggara komunitas Python Moskow - Vladimir Filonov , mendaftar, itu akan menarik. Dan sekarang kami menunggu komentar Anda tentang materi yang diterjemahkan.

Source: https://habr.com/ru/post/id447556/


All Articles