{"id":38479,"date":"2019-10-31T22:23:56","date_gmt":"2019-10-31T19:23:56","guid":{"rendered":"https:\/\/prohoster.info\/blog\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\/"},"modified":"2019-10-31T22:23:56","modified_gmt":"2019-10-31T19:23:56","slug":"put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","status":"publish","type":"post","link":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Pot do preverjanja tipov 4 milijonov vrstic Python kode. Del 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Danas objavljujemo drugi deo prevoda materijala o tome kako je Dropbox organizovao kontrolu tipova nekoliko miliona redova Python koda.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Pot do preverjanja tipov 4 milijonov vrstic Python kode. Del 2\" src=\"\/wp-content\/uploads\/2019\/09\/3f8f396d724153a0374a3fadca190d55.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>\u2192 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468233\/\">Pro\u010ditajte prvi deo<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Zvani\u010dna podr\u0161ka tipovima (PEP 484)<\/h2>\n<p>\nSproveli smo prve ozbiljne eksperimente sa mypy u Dropbox-u tokom Hack Week-a 2014. Hack Week je doga\u0111aj koji Dropbox organizuje tokom jedne nedelje. Tokom tog vremena, zaposleni mogu raditi na \u010demu god \u017eele! Neki od najpoznatijih tehnolo\u0161kih projekata Dropbox-a zapo\u010deli su upravo na sli\u010dnim doga\u0111ajima. Kao rezultat ovog eksperimenta, zaklju\u010dili smo da mypy izgleda obe\u0107avaju\u0107e, iako taj projekat jo\u0161 uvek nije bio spreman za \u0161iroku upotrebu.<\/p>\n<p>Tada je u vazduhu bila ideja o standardizaciji sistema za davanje tipova Python-a. Kao \u0161to sam ve\u0107 spomenuo, od Python-a 3.0 moglo je da se koristi anotacije tipova za funkcije, ali to su bile samo proizvoljne izraze, bez definisanog sintakti\u010dkog i semanti\u010dkog zna\u010denja. Tokom izvr\u0161enja programa, te anotacije su uglavnom jednostavno ignorisane. Nakon Hack Week-a, po\u010deli smo raditi na standardizaciji semantike. Ovaj rad doveo je do pojave <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (na ovom dokumentu radili su Guido van Rossum, Luk\u00e1\u0161 Lange i ja).<\/p>\n<p>Na\u0161e motive je mogu\u0107e posmatrati iz dva ugla. Prvo, nadali smo se da \u0107e cela Python ekosistem prihvatiti jedinstven pristup kori\u0161\u0107enju tipova (type hints \u2014 termin koji se u Python-u koristi kao sinonim za 'anotacije tipova'). To bi, uz mogu\u0107e rizike, bilo bolje od kori\u0161\u0107enja mnogih me\u0111usobno nespojivih pristupa. Drugo, \u017eeleli smo otvoreno razgovarati o mehanizmima anotiranja tipova sa brojnim predstavnicima Python zajednice. Delimi\u010dno, ova \u017eelja je bila vo\u0111ena time da ne \u017eelimo da izgledamo kao 'odmetnici' od osnovnih ideja jezika u o\u010dima \u0161ireg kruga Python programera. Ovo je dinami\u010dki tipiziran jezik, poznat po 'patkogenom tipiziranju'. U zajednici, na po\u010detku, nije moglo da ne do\u0111e do sumnje u ideju stati\u010dke tipizacije. No, takav stav je vremenom oslabio \u2014 nakon \u0161to je postalo jasno da stati\u010dka tipizacija ne\u0107e biti obavezna (i nakon \u0161to su ljudi shvatili da je zaista korisna).<\/p>\n<p>Sprejeta sintaksa namigov po tipih je bila zelo podobna tisti, ki jo je v tistem \u010dasu podpirala mypy. Dokument PEP 484 je iz\u0161el skupaj s Python 3.5 leta 2015. Python ni ve\u010d bil jezik, ki bi podpiral le dinami\u010dno tipizacijo. Rad razmi\u0161ljam o tem dogodku kot o pomembnem mejniku v zgodovini Pythona.<\/p>\n<h2>Za\u010detek migracije<\/h2>\n<p>\nKonec leta 2015 je v Dropboxu, za delo na mypy, nastala ekipa iz treh ljudi. V njej so bili Guido van Rossum, Greg Price in David Fisher. Od takrat se je situacija za\u010dela hitro razvijati. Prva ovira na poti rasti mypy je bila zmogljivost. Kot sem \u017ee namignil zgoraj, sem v zgodnjem obdobju razvoja projekta razmi\u0161ljal o prenosu implementacije mypy na jezik C, vendar je bila ta ideja zaenkrat izbrisana s seznama. Zastali smo na tem, da je za zagon sistema uporabljal interpreter CPython, ki ni bil dovolj hiter za orodja, kot je mypy. (Projekt PyPy, alternativna implementacija Pythona z JIT-kompilatorjem, nam tudi ni pomagal.)<\/p>\n<p>Na sre\u010do so nam tu pomagala nekatera algoritmi\u010dna izbolj\u0161anja. Prvi mo\u010dan \"pospe\u0161evalec\" je bila implementacija inkrementalne preveritve. Ideja tega izbolj\u0161anja je bila preprosta: \u010de se vse odvisnosti modula od zadnjega zagona mypy niso spremenile, lahko med delom z odvisnostmi uporabimo podatke, shranjene med prej\u0161njo sejo. Potrebovali smo le izvajati preverjanje tipov v spremenjenih datotekah in v tistih datotekah, ki so bile od njih odvisne. Mypy je \u0161el celo nekoliko dlje: \u010de se zunanji vmesnik modula ni spreminjal \u2013 je mypy menil, da ni treba ponovno preverjati drugih modulov, ki uva\u017eajo ta modul.<\/p>\n<p>Inkrementna kontrola nam je znatno pomogla u anotaciji velikih koli\u010dina postoje\u0107eg koda. Naime, ovaj proces obi\u010dno uklju\u010duje mno\u0161tvo iterativnih pokretanja mypy-a, jer se anotacije postepeno dodaju u kod i postepeno pobolj\u0161avaju. Prvo pokretanje mypy-a je i dalje bilo vrlo sporo, jer je trebalo proveriti veliki broj zavisnosti. Tada smo, radi pobolj\u0161anja situacije, implementirali mehanizam udaljenog ke\u0161iranja. Ako mypy otkrije da je lokalni ke\u0161 verovatno zastareo, preuzima trenutnu sliku ke\u0161a za celu kodeks bazu iz centralizovanog repozitorijuma. Zatim izvr\u0161ava, koriste\u0107i tu sliku, inkrementalnu proveru. Ovo nas je pomerilo jo\u0161 jedan zna\u010dajan korak napred u pove\u0107anju performansi mypy-a.<\/p>\n<p>To je bio period brze i prirodne implementacije sistema provere tipova u Dropbox-u. Do kraja 2016. godine, imali smo oko 420000 linija Python koda sa anotacijama tipova. Mnogi korisnici su entuzijasti\u010dno pristupili proveri tipova. U Dropbox-u mypy je koristilo sve vi\u0161e timova programera.<\/p>\n<p>Sve je tada izgledalo dobro, ali nas je jo\u0161 mnogo posla \u010dekalo. Po\u010deli smo da sprovodimo periodi\u010dne interne ankete me\u0111u korisnicima kako bismo identifikovali problemati\u010dna mesta projekta i razumeli koja pitanja treba re\u0161iti kao prioritet (ova praksa se koristi i danas). Kao \u0161to se pokazalo, najva\u017enije su bile dve zadatke. Prvo \u2014 potrebno je ve\u0107e pokri\u0107e koda tipovima, drugo \u2014 mypy je trebao da radi br\u017ee. Bilo je sasvim jasno da je na\u0161 rad na ubrzanju mypy-a i njegovom implementaciji u projekte kompanije jo\u0161 daleko od zavr\u0161etka. Svesni va\u017enosti ova dva zadatka, potpuno smo se posvetili njihovom re\u0161avanju.<\/p>\n<h2>Vi\u0161e performansi!<\/h2>\n<p>\nIncremntalne provjere su ubrzale mypy, ali ovaj alat jo\u0161 uvijek nije bio dovoljno brz. Mnoge inkrementalne provjere trajale su oko minute. Uzrok tome bili su cikli\u010dni imports. To vjerojatno ne\u0107e iznenaditi one koji su radili s velikim bazama koda pisanima na Pythonu. Imali smo skupove od stotina modula, od kojih je svaki neizravno uvozio sve druge. Ako bi se bilo koja datoteka u ciklusu uvoza promijenila, mypy je morao obraditi sve datoteke koje ulaze u taj ciklus, a \u010desto i sve module koji uvoze module iz ovog ciklusa. Jedan od takvih ciklusa bio je zloglasni 'klubak zavisnosti', koji je uzrokovao mnoge nevolje u Dropboxu. Jednom prilikom, ova struktura je sadr\u017eavala nekoliko stotina modula, pri \u010demu su je uvozili, izravno ili neizravno, mnogi testovi, a kori\u0161tena je i u produkcionalnom kodu.<\/p>\n<p>Razmatrali smo mogu\u0107nost 'raspetljavanja' cikli\u010dnih zavisnosti, ali nismo imali resurse za to. Bilo je previ\u0161e koda s kojim nismo bili upoznati. Na kraju smo se odlu\u010dili za alternativni pristup. Odlu\u010dili smo napraviti da mypy radi brzo \u010dak i u prisustvu 'klubaka zavisnosti'. Ovaj cilj postigli smo pomo\u0107u mypy demona. Demon je serverski proces koji implementira dvije zanimljive mogu\u0107nosti. Prvo, dr\u017ei u memoriji informacije o cijeloj bazi koda. To zna\u010di da pri svakom pokretanju mypy ne mora u\u010ditavati ke\u0161irane podatke koji se odnose na tisu\u0107e uvezenih zavisnosti. Drugo, pa\u017eljivo analizira zavisnosti izme\u0111u funkcija i drugih entiteta na razini malih strukturnih jedinica. Na primjer, ako funkcija <code>foo<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>bar<\/code>, tada postoji zavisnost <code>foo<\/code> od <code>bar<\/code>. Kada se datoteka promijeni \u2014 demon prvo, u izolaciji, obra\u0111uje samo promijenjenu datoteku. Zatim gleda na promjene te datoteke, vidljive izvan, kao \u0161to su promijenjeni potpisi funkcija. Demon koristi detaljne informacije o uvozu samo za ponovno provjeravanje onih funkcija koje zaista koriste promijenjenu funkciju. Obi\u010dno se ovim pristupom provjerava vrlo malo funkcija.<\/p>\n<p>Izvedba vsega tega ni bila enostavna naloga, saj je bila prvotna izvedba mypy mo\u010dno usmerjena na obdelavo enega samega datoteka naenkrat. Soo\u010diti smo se morali z mno\u017eico mejnih situacij, ki so zahtevale ponovne preveritve, kadar je pri\u0161lo do sprememb v kodi. Na primer, do tega pride, ko razredu dodelijo novo osnovno klas. Ko smo dosegli, kar smo \u017eeleli, smo ve\u010dino inkrementalnih preverjanj uspeli skraj\u0161ati na nekaj sekund. To se nam je zdelo velik dose\u017eek.<\/p>\n<h2>\u0160e ve\u010d zmogljivosti!<\/h2>\n<p>\nSkupaj z oddaljenim predpomnjenjem, o katerem sem govoril prej, je demon mypy prakti\u010dno v celoti re\u0161il te\u017eave, ki nastanejo, kadar programer pogosto izvaja preverjanje tipov ob spreminjanju majhnega \u0161tevila datotek. Vendar je bila zmogljivost sistema v najslab\u0161em mo\u017enem scenariju \u0161e vedno dale\u010d od optimalne. \u010cisto zagon mypy je lahko trajal ve\u010d kot 15 minut. Kar je bilo precej ve\u010d, kot bi nam bilo sprejemljivo. Vsak teden se je situacija slab\u0161ala, saj so programerji \u0161e naprej pisali nov kodo in dodajali pripombe obstoje\u010demu kodu. Na\u0161i uporabniki so \u0161e naprej hrepeneli po ve\u010dji zmogljivosti, mi pa smo bili z veseljem pripravljeni priti naproti.<\/p>\n<p>Odlo\u010dili smo se vrniti k eni izmed prvih idej o mypy. Natan\u010dneje, k pretvorbi Python kode v C kodo. Eksperimenti s Cython-om (sistemom, ki omogo\u010da prevod kode napisane v Pythonu v C kodo) niso prinesli opaznega pospe\u0161ka, zato smo se odlo\u010dili obuditi idejo pisanja lastnega prevajalnika. Ker je \u017ee obstoje\u010da koda mypy (napisana v Pythonu) vsebovala vse potrebne oznake tipov, se nam je zdela smiselna poskusiti uporabiti te oznake za pospe\u0161itev delovanja sistema. Hitro sem ustvaril prototip za preverjanje te ideje. Poka\u017ee je ve\u010d kot 10-kratno pove\u010danje zmogljivosti v razli\u010dni mikro-benchmarkih. Na\u0161a ideja je bila, da prevedemo Python module v C module z uporabo Cython-a in spremenimo oznake tipov v preverjanja tipov, ki se izvajajo med izvajanjem programa (obi\u010dajno se oznake tipov med izvajanjem programov ignorirajo in se uporabljajo le s sistemi za preverjanje tipov). Dejansko smo na\u010drtovali, da prevedemo implementacijo mypy iz Pythona v jezik, ki bi bil stati\u010dno tipiziran in bi izgledal (in ve\u010dinoma tudi deloval) natan\u010dno tako kot Python. (Ta vrsta medjezikovne migracije je postala nekak\u0161na tradicija projekta mypy. Prvotna implementacija mypy je bila napisana v Alore, nato je bil ustvarjen sintakti\u010dni hibrid Java in Python).<\/p>\n<p>Usmeritev na API raz\u0161iritev CPython je bila klju\u010dna za ohranjanje projektne upravljivosti. Nismo morali implementirati virtualnega stroja ali katerekoli knji\u017enice, ki bi jo mypy potreboval. Poleg tega bi bila celotna ekosistem Python \u0161e vedno dostopna, vse orodja (kot je pytest) bi bila na voljo. To je pomenilo, da bi lahko \u0161e naprej uporabljali interpretirano Python kodo med razvojem, kar bi nam omogo\u010dilo hitro spreminjanje kode in njeno testiranje, namesto da bi \u010dakali na prevod kode. Videti je bilo, kot da nam odli\u010dno uspeva, da smo tako reko\u010d sedeli na dveh stolih, in to nam je bilo v\u0161e\u010d.<\/p>\n<p>Kompajler, ki smo ga poimenovali mypyc (saj uporablja mypy za analizo tipov kot sprednji del), se je izkazal za zelo uspe\u0161en projekt. Na splo\u0161no smo dosegli pribli\u017eno \u0161tirikratno pospe\u0161itev pogostih zagonov mypy brez uporabe predpomnjenja. Razvoj jedra projekta mypyc je manj\u0161emu timu, ki so ga sestavljali Michael Sullivan, Ivan Levkivski, Hugh Han in jaz, vzel pribli\u017eno \u0161tiri koledarske mesece. Ta obseg dela je bil precej manj\u0161i od tistega, ki bi ga potrebovali za prepisovanje mypy, na primer v C++ ali Go. Poleg tega smo upali, da bomo lahko mypyc razvili do te mere, da ga bodo drugi programerji iz Dropboxa lahko uporabljali za kompilacijo in pospe\u0161evanje svojega kode.<\/p>\n<p>Za dosego tak\u0161ne ravni zmogljivosti smo morali uporabiti nekatere zanimive in\u017eenirske re\u0161itve. Tako lahko kompajler pospe\u0161i izvajanje mnogih operacij s pomo\u010djo hitrih nizkotohanovskih konstrukcij C. Na primer, klic skompajlirane funkcije se prevede v klic C-funkcije. Tak klic se izvaja precej hitreje kot klic interpretirane funkcije. Nekatere operacije, kot je iskanje v besedilnih datotekah, so se \u0161e vedno zana\u0161ale na obi\u010dajne klice C-API iz CPython, ki so po kompilaciji postali le malenkost hitrej\u0161i. Uspe\u0161no smo se znebili dodatne obremenitve sistema, ki jo je povzro\u010dala interpretacija, a je to v tem primeru prineslo le majhen dobi\u010dek pri zmogljivosti.<\/p>\n<p>Za odkrivanje najpogostej\u0161ih 'po\u010dasnih' operacij smo izvedli profiliranje kode. Oboro\u017eeni s pridobljenimi podatki smo posku\u0161ali bodisi prilagoditi mypyc, da bi generiral hitrej\u0161o C-kodo za tovrstne operacije, bodisi prepisati ustrezno Python kodo z uporabo hitrej\u0161ih operacij (v\u010dasih pa nismo imeli preprostih re\u0161itev za dolo\u010dene te\u017eave). Prepisovanje Python kode se je pogosto izkazalo za la\u017ejo re\u0161itev problema kot izvedba avtomatizacije iste transformacije v prevajalniku. Na dolgi rok smo \u017eeleli avtomatizirati mnoge od teh transformacij, vendar smo bili takrat osredoto\u010deni na to, da bi mypy pospe\u0161ili z minimalnim trudom. In na tej poti smo nekoliko skraj\u0161ali ovinke.<\/p>\n<p>Nastavi\u0107e se\u2026<\/p>\n<p><b>Spo\u0161tovani bralci!<\/b> Kak\u0161ni so bili va\u0161i vtisi o projektu mypy, ko ste izvedeli za njegovo obstoj?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Pot do preverjanja tipov 4 milijonov vrstic Python kode. Del 2\" src=\"\/wp-content\/uploads\/2019\/09\/e3c1dd40228b56d0196fd65e4df22a59.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Pot do preverjanja tipov 4 milijonov vrstic Python kode. Del 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Izvor: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u2192 \u0427\u0438\u0442\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0438\u043f\u043e\u0432 (PEP 484) \u041c\u044b \u043f\u0440\u043e\u0432\u0435\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b \u0441 mypy \u0432 Dropbox \u0432\u043e \u0432\u0440\u0435\u043c\u044f Hack Week 2014. Hack Week \u2014 \u044d\u0442\u043e \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u0435, \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c\u043e\u0435 Dropbox \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0439 \u043d\u0435\u0434\u0435\u043b\u0438. \u0412 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-38479","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.5 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.5\" \/>\n\t\t<meta property=\"og:locale\" content=\"sl_SI\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Pot do preverjanja tipov 4 milijonov vrstic Python kode. Del 2 | ProHoster","description":"Danas objavljujemo drugi deo prevoda materijala o tome kako je Dropbox organizovao kontrolu tipova nekoliko miliona redova Python koda.","canonical_url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sl_SI","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster","og:description":"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.","og:url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:23:56+00:00","article:modified_time":"2019-10-31T19:23:56+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"38479","title":null,"description":null,"keywords":null,"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 22:13:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:08:32","updated":"2026-08-11 12:50:31","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}