Laluan untuk menyemak taip 4 juta baris kod Python. Bahagian 1

Hari ini kami membawa perhatian kepada anda bahagian pertama terjemahan bahan tentang cara Dropbox menangani kawalan jenis kod Python.

Laluan untuk menyemak taip 4 juta baris kod Python. Bahagian 1

Dropbox banyak menulis dalam Python. Ia adalah bahasa yang kami gunakan secara meluas, baik untuk perkhidmatan back-end dan aplikasi klien desktop. Kami juga banyak menggunakan Go, TypeScript dan Rust, tetapi Python adalah bahasa utama kami. Memandangkan skala kami, dan kami bercakap tentang berjuta-juta baris kod Python, ternyata penaipan dinamik kod sedemikian tidak perlu merumitkan pemahamannya dan mula menjejaskan produktiviti buruh secara serius. Untuk mengurangkan masalah ini, kami telah mula mengalihkan kod kami secara beransur-ansur kepada pemeriksaan jenis statik menggunakan mypy. Ini mungkin sistem semakan jenis kendiri yang paling popular untuk Python. Mypy ialah projek sumber terbuka, pembangun utamanya berfungsi dalam Dropbox.

Dropbox ialah salah satu syarikat pertama yang melaksanakan semakan jenis statik dalam kod Python pada skala ini. Mypy digunakan dalam beribu-ribu projek hari ini. Alat ini berkali-kali, seperti yang mereka katakan, "diuji dalam pertempuran." Kami telah pergi jauh untuk sampai ke tempat kami sekarang. Sepanjang perjalanan, terdapat banyak usaha yang tidak berjaya dan percubaan yang gagal. Siaran ini merangkumi sejarah penyemakan jenis statik dalam Python, dari permulaannya yang sukar sebagai sebahagian daripada projek penyelidikan saya, hingga ke hari ini, apabila penyemakan jenis dan pembayang jenis telah menjadi perkara biasa bagi banyak pembangun yang menulis dalam Python. Mekanisme ini kini disokong oleh banyak alat seperti IDE dan penganalisis kod.

β†’ Baca bahagian kedua

Mengapakah pemeriksaan jenis perlu?

Jika anda pernah menggunakan Python yang ditaip secara dinamik, anda mungkin mempunyai sedikit kekeliruan tentang mengapa terdapat kekecohan mengenai penaipan statik dan mypy kebelakangan ini. Atau mungkin anda suka Python kerana penaipan dinamiknya, dan apa yang berlaku hanya mengganggu anda. Kunci kepada nilai penaipan statik ialah skala penyelesaian: semakin besar projek anda, semakin anda condong ke arah penaipan statik, dan pada akhirnya, semakin anda benar-benar memerlukannya.

Katakan projek tertentu telah mencapai saiz puluhan ribu baris, dan ternyata beberapa pengaturcara sedang mengusahakannya. Melihat projek yang serupa, berdasarkan pengalaman kami, kami boleh mengatakan bahawa memahami kodnya akan menjadi kunci untuk memastikan pembangun produktif. Tanpa anotasi jenis, sukar untuk mengetahui, contohnya, argumen yang hendak dihantar kepada fungsi atau jenis fungsi yang boleh dikembalikan. Berikut ialah soalan biasa yang selalunya sukar dijawab tanpa menggunakan anotasi jenis:

  • Bolehkah fungsi ini kembali None?
  • Apa yang sepatutnya menjadi hujah ini? items?
  • Apakah jenis atribut id: int adakah, str, atau mungkin beberapa jenis tersuai?
  • Patutkah hujah ini menjadi senarai? Adakah mungkin untuk menghantar tupel kepadanya?

Jika anda melihat coretan kod beranotasi jenis berikut dan cuba menjawab soalan yang serupa, ternyata ini adalah tugas yang paling mudah:

class Resource:
    id: bytes
    ...
    def read_metadata(self, 
                      items: Sequence[str]) -> Dict[str, MetadataItem]:
        ...

  • read_metadata tidak kembali None, kerana jenis pulangan bukan Optional[…].
  • hujah items ialah urutan baris. Ia tidak boleh diulang secara rawak.
  • Atribut id ialah rentetan bait.

Dalam dunia yang ideal, seseorang akan menjangkakan bahawa semua kehalusan tersebut akan diterangkan dalam dokumentasi terbina dalam (docstring). Tetapi pengalaman memberikan banyak contoh fakta bahawa dokumentasi sedemikian sering tidak diperhatikan dalam kod yang anda perlu bekerja. Walaupun dokumentasi sedemikian terdapat dalam kod, seseorang tidak boleh bergantung pada ketepatan mutlaknya. Dokumentasi ini boleh menjadi kabur, tidak tepat dan terbuka kepada salah faham. Dalam pasukan besar atau projek besar, masalah ini boleh menjadi sangat meruncing.

