4 milyon satırlık Python kodunu kontrol etmenin yolu. Bölüm 1

Bugün, Dropbox'ın Python kodunun tip kontrolünü nasıl ele aldığına ilişkin materyalin çevirisinin ilk bölümünü dikkatinize sunuyoruz.

4 milyon satırlık Python kodunu kontrol etmenin yolu. Bölüm 1

Dropbox, Python'da çok şey yazar. Hem arka uç hizmetleri hem de masaüstü istemci uygulamaları için son derece yaygın olarak kullandığımız bir dildir. Go, TypeScript ve Rust'u da çok kullanıyoruz ama Python bizim ana dilimiz. Ölçeğimiz göz önüne alındığında ve milyonlarca satırlık Python kodundan bahsediyoruz, bu tür bir kodun dinamik olarak yazılmasının, anlaşılmasını gereksiz yere karmaşıklaştırdığı ve işgücü verimliliğini ciddi şekilde etkilemeye başladığı ortaya çıktı. Bu sorunu azaltmak için, mypy kullanarak kodumuzu kademeli olarak statik tip kontrolüne geçirmeye başladık. Bu muhtemelen Python için en popüler bağımsız tip kontrol sistemidir. Mypy açık kaynaklı bir projedir, ana geliştiricileri Dropbox'ta çalışır.

Dropbox, bu ölçekte Python kodunda statik tip denetimi uygulayan ilk şirketlerden biriydi. Mypy bugünlerde binlerce projede kullanılıyor. Bu araç, dedikleri gibi, sayısız kez "savaşta test edildi". Şu an bulunduğumuz yere gelmek için uzun bir yol kat ettik. Yol boyunca birçok başarısız girişim ve başarısız deney vardı. Bu yazı, araştırma projemin bir parçası olarak zorlu başlangıcından, Python'da yazan sayısız geliştirici için tip kontrolü ve tip ipucu vermenin sıradan hale geldiği günümüze kadar, Python'da statik tip kontrolünün tarihini kapsar. Bu mekanizmalar artık IDE'ler ve kod analizörleri gibi birçok araç tarafından desteklenmektedir.

ikinci bölümü oku

Tip kontrolü neden gereklidir?

Dinamik olarak yazılan Python'u daha önce kullandıysanız, son zamanlarda statik yazma ve mypy çevresinde neden bu kadar yaygara olduğu konusunda kafa karışıklığı yaşayabilirsiniz. Ya da belki Python'u tam olarak dinamik yazma özelliğinden dolayı seviyorsunuz ve olup bitenler sizi üzüyor. Statik yazmanın değerinin anahtarı, çözümlerin ölçeğidir: projeniz ne kadar büyükse, statik yazmaya o kadar çok yönelirsiniz ve sonunda, buna gerçekten daha çok ihtiyacınız olur.

Belirli bir projenin on binlerce satırlık bir boyuta ulaştığını ve birkaç programcının üzerinde çalıştığını varsayalım. Deneyimimize dayanarak benzer bir projeye baktığımızda, kodunu anlamanın geliştiricileri üretken tutmanın anahtarı olacağını söyleyebiliriz. Tip ek açıklamaları olmadan, örneğin bir fonksiyona hangi argümanların iletileceğini veya bir fonksiyonun hangi tiplerin dönebileceğini anlamak zor olabilir. Tip ek açıklamaları kullanmadan yanıtlanması genellikle zor olan tipik sorular şunlardır:

  • Bu işlev geri dönebilir mi? None?
  • Bu argüman ne olmalı? items?
  • öznitelik türü nedir id: int bu mu, strveya belki bazı özel türler?
  • Bu argüman bir liste olmalı mı? Ona bir demet geçirmek mümkün mü?

Aşağıdaki tür açıklamalı kod parçacığına bakıp benzer soruları yanıtlamaya çalışırsanız, bunun en basit görev olduğu ortaya çıkıyor:

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

  • read_metadata geri dönmüyor Nonedönüş türü olmadığı için Optional[…].
  • tartışma items bir dizi satırdır. Rastgele yinelenemez.
  • Öznitelik id bir bayt dizisidir.

İdeal bir dünyada, tüm bu tür inceliklerin yerleşik belgelerde (docstring) açıklanması beklenir. Ancak deneyim, bu tür belgelerin çalışmak zorunda olduğunuz kodda genellikle gözlenmediğine dair birçok örnek verir. Kodda bu tür belgeler bulunsa bile, onun mutlak doğruluğuna güvenilemez. Bu belgeler belirsiz, hatalı ve yanlış anlaşılmalara açık olabilir. Büyük ekiplerde veya büyük projelerde bu sorun son derece şiddetli hale gelebilir.

