{"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\/hr\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Put do provjere tipova 4 milijuna linija Python koda. Dio 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Danas objavljujemo drugi dio prijevoda materijala o tome kako su u Dropboxu organizirali kontrolu tipova za nekoliko milijuna linija Python koda.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Put do provjere tipova 4 milijuna linija Python koda. Dio 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\u010ditaj prvu dio<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Slu\u017ebena podr\u0161ka za tipove (PEP 484)<\/h2>\n<p>\nProveli smo prve ozbiljne eksperimentalne radove s mypy u Dropboxu tijekom Hack Weeka 2014. Hack Week je doga\u0111aj koji se u Dropboxu odr\u017eava tijekom jednog tjedna. U to vrijeme zaposlenici mogu raditi na \u010demu god \u017eele! Neki od najpoznatijih tehnolo\u0161kih projekata u Dropboxu zapo\u010deli su upravo na takvim doga\u0111ajima. Kao rezultat ovog eksperimenta, do\u0161li smo do zaklju\u010dka da mypy izgleda obe\u0107avaju\u0107e, iako ovaj projekt jo\u0161 uvijek nije bio spreman za \u0161iroku upotrebu.<\/p>\n<p>Tada se u zraku osje\u0107ala ideja o standardizaciji sustava za davanje savjeta o tipovima u Pythonu. Kao \u0161to sam spomenuo, po\u010dev\u0161i od Pythona 3.0, mogli su se koristiti tipne anotacije za funkcije, ali to su bila samo proizvoljna izra\u017eavanja, bez odre\u0111enih sintakti\u010dkih i semanti\u010dkih pravila. Tijekom izvo\u0111enja programa, te se anotacije uglavnom jednostavno ignoriraju. Nakon Hack Weeka, po\u010deli smo raditi na standardizaciji semantike. Ovaj rad doveo je do stvaranja <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (na ovom dokumentu su zajedno radili Guido van Rossum, Lukasz Langa i ja).<\/p>\n<p>Na\u0161e motive mogli bismo razmotriti iz dva aspekta. Prvo, nadali smo se da \u0107e cijela ekosustav Pythona mo\u0107i prihvatiti zajedni\u010dki pristup kori\u0161tenju tipnih savjeta (type hints \u2014 termin koji se u Pythonu koristi kao ekvivalent \"tipnim anotacijama\"). To bi, uzimaju\u0107i u obzir mogu\u0107e rizike, bilo bolje od kori\u0161tenja mno\u0161tva me\u0111usobno nespojivih pristupa. Drugo, \u017eeljeli smo otvoreno raspraviti mehanizme anotiranja tipova s brojnim \u010dlanovima Python zajednice. Djelomi\u010dno, ta \u017eelja bila je uzrokovana time da ne bismo \u017eeljeli izgledati kao \"odmetnici\" od osnovnih ideja jezika u o\u010dima \u0161iroke mase Python programera. To je dinami\u010dki tipizirani jezik, poznat po \"patkasti tipizaciji\". U zajednici, na samom po\u010detku, nije moglo ne izazvati sumnju prema ideji stati\u010dke tipizacije. No, takav stav na kraju je oslabio \u2014 nakon \u0161to je postalo jasno da se ne planira u\u010diniti stati\u010dku tipizaciju obveznom (i nakon \u0161to su ljudi shvatili da je ona zaista korisna).<\/p>\n<p>Kona\u010dno usvojena sintaksa natuknica za tipove bila je vrlo sli\u010dna onoj koju je tada podr\u017eavao mypy. Dokument PEP 484 objavljen je zajedno s Python 3.5 2015. godine. Python vi\u0161e nije bio jezik koji podr\u017eava samo dinami\u010dko tipiziranje. Volim misliti o tom doga\u0111aju kao o zna\u010dajnoj prekretnici u povijesti Pythona.<\/p>\n<h2>Po\u010detak migracije<\/h2>\n<p>\nKrajem 2015. u Dropboxu je formiran tim od tri osobe za rad na mypyju. Tim su \u010dinili Guido van Rossum, Greg Price i David Fisher. Od tog trenutka situacija je po\u010dela vrlo brzo napredovati. Prva prepreka na putu rasta mypyja bila je izvedba. Kao \u0161to sam ranije spomenuo, u ranom razdoblju razvoja projekta razmi\u0161ljao sam o prevo\u0111enju implementacije mypyja na jezik C, ali je ta ideja zasad izba\u010dena iz liste. Bili smo zaglavljeni s tim da se za pokretanje sustava koristio interpreter CPython, koji nije bio dovoljno brz za alate poput mypyja. (Projekt PyPy, alternativna implementacija Pythona s JIT kompajlerom, tako\u0111er nam nije pomogao.)<\/p>\n<p>Sre\u0107om, ovdje su nam pomogla neka algoritamska pobolj\u0161anja. Prvo sna\u017eno \"ubrzanje\" bilo je implementacija inkrementalne provjere. Ideja ovog pobolj\u0161anja bila je jednostavna: ako se sve ovisnosti modula od prethodnog pokretanja mypyja nisu promijenile, mo\u017eemo koristiti, tijekom rada s ovisnostima, podatke pohranjene tijekom prethodne sesije. Trebalo je samo izvr\u0161iti provjeru tipova u izmijenjenim datotekama i u onim datotekama koje su od njih ovisile. Mypy je oti\u0161ao \u010dak i malo dalje: ako vanjski su\u010delje modula nije promijenjeno \u2014 mypy je smatrao da nije potrebno ponovo provjeravati druge module koji uvoze taj modul.<\/p>\n<p>Inkrementalna provjera zna\u010dajno nam je pomogla u anotiranju velikih koli\u010dina postoje\u0107eg koda. Naime, ovaj proces obi\u010dno uklju\u010duje mnogo iterativnih pokretanja mypy-a, jer se anotacije postupno dodaju u kod i postupno pobolj\u0161avaju. Prvo pokretanje mypy-a jo\u0161 uvijek je bilo vrlo sporo, jer je prilikom njegovog izvo\u0111enja trebalo provjeriti brojne ovisnosti. Tada smo, kako bismo pobolj\u0161ali situaciju, implementirali mehanizam udaljenog ke\u0161iranja. Ako mypy otkrije da je lokalni ke\u0161 vjerojatno zastario, preuzima trenutnu snimku ke\u0161a za cijelu kodnu bazu iz centraliziranog repozitorija. Zatim izvodi, koriste\u0107i ovu snimku, inkrementalnu provjeru. To nas je jo\u0161 jedan zna\u010dajan korak unaprijedilo u pove\u0107anju performansi mypy-a.<\/p>\n<p>To je bio period brzog i prirodnog uvo\u0111enja sustava provjere tipova u Dropbox. Do kraja 2016. godine imali smo ve\u0107 otprilike 420000 redaka Python koda s anotacijama tipova. Mnogi su korisnici s entuzijazmom prihvatili provjeru tipova. U Dropbox-u, mypy su koristile sve vi\u0161e timova programera.<\/p>\n<p>Sve je tada izgledalo dobro, ali nam je jo\u0161 mnogo posla preostalo. Po\u010deli smo provoditi periodi\u010dne interne ankete me\u0111u korisnicima kako bismo identificirali problemati\u010dna mjesta projekta i razumjeli koje je pitanje potrebno rije\u0161iti prvo (ova praksa se koristi u tvrtki i danas). Najva\u017enija, kako je postalo jasno, bila su dva zadatka. Prvi je bio pove\u0107ati pokrivenost koda tipovima, drugi je bio osigurati da mypy radi br\u017ee. Bilo je potpuno jasno da na\u0161 rad na ubrzavanju mypy-a i njegovom uvo\u0111enju u projekte tvrtke jo\u0161 uvijek nije bio zavr\u0161en. Svjesni va\u017enosti ova dva zadatka, krenuli smo u njihovo rje\u0161avanje.<\/p>\n<h2>Vi\u0161e performansi!<\/h2>\n<p>\nInkrementne provjere ubrzale su mypy, no ovaj alat jo\u0161 uvijek nije bio dovoljno brz. Mnoge inkrementne provjere trajale su oko minute. Razlog tome su bile cikli\u010dne ovisnosti. Ovo vjerojatno ne\u0107e iznenaditi one koji su radili s velikim kodnim bazama napisanim na Pythonu. Imali smo setove od stotina modula, od kojih je svaki neizravno uvozio sve ostale. Ako bi se bilo koja datoteka u ciklusu uvoza promijenila, mypy bi morao obraditi sve datoteke uklju\u010dene u taj ciklus, a \u010desto i bilo koji modul koji uvozi module iz ovog ciklusa. Jedan od takvih ciklusa bio je zloslutni \"klubak ovisnosti\", koji je bio uzrok mnogih neugodnosti u Dropboxu. Jednom je ta struktura sadr\u017eavala nekoliko stotina modula, a izravno ili neizravno ju je uvozilo mno\u0161tvo testova, kori\u0161tena je i u produkcijskom kodu.<\/p>\n<p>Razmatrali smo mogu\u0107nost \"razmatanja\" cikli\u010dnih ovisnosti, 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 da mypy radi brzo \u010dak i u prisutnosti \"klubaka ovisnosti\". Ovu smo svrhu postigli pomo\u0107u mypy demona. Demon je serverski proces koji implementira dvije zanimljive mogu\u0107nosti. Prvo \u2014 dr\u017ei u memoriji informacije o cijeloj kodnoj bazi. To zna\u010di da prilikom svakog pokretanja mypy ne treba u\u010ditavati predmemorirane podatke koji se odnose na tisu\u0107e uvezenih ovisnosti. Drugo \u2014 pa\u017eljivo analizira ovisnosti izme\u0111u funkcija i drugih entiteta na razini malih strukturalnih jedinica. Na primjer, ako funkcija <code>foo<\/code> poziva funkciju <code>bar<\/code>, postoji ovisnost <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 izvana, poput promijenjenih potpisivanja funkcija. Demon koristi detaljne informacije o uvozu samo za ponovno provjeravanje onih funkcija koje zaista koriste promijenjenu funkciju. Obi\u010dno, s ovim pristupom, potrebno je provjeravati vrlo mali broj funkcija.<\/p>\n<p>Implementacija svega ovoga bila je izazovna, jer je izvorna implementacija mypy bila usredoto\u010dena na obradu jednog datoteke odjednom. Morali smo se suo\u010diti s brojnim rubnim situacijama, \u010dije je nastajanje zahtijevalo ponovne provjere u slu\u010dajevima kada se u kodu ne\u0161to mijenjalo. Na primjer, to se doga\u0111a kada se klasi dodijeli nova osnovna klasa. Nakon \u0161to smo postigli ono \u0161to smo htjeli, uspjeli smo smanjiti vrijeme izvr\u0161avanja ve\u0107ine inkrementalnih provjera na nekoliko sekundi. To nam se \u010dinilo velikom pobjedom.<\/p>\n<h2>Jo\u0161 vi\u0161e performansi!<\/h2>\n<p>\nZajedno s udaljenim ke\u0161iranjem, o kojem sam prethodno govorio, mypy demon gotovo potpuno je rije\u0161io probleme koji nastaju kada programer \u010desto pokre\u0107e provjeru tipova, unose\u0107i promjene u mali broj datoteka. Me\u0111utim, performanse sustava u najmanje povoljnim uvjetima kori\u0161tenja jo\u0161 uvijek su bile daleko od optimalnih. \u010cisto pokretanje mypy moglo je trajati vi\u0161e od 15 minuta. A to je bilo daleko vi\u0161e nego \u0161to bismo \u017eeljeli. Svakog tjedna situacija se pogor\u0161avala, jer su programeri nastavili pisati novi kod i dodavati anotacije postoje\u0107em kodu. Na\u0161i korisnici i dalje su \u017eeljeli ve\u0107u izvedbu, a mi smo s rado\u0161\u0107u bili spremni iza\u0107i im u susret.<\/p>\n<p>Odlu\u010dili smo se vratiti na jednu od ranih ideja vezanih uz mypy. Naime, na pretvaranje Python koda u C kod. Eksperimenti s Cython-om (to je sustav koji omogu\u0107ava prevo\u0111enje koda napisanog na Python-u u C kod) nisu nam omogu\u0107ili nikakvo vidljivo ubrzanje, stoga smo odlu\u010dili o\u017eivjeti ideju pisanja vlastitog kompajlera. Kako je kodna baza mypy (napisana na Python-u) ve\u0107 sadr\u017eavala sve potrebne tipovne anotacije, \u010dinilo nam se vrijednim poku\u0161ati iskoristiti te anotacije za ubrzanje rada sustava. Brzo sam stvorio prototip kako bih provjerio tu ideju. Pokazao je vi\u0161e od 10 puta ve\u0107e performanse na raznim mikro-benchmarcima. Na\u0161a ideja bila je kompilirati Python module u C module koriste\u0107i Cython, i pretvarati tipovne anotacije u provjere tipova koje se izvode tijekom izvo\u0111enja programa (obi\u010dno se tipovne anotacije ignoriraju tijekom izvo\u0111enja programa i koriste ih samo sustavi za provjeru tipova). U stvari, planirali smo prevesti implementaciju mypy s Pythona na jezik koji je stati\u010dki tipiziran, koji bi izgledao (i, ve\u0107inom, radio) to\u010dno kao Python. (Ova vrsta me\u0111ujezi\u010dne migracije postala je svojevrsna tradicija projekta mypy. Prvotna implementacija mypy bila je napisana na Alore, zatim je bio sintakti\u010dki hibrid Java i Python-a).<\/p>\n<p>Usmjerenje na API ekstenzija CPython-a bilo je klju\u010dno da ne izgubimo mogu\u0107nosti upravljanja projektom. Nismo trebali implementirati virtualnu ma\u0161inu ili bilo koje biblioteke koje su bile potrebne mypy-u. Osim toga, cijela ekosustava Python-a bi nam i dalje bila dostupna, svi alati (poput pytest-a) bi bili dostupni. To je zna\u010dilo da bismo mogli nastaviti koristiti interpretirani Python kod tijekom razvoja, \u0161to bi nam omogu\u0107ilo nastavak rada koriste\u0107i vrlo brz sustav uno\u0161enja promjena u kod i njegovo testiranje, umjesto da \u010dekamo kompilaciju koda. Izgledalo je kao da nam odli\u010dno ide, da tako ka\u017eem, sjediti na dva stolca, i to nam se svi\u0111alo.<\/p>\n<p>Kompajler koji smo nazvali mypyc (s obzirom na to da kao prednji deo koristi mypy za analizu tipova) pokazao se kao prili\u010dno uspe\u0161an projekat. U su\u0161tini, postigli smo pribli\u017eno \u010detverostruko ubrzanje \u010destih pokretanja mypy bez kori\u0161\u0107enja ke\u0161iranja. Razvoj jezgra projekta mypyc trajao je od malog tima, u kojem su bili Majkl Salivan, Ivan Levkivski, Hju Han i ja, oko 4 kalendarska meseca. Ovaj obim posla bio je daleko manji od onog koji bi bio potreban za prepisivanje mypy, na primer, na C++ ili Go. Osim toga, promena koju smo morali da izvr\u0161imo u projektu bila je mnogo manja nego \u0161to bi bila potrebna za prepisivanje na drugi jezik. Tako\u0111e smo se nadali da \u0107emo mo\u0107i da dovedemo mypyc do nivoa koji \u0107e omogu\u0107iti drugim programerima iz Dropbox-a da ga koriste za kompilaciju i ubrzanje svog koda.<\/p>\n<p>Da bismo postigli sli\u010dan nivo performansi, morali smo primeniti neka zanimljiva in\u017eenjerska re\u0161enja. Tako kompajler mo\u017ee ubrzati izvr\u0161avanje mnogih operacija zahvaljuju\u0107i kori\u0161\u0107enju brzih niskonivojskih konstrukcija C. Na primer, poziv kompajlirane funkcije prevodi se u poziv C funkcije. Takav poziv se izvr\u0161ava mnogo br\u017ee od poziva interpretirane funkcije. Neke operacije, kao \u0161to je pretra\u017eivanje u re\u010dnicima, jo\u0161 uvek su se svodile na kori\u0161\u0107enje obi\u010dnih poziva C API-ja iz CPython-a, koji su nakon kompajlacije bili samo malo br\u017ei. Uspeli smo da se oslobodimo dodatnog optere\u0107enja na sistem koje stvara interpretacija, ali to je u ovom slu\u010daju donelo samo mali dobitak u pogledu performansi.<\/p>\n<p>Kako bismo identificirali naj\u010de\u0161\u0107e \"spore\" operacije, proveli smo profiliranje koda. Opremljeni tim podacima, poku\u0161ali smo ili optimizirati mypyc kako bi generirao br\u017ei C-kod za te operacije, ili prepisati odgovaraju\u0107i Python kod koriste\u0107i br\u017ee operacije (ponekad jednostavno nismo imali razrje\u0161enje za odre\u0111eni problem). Prepisivanje Python koda \u010desto se pokazalo lak\u0161im rje\u0161enjem od implementacije automatskog izvr\u0161avanja iste transformacije u kompajleru. Dugoro\u010dno, \u017eeljeli smo automatizirati mnoge od tih transformacija, ali u tom trenutku bili smo usmjereni na to da, s minimalnim naporom, ubrzamo mypy. Na tom putu skratili smo nekoliko procesa.<\/p>\n<p>Nastavak slijedi\u2026<\/p>\n<p><b>Dragi \u010ditatelji!<\/b> Koji su dojmovi na vas ostavili mypy projekt kada ste prvi put saznali za njega?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Put do provjere tipova 4 milijuna linija Python koda. Dio 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=\"Put do provjere tipova 4 milijuna linija Python koda. Dio 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.4 - 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\/hr\/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.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"hr_HR\" \/>\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\/hr\/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\udd47Put do provjere tipova 4 milijuna redaka Python koda. Dio 2 | ProHoster","description":"Danas objavljujemo drugi dio prijevoda materijala o tome kako su u Dropboxu organizirali kontrolu tipova za nekoliko milijuna linija Python koda.","canonical_url":"https:\/\/prohoster.info\/hr\/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":"hr_HR","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\/hr\/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\/hr\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}