CeļŔ uz 4 miljonu rindu Python koda tipu pārbaudi. 1. daļa

Šodien mēs piedāvājam jūsu uzmanībai pirmo daļu par to, kā Dropbox veic Python koda tipu kontroli.

CeļŔ uz 4 miljonu rindu Python koda tipu pārbaudi. 1. daļa

Dropbox daudz raksta Python valodā. Tā ir valoda, ko mēs izmantojam ārkārtÄ«gi plaÅ”i — gan aizmugures servisos, gan darbvirsmas klientu lietotnēs. Mēs arÄ« ievērojami lietojam Go, TypeScript un Rust, taču Python ir mÅ«su galvenā valoda. Ņemot vērā mÅ«su mērogus, jo runa ir par miljoniem rindiņu Python koda, izrādÄ«jās, ka dinamiskā tipizēŔana bÅ«tiski apgrÅ«tina tā sapraÅ”anu un sākusi nopietni ietekmēt darba produktivitāti. Lai mazinātu Å”o problēmu, mēs sākām pakāpeniski pārvērst mÅ«su kodu statiskajā tipu pārbaudē, izmantojot mypy. Tas, iespējams, ir vispopulārākā neatkarÄ«gā tipu pārbaudes sistēma Python valodai. Mypy ir atvērtā koda projekts, un tā galvenie izstrādātāji strādā Dropbox.

Dropbox ir bijusi viena no pirmajām uzņēmumām, kas ieviesusi statisku tipu pārbaudi Python kodā Ŕādā mērogā. MÅ«sdienās mypy tiek izmantots tÅ«kstoÅ”iem projektu. Å is rÄ«ks ir bez skaitāmu reizi ā€žpārbaudÄ«ts kaujÄā€. Mums, lai nokļūtu lÄ«dz Å”eit, kur mēs esam tagad, bija jāveic garÅ” ceļŔ. Å ajā ceļā bija daudz neveiksmÄ«gu mēģinājumu un izgāzuÅ”os eksperimentu. Å is materiāls stāsta par statiskās tipu pārbaudes vēsturi Python — no tās sarežģītā sākuma, kas bija daļa no maniem zinātniskajiem pētÄ«jumiem, lÄ«dz Å”odienai, kad tipu pārbaudes un tipu norādÄ«jumi ir kļuvuÅ”i ierasti neskaitāmiem izstrādātājiem, kuri raksta Python. Å ie mehānismi tagad tiek atbalstÄ«ti daudzu rÄ«ku — tādu kā IDE un koda analizatori.

→ LasÄ«t otro daļu

Kāpēc nepiecieÅ”ama tipu pārbaude?

Ja jÅ«s kādreiz esat izmantojis dinamiskās tipizācijas Python, jums var rasties neskaidrÄ«bas par to, kādēļ apkārt statiskajai tipizācijai un mypy pēdējā laikā ir radusies tik liela ažiotāža. VarbÅ«t jÅ«s tieÅ”i mÄ«lat Python tā dinamiskās tipizācijas dēļ, un viss, kas notiek, jÅ«s vienkārÅ”i sarÅ«gtina. Atslēga uz statiskās tipizācijas vērtÄ«bu ir risinājumu mērogs: jo lielāks ir jÅ«su projekts, jo spēcÄ«gāk jÅ«s tendējaties uz statisko tipizāciju, un galu galā, jo vairāk tas patieŔām ir nepiecieÅ”ams.

Pieņemsim, ka projekts ir sasniedzis desmitiem tÅ«kstoÅ”u rindu, un izrādās, ka pie tā strādā vairāki programmētāji. Izskatot lÄ«dzÄ«gu projektu, balstoties uz mÅ«su pieredzi, varam teikt, ka koda sapratne kļūs par galveno faktoru izstrādātāju produktivitātes nodroÅ”ināŔanā. Bez tipu anotācijām ir grÅ«ti noskaidrot, piemēram, kādi argumenti jānodod funkcijai vai kādu tipu vērtÄ«bas kāda funkcija var atgriezt. Å eit ir tipiski jautājumi, uz kuriem bieži vien ir grÅ«ti atbildēt, neizmantojot tipu anotācijas:

  • Vai Ŕī funkcija var atgriezt None?
  • Kas jābÅ«t Å”im argumentam items?
  • Kāds ir atribÅ«ta tips id: int vai tas ir, str, vai varbÅ«t kāds pielāgots tips?
  • Vai Å”im argumentam jābÅ«t sarakstam? Vai tajā var nodot tuple?