Walaupun Python cemerlang dalam peringkat awal atau pertengahan projek, pada satu ketika projek dan syarikat yang berjaya yang menggunakan Python mungkin menghadapi soalan penting: "Adakah kita perlu menulis semula semuanya dalam bahasa yang ditaip secara statik?".

Sistem semakan taip seperti mypy menyelesaikan masalah di atas dengan menyediakan bahasa rasmi kepada pembangun untuk menerangkan jenis, dan dengan menyemak bahawa pengisytiharan jenis sepadan dengan pelaksanaan program (dan, secara pilihan, dengan menyemak kewujudan mereka). Secara umum, kita boleh mengatakan bahawa sistem ini menyediakan sesuatu seperti dokumentasi yang diperiksa dengan teliti.

Penggunaan sistem sedemikian mempunyai kelebihan lain, dan mereka sudah tidak penting:

  • Sistem semakan jenis boleh mengesan beberapa ralat kecil (dan tidak begitu kecil). Contoh biasa ialah apabila mereka terlupa untuk memproses nilai None atau beberapa syarat khas lain.
  • Pemfaktoran semula kod sangat dipermudahkan kerana sistem semakan jenis selalunya sangat tepat tentang kod yang perlu diubah. Pada masa yang sama, kita tidak perlu mengharapkan liputan kod 100% dengan ujian, yang, dalam apa jua keadaan, biasanya tidak dapat dilaksanakan. Kita tidak perlu menyelidiki kedalaman jejak tindanan untuk mengetahui punca masalah.
  • Walaupun pada projek besar, mypy selalunya boleh melakukan semakan jenis penuh dalam pecahan sesaat. Dan pelaksanaan ujian biasanya mengambil masa berpuluh-puluh saat atau bahkan minit. Sistem semakan jenis memberikan maklum balas segera pengaturcara dan membolehkannya melakukan tugasnya dengan lebih pantas. Dia tidak perlu lagi menulis ujian unit yang rapuh dan sukar untuk diselenggara yang menggantikan entiti sebenar dengan olok-olok dan tampalan hanya untuk mendapatkan keputusan ujian kod dengan lebih cepat.

IDE dan editor seperti PyCharm atau Visual Studio Code menggunakan kuasa anotasi jenis untuk menyediakan pembangun dengan pelengkapan kod, penyerlahan ralat dan sokongan untuk binaan bahasa yang biasa digunakan. Dan ini hanyalah sebahagian daripada faedah menaip. Bagi sesetengah pengaturcara, semua ini adalah hujah utama yang memihak kepada menaip. Ini adalah sesuatu yang mendapat manfaat serta-merta selepas pelaksanaan. Kes penggunaan untuk jenis ini tidak memerlukan sistem semakan jenis yang berasingan seperti mypy, walaupun perlu diperhatikan bahawa mypy membantu memastikan anotasi jenis konsisten dengan kod.

Latar belakang mypy

Sejarah mypy bermula di UK, di Cambridge, beberapa tahun sebelum saya menyertai Dropbox. Saya telah terlibat, sebagai sebahagian daripada penyelidikan kedoktoran saya, dalam penyatuan bahasa yang ditaip secara statik dan dinamik. Saya telah diilhamkan oleh artikel mengenai penaipan tambahan oleh Jeremy Siek dan Walid Taha, dan oleh projek Raket Taip. Saya cuba mencari cara untuk menggunakan bahasa pengaturcaraan yang sama untuk pelbagai projek - daripada skrip kecil kepada pangkalan kod yang terdiri daripada berjuta-juta baris. Pada masa yang sama, saya ingin memastikan bahawa dalam projek dalam sebarang skala, seseorang tidak perlu membuat kompromi yang terlalu besar. Bahagian penting dari semua ini ialah idea untuk beralih secara beransur-ansur daripada projek prototaip yang tidak ditaip kepada produk siap ditaip secara statik yang diuji secara menyeluruh. Pada hari ini, idea-idea ini sebahagian besarnya dipandang remeh, tetapi pada tahun 2010 ia merupakan masalah yang masih diterokai secara aktif.

Kerja asal saya dalam pemeriksaan jenis tidak ditujukan kepada Python. Sebaliknya, saya menggunakan bahasa "buatan sendiri" kecil Alore. Berikut ialah contoh yang akan membolehkan anda memahami perkara yang kami bincangkan (anotasi jenis adalah pilihan di sini):

def Fib(n as Int) as Int
  if n <= 1
    return n
  else
    return Fib(n - 1) + Fib(n - 2)
  end
end