Python, projelerin erken veya orta aşamalarında mükemmel olsa da, bir noktada Python kullanan başarılı projeler ve şirketler şu hayati soruyla karşılaşabilir: "Her şeyi statik olarak yazılan bir dilde yeniden yazmalı mıyız?".

Mypy gibi tip kontrol sistemleri, geliştiriciye türleri tanımlamak için resmi bir dil sağlayarak ve tür bildirimlerinin program uygulamasıyla eşleşip eşleşmediğini kontrol ederek (ve isteğe bağlı olarak varlıklarını kontrol ederek) yukarıdaki sorunu çözer. Genel olarak, bu sistemlerin dikkatle kontrol edilmiş belgeler gibi bir şeyi hizmetimize sunduğunu söyleyebiliriz.

Bu tür sistemlerin kullanılmasının başka avantajları vardır ve bunlar zaten tamamen önemsiz değildir:

  • Tip kontrol sistemi bazı küçük (ve çok da küçük olmayan) hataları tespit edebilir. Tipik bir örnek, bir değeri işlemeyi unuttukları zamandır. None veya başka bir özel durum.
  • Tip kontrol sistemi genellikle hangi kodun değiştirilmesi gerektiği konusunda çok doğru olduğundan, kodun yeniden düzenlenmesi büyük ölçüde basitleştirilmiştir. Aynı zamanda, her durumda genellikle mümkün olmayan testlerle %100 kod kapsamını ummamıza gerek yok. Sorunun nedenini bulmak için yığın izinin derinliklerine inmemize gerek yok.
  • Büyük projelerde bile, mypy çoğu zaman tam tip denetimini saniyeden kısa bir sürede yapabilir. Ve testlerin yürütülmesi genellikle onlarca saniye hatta dakikalar alır. Tip kontrol sistemi, programcıya anında geri bildirim verir ve işini daha hızlı yapmasını sağlar. Artık, yalnızca kod testi sonuçlarını daha hızlı almak için gerçek varlıkları taklitler ve yamalarla değiştiren kırılgan ve bakımı zor birim testleri yazması gerekmiyor.

PyCharm veya Visual Studio Code gibi IDE'ler ve düzenleyiciler, geliştiricilere kod tamamlama, hata vurgulama ve yaygın olarak kullanılan dil yapıları için destek sağlamak için tür açıklamalarının gücünü kullanır. Ve bunlar yazmanın faydalarından sadece birkaçı. Bazı programcılar için tüm bunlar, yazmaktan yana olan ana argümandır. Bu, uygulamadan hemen sonra fayda sağlayan bir şeydir. Türler için bu kullanım durumu, mypy gibi ayrı bir tür kontrol sistemi gerektirmez, ancak mypy'nin tür ek açıklamalarını kodla tutarlı tutmaya yardımcı olduğuna dikkat edilmelidir.

mypy'nin arka planı

Mypy'nin tarihi, ben Dropbox'a katılmadan birkaç yıl önce Birleşik Krallık'ta, Cambridge'de başladı. Doktora araştırmamın bir parçası olarak, statik olarak yazılan ve dinamik dillerin birleştirilmesiyle ilgilendim. Jeremy Siek ve Walid Taha'nın artımlı yazımla ilgili bir makalesinden ve Typed Racket projesinden ilham aldım. Küçük betiklerden milyonlarca satırdan oluşan kod tabanlarına kadar çeşitli projeler için aynı programlama dilini kullanmanın yollarını bulmaya çalıştım. Aynı zamanda, herhangi bir ölçekte bir projede çok büyük tavizler vermek zorunda kalmamayı sağlamak istedim. Tüm bunların önemli bir kısmı, kademeli olarak tiplendirilmemiş bir prototip projeden kapsamlı bir şekilde test edilmiş, statik olarak tiplendirilmiş bir bitmiş ürüne geçme fikriydi. Bu günlerde, bu fikirler büyük ölçüde doğal karşılanıyor, ancak 2010'da hala aktif olarak araştırılan bir sorundu.

Tip kontrolündeki orijinal çalışmam Python'u hedef almıyordu. Bunun yerine küçük bir "ev yapımı" dil kullandım Alor. İşte neden bahsettiğimizi anlamanıza yardımcı olacak bir örnek (burada tür ek açıklamaları isteğe bağlıdır):

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

