Jalur untuk memeriksa 4 juta baris kode Python. Bagian 1

Hari ini kami menyampaikan kepada Anda bagian pertama dari terjemahan materi tentang bagaimana Dropbox berurusan dengan kontrol tipe kode Python.

Jalur untuk memeriksa 4 juta baris kode Python. Bagian 1

Dropbox banyak menulis dengan Python. Ini adalah bahasa yang kami gunakan secara luas, baik untuk layanan back-end maupun aplikasi klien desktop. Kami juga sering menggunakan Go, TypeScript, dan Rust, tetapi Python adalah bahasa utama kami. Mempertimbangkan skala kami, dan kami berbicara tentang jutaan baris kode Python, ternyata pengetikan dinamis dari kode semacam itu tidak perlu memperumit pemahamannya dan mulai berdampak serius pada produktivitas tenaga kerja. Untuk mengatasi masalah ini, kami telah mulai secara bertahap mentransisikan kode kami ke pemeriksaan tipe statis menggunakan mypy. Ini mungkin sistem pengecekan tipe mandiri yang paling populer untuk Python. Mypy adalah proyek sumber terbuka, pengembang utamanya bekerja di Dropbox.

Dropbox adalah salah satu perusahaan pertama yang menerapkan pemeriksaan tipe statis dalam kode Python pada skala ini. Mypy digunakan dalam ribuan proyek saat ini. Alat ini berkali-kali, seperti yang mereka katakan, "diuji dalam pertempuran." Kami telah menempuh perjalanan panjang untuk sampai ke tempat kami sekarang. Sepanjang jalan, ada banyak usaha yang gagal dan eksperimen yang gagal. Posting ini mencakup sejarah pemeriksaan tipe statis dengan Python, dari awal yang sulit sebagai bagian dari proyek penelitian saya, hingga hari ini, ketika pemeriksaan tipe dan petunjuk tipe telah menjadi hal biasa bagi banyak pengembang yang menulis dengan Python. Mekanisme ini sekarang didukung oleh banyak alat seperti IDE dan penganalisa kode.

β†’ Baca bagian kedua

Mengapa pemeriksaan tipe diperlukan?

Jika Anda pernah menggunakan Python yang diketik secara dinamis, Anda mungkin bingung mengapa akhir-akhir ini ada keributan seputar pengetikan statis dan mypy. Atau mungkin Anda menyukai Python justru karena pengetikannya yang dinamis, dan apa yang terjadi membuat Anda kesal. Kunci dari nilai pengetikan statis adalah skala solusinya: semakin besar proyek Anda, semakin Anda condong ke pengetikan statis, dan pada akhirnya, semakin Anda benar-benar membutuhkannya.

Misalkan sebuah proyek tertentu telah mencapai ukuran puluhan ribu baris, dan ternyata beberapa programmer sedang mengerjakannya. Melihat proyek serupa, berdasarkan pengalaman kami, kami dapat mengatakan bahwa memahami kodenya akan menjadi kunci agar pengembang tetap produktif. Tanpa anotasi tipe, akan sulit untuk mencari tahu, misalnya, argumen apa yang akan diteruskan ke suatu fungsi, atau tipe apa yang dapat dikembalikan oleh suatu fungsi. Berikut adalah pertanyaan tipikal yang seringkali sulit dijawab tanpa menggunakan anotasi jenis:

  • Bisakah fungsi ini kembali None?
  • Apa yang seharusnya menjadi argumen ini? items?
  • Apa jenis atributnya id: int Apakah itu, str, atau mungkin jenis khusus?
  • Haruskah argumen ini berupa daftar? Apakah mungkin untuk meneruskan tuple ke sana?

Jika Anda melihat cuplikan kode beranotasi jenis berikut dan mencoba menjawab pertanyaan serupa, ternyata ini adalah tugas yang paling sederhana:

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

  • read_metadata tidak kembali None, karena tipe pengembaliannya bukan Optional[…].
  • Argumen items adalah rangkaian baris. Itu tidak dapat diulang secara acak.
  • Atribut id adalah string byte.

Di dunia yang ideal, orang akan berharap bahwa semua seluk-beluk seperti itu akan dijelaskan dalam dokumentasi bawaan (docstring). Tetapi pengalaman memberikan banyak contoh fakta bahwa dokumentasi semacam itu sering tidak diamati dalam kode yang harus Anda gunakan untuk bekerja. Sekalipun dokumentasi semacam itu ada dalam kode, seseorang tidak dapat mengandalkan kebenaran mutlaknya. Dokumentasi ini bisa tidak jelas, tidak akurat, dan rentan terhadap kesalahpahaman. Dalam tim besar atau proyek besar, masalah ini bisa menjadi sangat akut.