Menggunakan bahasa ibunda yang dipermudahkan adalah pendekatan yang biasa digunakan dalam penyelidikan saintifik. Ini benar, bukan kurangnya kerana ia membolehkan anda menjalankan eksperimen dengan cepat, dan juga disebabkan oleh fakta bahawa apa yang tiada kaitan dengan penyelidikan boleh diabaikan dengan mudah. Bahasa pengaturcaraan dunia sebenar cenderung menjadi fenomena berskala besar dengan pelaksanaan yang kompleks, dan ini melambatkan percubaan. Walau bagaimanapun, sebarang keputusan berdasarkan bahasa yang dipermudahkan kelihatan agak mencurigakan, kerana dalam mendapatkan hasil ini penyelidik mungkin telah mengorbankan pertimbangan penting untuk penggunaan bahasa secara praktikal.

Penyemak jenis saya untuk Alore kelihatan sangat menjanjikan, tetapi saya ingin mengujinya dengan bereksperimen dengan kod sebenar, yang mungkin anda katakan, tidak ditulis dalam Alore. Nasib baik bagi saya, bahasa Alore sebahagian besarnya berdasarkan idea yang sama seperti Python. Ia cukup mudah untuk membuat semula pemeriksa taip supaya ia boleh berfungsi dengan sintaks dan semantik Python. Ini membolehkan kami mencuba semakan taip dalam kod Python sumber terbuka. Di samping itu, saya menulis transpiler untuk menukar kod yang ditulis dalam Alore kepada kod Python dan menggunakannya untuk menterjemah kod penyemak taip saya. Sekarang saya mempunyai sistem semakan jenis yang ditulis dalam Python yang menyokong subset Python, sejenis bahasa itu! (Keputusan seni bina tertentu yang masuk akal untuk Alore kurang sesuai untuk Python, dan ini masih ketara di bahagian pangkalan kod mypy.)

Malah, bahasa yang disokong oleh sistem jenis saya tidak boleh dipanggil Python pada ketika ini: ia adalah varian Python kerana beberapa batasan sintaks anotasi jenis Python 3.

Ia kelihatan seperti campuran Java dan Python:

int fib(int n):
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Salah satu idea saya pada masa itu ialah menggunakan anotasi jenis untuk meningkatkan prestasi dengan menyusun Python jenis ini kepada C, atau mungkin kod bait JVM. Saya sampai ke peringkat menulis prototaip pengkompil, tetapi saya meninggalkan idea ini, kerana pemeriksaan jenis itu sendiri kelihatan agak berguna.

Saya akhirnya membentangkan projek saya di PyCon 2013 di Santa Clara. Saya juga bercakap tentang perkara ini dengan Guido van Rossum, diktator Python yang baik hati seumur hidup. Dia meyakinkan saya untuk menggugurkan sintaks saya sendiri dan menggunakan sintaks Python 3 standard. Python 3 menyokong anotasi fungsi, jadi contoh saya boleh ditulis semula seperti yang ditunjukkan di bawah, menghasilkan program Python biasa:

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Saya terpaksa membuat beberapa kompromi (pertama sekali, saya ingin ambil perhatian bahawa saya mencipta sintaks saya sendiri atas sebab ini). Khususnya, Python 3.3, versi terbaru bahasa pada masa itu, tidak menyokong anotasi berubah-ubah. Saya berbincang dengan Guido melalui e-mel pelbagai kemungkinan untuk reka bentuk sintaksis bagi anotasi tersebut. Kami memutuskan untuk menggunakan komen jenis untuk pembolehubah. Ini memenuhi tujuan yang dimaksudkan, tetapi agak menyusahkan (Python 3.6 memberi kami sintaks yang lebih baik):

products = []  # type: List[str]  # Eww

Komen jenis juga berguna untuk menyokong Python 2, yang tidak mempunyai sokongan terbina dalam untuk anotasi jenis:

f fib(n):
    # type: (int) -> int
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Ternyata pertukaran ini (dan lain-lain) tidak begitu penting - faedah menaip statik bermakna pengguna tidak lama lagi terlupa tentang sintaks yang kurang sempurna. Memandangkan tiada binaan sintaksis khas digunakan dalam kod Python yang disemak jenis, alat Python sedia ada dan proses pemprosesan kod terus berfungsi seperti biasa, menjadikannya lebih mudah untuk pembangun mempelajari alat baharu.

Guido juga meyakinkan saya untuk menyertai Dropbox selepas saya menyelesaikan tesis siswazah saya. Di sinilah bahagian paling menarik dari cerita mypy bermula.

Perlu diteruskan ...

Pembaca yang dihormati! Jika anda menggunakan Python, sila beritahu kami tentang skala projek yang anda bangunkan dalam bahasa ini.

Laluan untuk menyemak taip 4 juta baris kod Python. Bahagian 1
Laluan untuk menyemak taip 4 juta baris kod Python. Bahagian 1

Sumber: www.habr.com

Tambah komen