Ja aplÅ«ko nākamo koda fragmentu ar tipu anotācijām un mēģina atbildēt uz lÄ«dzÄ«giem jautājumiem, izrādÄ«sies, ka tas ir visvienkārŔākais uzdevums:

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

  • read_metadata neatgriež None, jo atgrieztā tipa nav Optional[…].
  • Arguments items ir virknes tipa. To nevar itera atseviŔķā secÄ«bā.
  • AtribÅ«ts id ir baitu virkne.

Ideālā pasaulē varētu cerēt, ka visas Ŕādas nianses bÅ«s aprakstÄ«tas iebÅ«vētajā dokumentācijā (docstring). Tomēr pieredze sniedz daudz piemēru tam, ka tāda dokumentācija bieži vien nav pieejama kode, ar kuru jāsaskaras. Pat ja tāda dokumentācija ir, nevar paļauties uz tās absolÅ«to pareizÄ«bu. Å Ä« dokumentācija var bÅ«t neskaidra, neprecÄ«za, radot iespējas tās nepareizai izpratnei. Lielās komandās vai lielos projektos Ŕī problēma var kļūt ārkārtÄ«gi akÅ«ta.

Lai gan Python lieliski darbojas agrÄ«nās vai vidējās projektu stadijās, noteiktā brÄ«dÄ« veiksmÄ«gi projekti un uzņēmumi, kas izmanto Python, var saskarties ar dzÄ«votspējÄ«gu jautājumu: ā€œVai mums jānodod viss uz statiski tipizētu valodu?ā€.

Veidu pārbaudes sistēmas, piemēram, mypy, risina iepriekÅ” minēto problēmu, jo tās nodroÅ”ina izstrādātājiem formālu valodu, lai aprakstÄ«tu tipus, un pārbauda, vai tipo apraksti atbilst programmas realizācijai (un, kas nav obligāti, arÄ« to pastāvēŔanu). Kopumā var teikt, ka Ŕīs sistēmas sniedz mums kaut ko lÄ«dzÄ«gu rÅ«pÄ«gi pārbaudÄ«tai dokumentācijai.

Šādu sistēmu pielietoÅ”anai ir arÄ« citi ieguvumi, kas noteikti nav triviali:

  • Veidu pārbaudes sistēma var atklāt dažas nelielas (kā arÄ« — ne tik nelielas) kļūdas. Tipisks piemērs ir tas, kad tiek aizmirsts apstrādāt vērtÄ«bu None vai kādu citu Ä«paÅ”u nosacÄ«jumu.
  • Kods ir ātrāk refaktorizējams, jo veidu pārbaudes sistēma bieži ļoti precÄ«zi norāda, kuru kodu jāmaina. Tajā paŔā laikā mums nav jāpārliecinās par 100% kodu testēŔanas pārklājumu, kas parasti ir nereāli. Mums nav jāiet dziļumos steka pēdējo ziņojumu izsekoÅ”anā, lai noskaidrotu problēmas cēloni.
  • Pat lielos projektos mypy bieži spēj veikt pilnÄ«gu tipa pārbaudi dažu sekunžu laikā. Savukārt testu izpilde parasti aizņem desmitiem sekunžu vai pat minÅ«tes. Tipu pārbaudes sistēma nodroÅ”ina programmētājam tÅ«lÄ«tēju atsauksmi un ļauj ātrāk paveikt darbu. Viņam vairs nav jāraksta trausli un grÅ«ti uzturami moduļu testi, kas aizvieto reālās entitātes ar moku un plecu palÄ«giem tikai tādēļ, lai ātrāk iegÅ«tu kodēŔanas testu rezultātus.