Basitleştirilmiş bir ana dili kullanmak, bilimsel araştırmalarda kullanılan yaygın bir yaklaşımdır. Bu böyledir, hem hızlı bir şekilde deneyler yapmanıza izin verdiği için hem de çalışma ile hiçbir ilgisi olmayan şeylerin kolayca göz ardı edilebileceği gerçeği nedeniyle. Gerçek dünya programlama dilleri, karmaşık uygulamalarla büyük ölçekli fenomenler olma eğilimindedir ve bu, deneyleri yavaşlatır. Bununla birlikte, araştırmacı bu sonuçları elde ederken dillerin pratik kullanımı için önemli hususları feda etmiş olabileceğinden, basitleştirilmiş bir dile dayalı herhangi bir sonuç biraz şüpheli görünmektedir.

Alore için tip denetleyicim çok umut verici görünüyordu, ancak Alore'da yazılmamış diyebileceğiniz gerçek kodla deneyler yaparak test etmek istedim. Neyse ki, Alore dili büyük ölçüde Python ile aynı fikirlere dayanıyordu. Daktilo denetleyicisini Python'un sözdizimi ve anlambilimiyle çalışabilecek şekilde yeniden yapmak yeterince kolaydı. Bu, açık kaynak Python kodunda tür denetimi yapmayı denememize olanak sağladı. Ek olarak, Alore'da yazılan kodu Python koduna dönüştürmek için bir aktarıcı yazdım ve onu daktilo kodumu çevirmek için kullandım. Şimdi Python'da yazılmış, Python'un bir alt kümesini, o dilin bir türünü destekleyen bir tip kontrol sistemim vardı! (Alore için mantıklı olan bazı mimari kararlar Python için pek uygun değildi ve bu, mypy kod tabanının bazı kısımlarında hala fark ediliyor.)

Aslında, yazım sistemim tarafından desteklenen dile bu noktada tam olarak Python denilemezdi: Python 3 tipi ek açıklama söz dizimindeki bazı sınırlamalar nedeniyle Python'un bir çeşidiydi.

Java ve Python karışımı gibi görünüyordu:

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

O zamanki fikirlerimden biri, bu tür Python'u C'ye veya belki de JVM bayt kodunu derleyerek performansı artırmak için tür ek açıklamaları kullanmaktı. Bir derleyici prototipi yazma aşamasına geldim, ancak tip denetiminin kendisi oldukça yararlı göründüğü için bu fikirden vazgeçtim.

Projemi Santa Clara'daki PyCon 2013'te sunmaya karar verdim. Python'un ömür boyu iyiliksever diktatörü Guido van Rossum ile de bu konuyu konuştum. Beni kendi sözdizimimi bırakmaya ve standart Python 3 sözdizimine bağlı kalmaya ikna etti.Python 3 işlev ek açıklamalarını destekler, bu nedenle örneğim aşağıda gösterildiği gibi yeniden yazılabilir ve sonuç olarak normal bir Python programı elde edilebilir:

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

Bazı tavizler vermek zorunda kaldım (her şeyden önce, tam da bu nedenle kendi sözdizimimi icat ettiğimi belirtmek isterim). Özellikle, o sırada dilin en son sürümü olan Python 3.3, değişken açıklamaları desteklemiyordu. Guido ile e-posta yoluyla bu tür ek açıklamaların sözdizimsel tasarımı için çeşitli olasılıkları tartıştım. Değişkenler için tip yorumları kullanmaya karar verdik. Bu amaçlanan amaca hizmet etti, ancak biraz hantaldı (Python 3.6 bize daha güzel bir sözdizimi verdi):

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

Tip açıklamaları, tip ek açıklamaları için yerleşik desteğe sahip olmayan Python 2'yi desteklemek için de kullanışlı oldu:

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

Bu (ve diğer) değiş tokuşların gerçekten önemli olmadığı ortaya çıktı - statik yazmanın faydaları, kullanıcıların ideal olmayan sözdizimini kısa sürede unutmaları anlamına geliyordu. Tip kontrolü yapılmış Python kodunda hiçbir özel sözdizimsel yapı kullanılmadığından, mevcut Python araçları ve kod işleme süreçleri normal şekilde çalışmaya devam ederek geliştiricilerin yeni aracı öğrenmesini çok daha kolaylaştırdı.

Guido ayrıca yüksek lisans tezimi tamamladıktan sonra beni Dropbox'a katılmaya ikna etti. Mypy hikayesinin en ilginç kısmı burada başlıyor.

Devam edecek ...

Sevgili okuyucular! Python kullanıyorsanız, lütfen bize bu dilde geliştirdiğiniz projelerin ölçeğinden bahsedin.

4 milyon satırlık Python kodunu kontrol etmenin yolu. Bölüm 1
4 milyon satırlık Python kodunu kontrol etmenin yolu. Bölüm 1

Kaynak: habr.com

Yorum ekle