Sementara Python unggul dalam proyek tahap awal atau menengah, di beberapa titik proyek yang berhasil dan perusahaan yang menggunakan Python mungkin menghadapi pertanyaan penting: "Haruskah kita menulis ulang semuanya dalam bahasa yang diketik secara statis?".

Sistem pengecekan tipe seperti mypy memecahkan masalah di atas dengan menyediakan pengembang dengan bahasa formal untuk mendeskripsikan tipe, dan dengan memeriksa bahwa deklarasi tipe cocok dengan implementasi program (dan, secara opsional, dengan memeriksa keberadaannya). Secara umum, kami dapat mengatakan bahwa sistem ini menyediakan sesuatu seperti dokumentasi yang diperiksa dengan cermat.

Penggunaan sistem semacam itu memiliki kelebihan lain, dan sudah sepenuhnya non-sepele:

  • Sistem pengecekan tipe dapat mendeteksi beberapa kesalahan kecil (dan tidak terlalu kecil). Contoh tipikal adalah ketika mereka lupa memproses suatu nilai None atau kondisi khusus lainnya.
  • Pemfaktoran ulang kode sangat disederhanakan karena sistem pengecekan tipe seringkali sangat akurat tentang kode apa yang perlu diubah. Pada saat yang sama, kami tidak perlu berharap untuk cakupan kode 100% dengan pengujian, yang, bagaimanapun, biasanya tidak dapat dilakukan. Kami tidak perlu mempelajari lebih dalam tentang pelacakan tumpukan untuk mengetahui penyebab masalahnya.
  • Bahkan pada proyek besar, mypy sering dapat melakukan pengecekan tipe lengkap dalam sepersekian detik. Dan pelaksanaan tes biasanya memakan waktu puluhan detik atau bahkan menit. Sistem pengecekan tipe memberi programmer umpan balik instan dan memungkinkannya melakukan pekerjaannya lebih cepat. Dia tidak perlu lagi menulis pengujian unit yang rapuh dan sulit dipertahankan yang menggantikan entitas nyata dengan tiruan dan tambalan hanya untuk mendapatkan hasil pengujian kode lebih cepat.

IDE dan editor seperti PyCharm atau Visual Studio Code menggunakan kekuatan anotasi tipe untuk memberi pengembang penyelesaian kode, penyorotan kesalahan, dan dukungan untuk konstruksi bahasa yang umum digunakan. Dan ini hanya beberapa manfaat dari mengetik. Untuk beberapa programmer, semua ini adalah argumen utama yang mendukung pengetikan. Ini adalah sesuatu yang bermanfaat segera setelah implementasi. Kasus penggunaan untuk tipe ini tidak memerlukan sistem pemeriksaan tipe terpisah seperti mypy, meskipun perlu dicatat bahwa mypy membantu menjaga anotasi tipe tetap konsisten dengan kode.

Latar belakang mypy

Sejarah mypy dimulai di Inggris, di Cambridge, beberapa tahun sebelum saya bergabung dengan Dropbox. Saya telah terlibat, sebagai bagian dari penelitian doktoral saya, dalam penyatuan bahasa yang diketik secara statis dan dinamis. Saya terinspirasi oleh sebuah artikel tentang pengetikan inkremental oleh Jeremy Siek dan Walid Taha, dan oleh proyek Typed Racket. Saya mencoba menemukan cara untuk menggunakan bahasa pemrograman yang sama untuk berbagai proyek - dari skrip kecil hingga basis kode yang terdiri dari jutaan baris. Pada saat yang sama, saya ingin memastikan bahwa dalam proyek dalam skala apa pun, seseorang tidak perlu membuat kompromi yang terlalu besar. Bagian penting dari semua ini adalah gagasan untuk secara bertahap beralih dari proyek prototipe yang tidak diketik ke produk jadi yang diketik secara statis yang diuji secara komprehensif. Hari-hari ini, ide-ide ini sebagian besar diterima begitu saja, tetapi pada tahun 2010 ini adalah masalah yang masih dieksplorasi secara aktif.

Pekerjaan asli saya dalam pengecekan tipe tidak ditujukan untuk Python. Sebagai gantinya, saya menggunakan bahasa "buatan sendiri" kecil alore. Berikut adalah contoh yang akan membuat Anda memahami apa yang sedang kita bicarakan (anotasi jenis bersifat opsional 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 asli yang disederhanakan adalah pendekatan umum yang digunakan dalam penelitian ilmiah. Hal ini paling tidak karena memungkinkan Anda melakukan eksperimen dengan cepat, dan juga karena fakta bahwa apa yang tidak ada hubungannya dengan penelitian dapat dengan mudah diabaikan. Bahasa pemrograman dunia nyata cenderung menjadi fenomena berskala besar dengan implementasi yang kompleks, dan ini memperlambat eksperimen. Namun, hasil apa pun berdasarkan bahasa yang disederhanakan terlihat sedikit mencurigakan, karena dalam memperoleh hasil ini, peneliti mungkin telah mengorbankan pertimbangan penting untuk penggunaan praktis bahasa.

Pemeriksa tipe saya untuk Alore tampak sangat menjanjikan, tetapi saya ingin mengujinya dengan bereksperimen dengan kode asli, yang, bisa dibilang, tidak ditulis di Alore. Beruntung bagi saya, bahasa Alore sebagian besar didasarkan pada ide yang sama dengan Python. Cukup mudah untuk membuat ulang pemeriksa huruf sehingga bisa bekerja dengan sintaks dan semantik Python. Ini memungkinkan kami untuk mencoba mengetik memeriksa kode Python open source. Selain itu, saya menulis sebuah transpiler untuk mengonversi kode yang ditulis dalam Alore ke kode Python dan menggunakannya untuk menerjemahkan kode pengetik huruf saya. Sekarang saya memiliki sistem pengecekan tipe yang ditulis dengan Python yang mendukung subset dari Python, semacam bahasa itu! (Keputusan arsitektur tertentu yang masuk akal untuk Alore kurang cocok untuk Python, dan ini masih terlihat di bagian basis kode mypy.)

Faktanya, bahasa yang didukung oleh sistem tipe saya tidak dapat disebut Python pada saat ini: itu adalah varian dari Python karena beberapa keterbatasan sintaks anotasi tipe Python 3.

Itu tampak seperti campuran Java dan Python:

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

Salah satu ide saya saat itu adalah menggunakan anotasi tipe untuk meningkatkan kinerja dengan mengkompilasi jenis Python ke C, atau mungkin bytecode JVM. Saya sampai pada tahap menulis prototipe kompiler, tetapi saya mengabaikan ide ini, karena pemeriksaan tipe itu sendiri terlihat cukup berguna.

Saya akhirnya mempresentasikan proyek saya di PyCon 2013 di Santa Clara. Saya juga membicarakan hal ini dengan Guido van Rossum, diktator Python yang baik hati seumur hidup. Dia meyakinkan saya untuk membuang sintaks saya sendiri dan tetap menggunakan sintaks standar Python 3. Python 3 mendukung anotasi fungsi, jadi contoh saya dapat ditulis ulang seperti yang ditunjukkan di bawah ini, menghasilkan program Python normal:

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

Saya harus membuat beberapa kompromi (pertama-tama, saya ingin mencatat bahwa saya menemukan sintaks saya sendiri karena alasan ini). Secara khusus, Python 3.3, versi bahasa terbaru saat itu, tidak mendukung anotasi variabel. Saya berdiskusi dengan Guido melalui email berbagai kemungkinan untuk desain sintaksis dari anotasi semacam itu. Kami memutuskan untuk menggunakan komentar tipe untuk variabel. Ini melayani tujuan yang dimaksud, tetapi agak rumit (Python 3.6 memberi kami sintaks yang lebih bagus):

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

Ketik komentar juga berguna untuk mendukung Python 2, yang tidak memiliki dukungan bawaan 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 lainnya) tidak terlalu penting - manfaat dari pengetikan statis berarti bahwa pengguna segera melupakan sintaks yang kurang sempurna. Karena tidak ada konstruksi sintaksis khusus yang digunakan dalam kode Python yang diperiksa jenisnya, alat Python yang ada dan proses pemrosesan kode terus bekerja secara normal, membuatnya lebih mudah bagi pengembang untuk mempelajari alat baru tersebut.

Guido juga meyakinkan saya untuk bergabung dengan Dropbox setelah saya menyelesaikan tesis pascasarjana saya. Di sinilah bagian paling menarik dari cerita mypy dimulai.

Untuk dilanjutkan ...

Pembaca yang terhormat Jika Anda menggunakan Python, beri tahu kami tentang skala proyek yang Anda kembangkan dalam bahasa ini.

Jalur untuk memeriksa 4 juta baris kode Python. Bagian 1
Jalur untuk memeriksa 4 juta baris kode Python. Bagian 1

Sumber: www.habr.com

Tambah komentar