IDE un redaktori, piemēram, PyCharm vai Visual Studio Code, izmanto tipu anotāciju iespējas, lai nodroÅ”inātu izstrādātājiem automātiskās kodēŔanas pabeigÅ”anas funkcijas, kļūdu izcelÅ”anu un atbalstu bieži izmantotām valodas konstrukcijām. Un tas ir tikai daži no ieguvumiem, ko sniedz tipizācija. Dažiem programmētājiem tas viss ir galvenais arguments par labu tipizācijai. Tas rada labumu tÅ«lÄ«t pēc ievieÅ”anas. Å is veids, kā izmantot tipus, neprasa atseviŔķas tipu pārbaudes sistēmas, piemēram, mypy, kaut gan jāatzÄ«mē, ka mypy palÄ«dz uzturēt atbilstÄ«bu tipu anotācijām un kodam.

mypy priekÅ”vēsture

mypy stāsts sākās Apvienotajā Karalistē, Kembridžā, dažus gadus pirms es pievienojos Dropbox. Es veicu doktorantÅ«ras pētÄ«jumu par statiski tipizētu un dinamisku valodu apvienoÅ”anu. Mani iedvesmoja Džeremija SÄ«eka un ValÄ«da Taha raksts par pakāpenisku tipizāciju, kā arÄ« projekts Typed Racket. Es mēģināju atrast veidus, kā izmantot vienu un to paÅ”u programmēŔanas valodu dažādiem projektiem — no maziem skriptiem lÄ«dz kodu bāzēm, kuras sastāv no vairākām miljoniem rindiņu. Man bija svarÄ«gi, lai jebkura mēroga projektā nebÅ«tu jāveic pārāk lieli kompromisi. SvarÄ«ga Ŕī procesa daļa bija ideja par pakāpenisku pāreju no netipizēta prototipa uz visaptveroÅ”i testētu statiski tipizētu gatavu produktu. MÅ«sdienās Ŕīs idejas lielā mērā tiek uzskatÄ«tas par paÅ”saprotamām, taču 2010. gadā tā bija problēma, kas vēl joprojām tika aktÄ«vi pēta.

Mana sākotnējā darba joma tipa pārbaudēs nebija vērsta uz Python. Tā vietā es izmantoju mazu 'paÅ”darinātu' valodu AloreÅ is ir piemērs, kas ļaus jums saprast, par ko ir runa (tipu anotācijas Å”eit nav obligātas):

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

VienkārÅ”a valoda, kas izstrādāta paÅ”gatavotam projektam, ir parasta pieeja, ko izmanto zinātniskajos pētÄ«jumos. Tas tā ir, ne mazākā mērā, jo tas ļauj ātri veikt eksperimentus, kā arÄ« to, kas nav saistÄ«ts ar pētÄ«jumu, var brÄ«vi ignorēt. Reāli lietotie programmēŔanas valodas parasti ir milzÄ«gas parādÄ«bas ar sarežģītām realizācijām, un tas palēnina eksperimentus. Tomēr jebkuri rezultāti, kas balstÄ«ti uz vienkārÅ”otu valodu, Ŕķiet nedaudz aizdomÄ«gi, jo, iegÅ«stot Å”os rezultātus, pētnieks varbÅ«t ir upurējis apsvērumus, kas ir svarÄ«gi valodu praktiskai izmantoÅ”anai.

Mans tipu pārbaudes rÄ«ks Alore izskatÄ«jās ļoti solÄ«gs, taču es vēlējos to pārbaudÄ«t, veicot eksperimentus ar reālu kodu, kura, teiksim, Alore nebija. Par laimi, valoda Alore lielā mērā balstÄ«jās uz tām paŔām idejām kā Python. Bija vienkārÅ”i pārveidot tipu pārbaudes rÄ«ku, lai tas varētu strādāt ar Python sintaksi un semantiku. Tas ļāva izmēģināt tipu pārbaudi atvērtā koda Python kodā. Turklāt es uzrakstÄ«ju transpaileri, lai pārvērstu kodu, kas uzrakstÄ«ts Alore, Python kodā, un izmantoju to, lai pārveidotu mana tipu pārbaudes rÄ«ka kodu. Tagad man bija tipu pārbaudes sistēma, kas rakstÄ«ta Python, kura atbalstÄ«ja Python apakÅ”kopu, noteiktu Ŕīs valodas paveidu! (Dažas arhitektÅ«ras izvēles, kas bija jēgpilnas Alore, sliktāk derēja Python, tas joprojām ir redzams dažās mypy koda bāzes daļās.)

PatiesÄ«bā, valoda, ko atbalstÄ«ja mana tipu sistēma, Å”ajā brÄ«dÄ« ne gluži varēja saukt par Python: tā bija Python variācija, kas radusies no dažām Python 3 tipu anotāciju sintakses ierobežojumiem.

Tā izskatījās kā Java un Python sajaukums:

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

Viens no manām idejām tajā laikā bija izmantot tipu anotācijas, lai uzlabotu veiktspēju, kompilējot Å”o Python paveidu C vai iespējams, JVM bytecode. Es nonācu lÄ«dz prototipa kompilatora rakstīŔanai, bet atstāju Å”o ieceri, jo tipa pārbaude pati par sevi izskatÄ«jās pietiekami noderÄ«ga.

Galu galā es prezentēju savu projektu konferencē PyCon 2013 Sanhojā. Es arÄ« par to parunāju ar Guido van Rossum, žēlastÄ«bas dzÄ«ves diktatoru Python. ViņŔ pārliecināja mani atteikties no sava sintaksiska un pieturēties pie standarta Python 3 sintakses. Python 3 atbalsta funkciju anotācijas, lÄ«dz ar to manu piemēru varēja pārformatēt tā, kā norādÄ«ts zemāk, iegÅ«stot parastu Python programmu:

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

Man vajadzēja piekrist daudziem kompromisiem (pirmkārt, gribu atzÄ«mēt, ka tieÅ”i tāpēc es izgudroju savu sintaksi). Proti, Python 3.3, tajā laikā jaunākā valodas versija, neatbalstÄ«ja mainÄ«go anotācijas. Es pa e-pastu apspriedu ar Guido dažādas Ŕādu anotāciju sintaktiskās noformēŔanas iespējas. Mēs izlēmām izmantot komentārus ar tipu norādēm, lai sasniegtu iecerēto mērÄ·i, bet tas izskatÄ«jās nedaudz neveikli (Python 3.6 mums sniedza patÄ«kamāku sintaksi):

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

Tipa komentāri, turklāt, noderēja, atbalstot Python 2, kurā nav iebūvēta tipa anotāciju atbalsta:

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

IzrādÄ«jās, ka Å”ie (un citi) kompromisi patiesÄ«bā nebija tik bÅ«tiski — statiskās tipizācijas priekÅ”rocÄ«bas noveda pie tā, ka lietotāji drÄ«z vien aizmirsÄ«s par nepilnÄ«go sintaksi. Tā kā tagad Python kodā, kurā tika kontrolēti tipi, netika izmantotas Ä«paÅ”as sintaktiskās konstrukcijas, esoÅ”ie Python rÄ«ki un procesi, kas apstrādā kodu, turpināja darboties normāli, kas ievērojami atviegloja izstrādātāju jaunā rÄ«ka apgūŔanu.

Guido, turklāt, pārliecināja mani pievienoties Dropbox pēc tam, kad es aizstāvēju savu diplomdarbu. Te sākas viss interesantākais mypy stāstā.

Turpinājums sekos…

CienÄ«jamie lasÄ«tāji! Ja, ja izmantojat Python, lÅ«dzu, pastāstiet, kādu projektu mērogu jÅ«s izstrādājat Å”ajā valodā.

CeļŔ uz 4 miljonu rindu Python koda tipu pārbaudi. 1. daļa
CeļŔ uz 4 miljonu rindu Python koda tipu pārbaudi. 1. daļa

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster