{"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\/sv\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"V\u00e4gen till typkontroll av 4 miljoner rader Python-kod. Del 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Idag publicerar vi den andra delen av \u00f6vers\u00e4ttningen av materialet om hur Dropbox organiserade typkontroll f\u00f6r flera miljoner rader Python-kod.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"V\u00e4gen till typkontroll av 4 miljoner rader Python-kod. 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\/\">L\u00e4s f\u00f6rsta delen<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Officiellt st\u00f6d f\u00f6r typer (PEP 484)<\/h2>\n<p>\nVi genomf\u00f6rde v\u00e5ra f\u00f6rsta seri\u00f6sa experiment med mypy p\u00e5 Dropbox under Hack Week 2014. Hack Week \u00e4r ett evenemang som h\u00e5lls av Dropbox under en vecka. Under denna tid kan anst\u00e4llda arbeta med vad de vill! N\u00e5gra av de mest k\u00e4nda tekniska projekten fr\u00e5n Dropbox har startats just under s\u00e5dana evenemang. Som ett resultat av detta experiment drog vi slutsatsen att mypy ser lovande ut, \u00e4ven om projektet \u00e4nnu inte var redo f\u00f6r bred anv\u00e4ndning.<\/p>\n<p>Vid den tiden fl\u00f6t id\u00e9n om att standardisera system f\u00f6r typinmatningar i Python i luften. Som jag redan n\u00e4mnt, sedan Python 3.0 har det varit m\u00f6jligt att anv\u00e4nda typanoteringar f\u00f6r funktioner, men dessa var bara godtyckliga uttryck utan specifik syntaktik och semantik. Under programk\u00f6rningen ignorerades dessa anoteringar i stor utstr\u00e4ckning. Efter Hack Week b\u00f6rjade vi arbeta med standardiseringen av semantiken. Detta arbete ledde till skapandet av <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (detta dokument utarbetades gemensamt av Guido van Rossum, \u0141ukasz Langa och mig).<\/p>\n<p>V\u00e5ra motiv kan ses fr\u00e5n tv\u00e5 perspektiv. F\u00f6r det f\u00f6rsta hoppades vi att hela Python-ekosystemet skulle kunna anta en gemensam metod f\u00f6r anv\u00e4ndning av typinmatningar (type hints \u2014 en term som anv\u00e4nds i Python som en analog till 'typannoteringar'). Detta, med alla eventuella risker i \u00e5tanke, skulle vara b\u00e4ttre \u00e4n att anv\u00e4nda m\u00e5nga \u00f6msesidigt of\u00f6renliga metoder. F\u00f6r det andra ville vi \u00f6ppet diskutera mekanismerna f\u00f6r typanotering med m\u00e5nga representanter fr\u00e5n Python-communityn. Delvis berodde denna \u00f6nskan p\u00e5 att vi inte ville framst\u00e5 som 'avvikare' fr\u00e5n spr\u00e5kets grundl\u00e4ggande id\u00e9er i \u00f6gonen p\u00e5 den breda massan av Python-programmerare. Det \u00e4r ett dynamiskt typat spr\u00e5k, k\u00e4nt f\u00f6r 'anktypning'. I communityn, i b\u00f6rjan, kunde man inte undvika en viss misst\u00e4nksamhet mot id\u00e9n om statisk typning. Men denna inst\u00e4llning har s\u00e5 sm\u00e5ningom minskat \u2014 efter att det blev klart att statisk typning inte planeras att g\u00f6ras obligatorisk (och efter att folk ins\u00e5g att det faktiskt \u00e4r anv\u00e4ndbart).<\/p>\n<p>Den antagna syntaktiska strukturen f\u00f6r typanvisningar liknade mycket den som mypy d\u00e5 st\u00f6dde. Dokumentet PEP 484 sl\u00e4pptes tillsammans med Python 3.5 \u00e5r 2015. Python var inte l\u00e4ngre ett spr\u00e5k som enbart st\u00f6dde dynamisk typning. Jag gillar att t\u00e4nka p\u00e5 detta som en viktig milstolpe i Pythons historia.<\/p>\n<h2>B\u00f6rjan av migrationen<\/h2>\n<p>\nI slutet av 2015 bildades ett team av tre personer p\u00e5 Dropbox f\u00f6r att arbeta med mypy. Det bestod av Guido van Rossum, Greg Price och David Fisher. Fr\u00e5n och med detta b\u00f6rjade situationen utvecklas mycket snabbt. Det f\u00f6rsta hinder som mypy st\u00f6tte p\u00e5 var prestanda. Som jag n\u00e4mnt tidigare funderade jag i projektets tidiga skeden p\u00e5 att \u00f6vers\u00e4tta mypy:s implementation till C, men den id\u00e9n str\u00f6ks f\u00f6r tillf\u00e4llet. Vi blev fast vid att vi anv\u00e4nde CPython-tolkaren, som inte hade en hastighet tillr\u00e4cklig f\u00f6r verktyg som mypy. (Projektet PyPy, en alternativ implementation av Python med JIT-kompilator, hj\u00e4lpte oss inte heller.)<\/p>\n<p>Som tur var fick vi hj\u00e4lp av vissa algoritmiska f\u00f6rb\u00e4ttringar. Den f\u00f6rsta kraftfulla \"acceleratorn\" var implementeringen av inkrementell kontroll. Id\u00e9n bakom denna f\u00f6rb\u00e4ttring var enkel: om alla beroenden f\u00f6r en modul inte hade f\u00f6r\u00e4ndrats sedan mypy senast k\u00f6rdes, s\u00e5 kunde vi anv\u00e4nda de data som cacheats under f\u00f6reg\u00e5ende session vid arbetet med beroenden. Vi beh\u00f6vde bara utf\u00f6ra typkontroll i de \u00e4ndrade filerna och i filer som berodde p\u00e5 dem. Mypy gick till och med ett steg l\u00e4ngre: om modulen yttre gr\u00e4nssnitt inte hade f\u00f6r\u00e4ndrats, ans\u00e5g mypy att andra moduler som importerade denna modul inte beh\u00f6vde kontrolleras igen.<\/p>\n<p>Den inkrementella kontrollen har verkligen hj\u00e4lpt oss n\u00e4r vi har anv\u00e4nt oss av annoteringar p\u00e5 stora m\u00e4ngder befintlig kod. Problemet \u00e4r att denna process vanligtvis involverar m\u00e5nga iterativa k\u00f6rningar av mypy, eftersom annotationerna gradvis l\u00e4ggs till i koden och f\u00f6rb\u00e4ttras \u00f6ver tid. Den f\u00f6rsta k\u00f6rningen av mypy var fortfarande v\u00e4ldigt l\u00e5ngsam, eftersom den beh\u00f6vde kontrollera m\u00e5nga beroenden. F\u00f6r att f\u00f6rb\u00e4ttra situationen implementerade vi en mekanism f\u00f6r fj\u00e4rr-cache. Om mypy uppt\u00e4cker att den lokala cachen sannolikt \u00e4r f\u00f6r\u00e5ldrad, laddar den ner den aktuella cachen f\u00f6r hela kodbasen fr\u00e5n ett centraliserat repository. D\u00e4refter utf\u00f6rs den inkrementella kontrollen med hj\u00e4lp av denna snapshot. Detta har avancerat oss ytterligare ett steg mot att \u00f6ka mypy:s prestanda.<\/p>\n<p>Det var en period av snabb och naturlig implementering av typkontrollsystemet p\u00e5 Dropbox. I slutet av 2016 hade vi redan omkring 420 000 rader Python-kod med typannoteringar. M\u00e5nga anv\u00e4ndare tog entusiastiskt till sig typkontrollen. P\u00e5 Dropbox anv\u00e4ndes mypy av allt fler utvecklingsteam.<\/p>\n<p>Allt s\u00e5g bra ut d\u00e5, men vi hade fortfarande mycket kvar att g\u00f6ra. Vi b\u00f6rjade genomf\u00f6ra regelbundna interna anv\u00e4ndarunders\u00f6kningar f\u00f6r att identifiera problemomr\u00e5den i projektet och f\u00f6rst\u00e5 vilka fr\u00e5gor som beh\u00f6vde l\u00f6sas f\u00f6rst (denna praktik anv\u00e4nds fortfarande i f\u00f6retaget idag). Det blev klart att tv\u00e5 uppgifter var de viktigaste. F\u00f6r det f\u00f6rsta beh\u00f6vde vi mer kodt\u00e4cke med typer, och f\u00f6r det andra beh\u00f6vde mypy att arbeta snabbare. Det var helt tydligt att v\u00e5rt arbete med att snabba upp mypy och implementera det i f\u00f6retagets projekt fortfarande hade en l\u00e5ng v\u00e4g kvar att g\u00e5. Med full insikt om vikten av dessa tv\u00e5 uppgifter satte vi ig\u00e5ng med att l\u00f6sa dem.<\/p>\n<h2>Mer prestanda!<\/h2>\n<p>\nInkrementella kontroller har p\u00e5skyndat mypy, men verktyget \u00e4r fortfarande inte tillr\u00e4ckligt snabbt. M\u00e5nga inkrementella kontroller tog runt en minut. Orsaken till detta var cykliska importer. Detta f\u00f6rv\u00e5nar f\u00f6rmodligen ingen som har arbetat med stora kodbaser skrivna i Python. Vi hade upps\u00e4ttningar med hundratals moduler, d\u00e4r var och en indirekt importerade alla andra. Om n\u00e5gon fil i importcykeln \u00e4ndrades, tvingades mypy att bearbeta alla filer som ingick i denna cykel, och ofta \u00e4ven moduler som importerade moduler fr\u00e5n denna cykel. En av dessa cykler var den \u00f6k\u00e4nda \"beroendeknuten\" som orsakade m\u00e5nga problem i Dropbox. Vid ett tillf\u00e4lle inneh\u00f6ll denna struktur flera hundra moduler, och den importerades, direkt eller indirekt, av m\u00e5nga tester, samt anv\u00e4ndes i produktionskoden.<\/p>\n<p>Vi \u00f6verv\u00e4gde m\u00f6jligheten att \"reda ut\" cykliska beroenden, men vi hade inte resurserna f\u00f6r att g\u00f6ra det. Det fanns alldeles f\u00f6r mycket kod som vi inte k\u00e4nde till. Till slut landade vi p\u00e5 ett alternativt tillv\u00e4gag\u00e5ngss\u00e4tt. Vi best\u00e4mde oss f\u00f6r att f\u00e5 mypy att fungera snabbt \u00e4ven med \"beroendeknutar\". Vi n\u00e5dde detta m\u00e5l med hj\u00e4lp av mypy-demonen. Demonen \u00e4r en serverprocess som implementerar tv\u00e5 intressanta funktioner. F\u00f6r det f\u00f6rsta - den h\u00e5ller all information om hela kodbasen i minnet. Detta inneb\u00e4r att mypy inte beh\u00f6ver ladda cachad data som r\u00f6r tusentals importerade beroenden vid varje k\u00f6rning. F\u00f6r det andra - den analyserar noggrant, p\u00e5 en detaljerad strukturell niv\u00e5, relationerna mellan funktioner och andra entiteter. Till exempel, om funktionen <code>foo<\/code> anropar funktionen <code>bar<\/code>, finns det ett beroende <code>foo<\/code> fr\u00e5n <code>bar<\/code>. N\u00e4r en fil \u00e4ndras, bearbetar demonen f\u00f6rst, i isolering, bara den \u00e4ndrade filen. D\u00e4refter tittar den p\u00e5 f\u00f6r\u00e4ndringarna i denna fil som \u00e4r synliga utanf\u00f6r, p\u00e5 s\u00e5dant som \u00e4ndrade funktionssignaturer. Demonen anv\u00e4nder detaljerad information om importer endast f\u00f6r att dubbelkolla de funktioner som verkligen anv\u00e4nder den \u00e4ndrade funktionen. Vanligtvis beh\u00f6ver man kontrollera ganska f\u00e5 funktioner med denna metod.<\/p>\n<p>Genomf\u00f6randet av allt detta var ingen l\u00e4tt uppgift, eftersom den ursprungliga implementationen av mypy var starkt inriktad p\u00e5 att bearbeta en fil i taget. Vi st\u00f6tte p\u00e5 m\u00e5nga gr\u00e4nsytor d\u00e4r det var n\u00f6dv\u00e4ndigt med upprepade kontroller n\u00e4r n\u00e5got f\u00f6r\u00e4ndrades i koden. Till exempel h\u00e4nder detta n\u00e4r en klass f\u00e5r en ny basklass. Efter att vi gjort det vi ville kunde vi minska tiden f\u00f6r de flesta inkrementella kontroller till n\u00e5gra sekunder. Detta k\u00e4ndes som en stor seger f\u00f6r oss.<\/p>\n<h2>\u00c4nnu mer prestanda!<\/h2>\n<p>\nTillsammans med den fj\u00e4rrcache som jag n\u00e4mnde tidigare har mypy-demonen i stort sett helt l\u00f6st problemen som uppst\u00e5r n\u00e4r en programmerare ofta k\u00f6r typkontroller medan de g\u00f6r \u00e4ndringar i ett f\u00e5tal filer. Dock var systemets prestanda i det minst gynnsamma anv\u00e4ndningsscenariot fortfarande l\u00e5ngt ifr\u00e5n optimal. En ren k\u00f6rning av mypy kunde ta \u00f6ver 15 minuter. Detta var l\u00e5ngt mer \u00e4n vad vi skulle vara n\u00f6jda med. Varje vecka blev situationen v\u00e4rre, eftersom programmerarna fortsatte att skriva ny kod och l\u00e4gga till anteckningar till den befintliga koden. V\u00e5ra anv\u00e4ndare l\u00e4ngtade fortfarande efter mer prestanda, och vi var mer \u00e4n villiga att m\u00f6ta deras f\u00f6rv\u00e4ntningar.<\/p>\n<p>Vi best\u00e4mde oss f\u00f6r att \u00e5terv\u00e4nda till en av de tidiga id\u00e9erna om mypy. N\u00e4rmare best\u00e4mt att konvertera Python-kod till C-kod. Experiment med Cython (ett system som g\u00f6r det m\u00f6jligt att \u00f6vers\u00e4tta kod skriven i Python till C-kod) gav oss ingen synlig prestandaf\u00f6rb\u00e4ttring, s\u00e5 vi beslutade oss f\u00f6r att v\u00e4cka id\u00e9n om att skriva en egen kompilator. Eftersom mypy:s kodbas (skriven i Python) redan inneh\u00f6ll alla n\u00f6dv\u00e4ndiga typannoteringar, verkade det v\u00e4rt att f\u00f6rs\u00f6ka anv\u00e4nda dessa annoteringar f\u00f6r att snabba upp systemets arbete. Jag skapade snabbt en prototyp f\u00f6r att testa denna id\u00e9. Den visade mer \u00e4n tiofaldig prestanda p\u00e5 olika mikrob\u00e4nkar. V\u00e5r id\u00e9 var att kompilera Python-moduler till C-moduler med hj\u00e4lp av Cython, och att omvandla typannoteringar till typkontroller som utf\u00f6rs under programmets k\u00f6rning (vanligtvis ignoreras typannoteringar vid k\u00f6rning och anv\u00e4nds endast av typkontrolleringssystem). Vi planerade faktiskt att \u00f6vers\u00e4tta implementeringen av mypy fr\u00e5n Python till ett spr\u00e5k som var statiskt typat och som skulle se ut (och i de flesta fall fungera) precis som Python. (Denna typ av spr\u00e5k\u00f6verg\u00e5ng har blivit n\u00e5got av en tradition f\u00f6r mypy-projektet. Den ursprungliga implementeringen av mypy skrevs i Alore, f\u00f6ljd av en syntaxhybrid mellan Java och Python).<\/p>\n<p>Fokus p\u00e5 CPython:s API:er var nyckeln till att inte f\u00f6rlora m\u00f6jligheter till projektledning. Vi beh\u00f6vde inte implementera en virtuell maskin eller n\u00e5gra bibliotek som mypy beh\u00f6vde. Dessutom skulle vi fortfarande ha tillg\u00e5ng till hela Python-ekosystemet, inklusive alla verktyg (som pytest). Detta innebar att vi skulle kunna forts\u00e4tta anv\u00e4nda tolkad Python-kod under utvecklingen, vilket skulle till\u00e5ta oss att arbeta med en mycket snabb kod\u00e4ndrings- och testningscykel, ist\u00e4llet f\u00f6r att v\u00e4nta p\u00e5 kodkompilering. Det k\u00e4ndes som att vi framg\u00e5ngsrikt kunde sitta p\u00e5 tv\u00e5 stolar, och vi gillade det.<\/p>\n<p>Kompilatoren som vi kallade mypyc (eftersom den anv\u00e4nder mypy f\u00f6r typanalys som frontend) har visat sig vara ett ganska framg\u00e5ngsrikt projekt. Totalt sett har vi uppn\u00e5tt ungef\u00e4r en 4-faldig \u00f6kning av hastigheten vid frekventa k\u00f6rningar av mypy utan att anv\u00e4nda cache. Utvecklingen av k\u00e4rnan i mypyc tog en liten grupp, best\u00e5ende av Michael Sullivan, Ivan Levkivskyi, Hugh Han och jag, cirka 4 kalender m\u00e5nader. Denna arbetsinsats var mycket mindre omfattande \u00e4n vad som skulle kr\u00e4vas f\u00f6r att skriva om mypy, till exempel i C++ eller Go. Och vi beh\u00f6vde ocks\u00e5 g\u00f6ra betydligt f\u00e4rre f\u00f6r\u00e4ndringar i projektet \u00e4n vi skulle ha beh\u00f6vt om vi skrivit om det p\u00e5 ett annat spr\u00e5k. Dessutom hoppades vi att vi skulle kunna ta mypyc till en niv\u00e5 d\u00e4r andra programmerare fr\u00e5n Dropbox skulle kunna anv\u00e4nda det f\u00f6r att kompilera och p\u00e5skynda sin kod.<\/p>\n<p>F\u00f6r att uppn\u00e5 en s\u00e5dan prestandaniv\u00e5 var vi tvungna att till\u00e4mpa n\u00e5gra intressanta ingenj\u00f6rsl\u00f6sningar. Kompilatorn kan p\u00e5skynda utf\u00f6randet av m\u00e5nga operationer genom att anv\u00e4nda snabba l\u00e5g niv\u00e5 C-konstruktioner. Till exempel \u00f6vers\u00e4tts ett anrop av en kompilerad funktion till ett anrop av en C-funktion. Och ett s\u00e5dant anrop utf\u00f6rs mycket snabbare \u00e4n ett anrop av en tolkad funktion. Vissa operationer, som s\u00f6kningar i ordb\u00f6cker, \u00e5tergick fortfarande till att anv\u00e4nda vanliga anrop av C-API fr\u00e5n CPython, vilka efter kompileringen bara var n\u00e5got snabbare. Vi lyckades eliminera den extra belastningen p\u00e5 systemet som skapades av tolkning, men detta gav endast en liten prestandaf\u00f6rb\u00e4ttring i det h\u00e4r fallet.<\/p>\n<p>F\u00f6r att identifiera de mest f\u00f6rekommande \"l\u00e5ngsamma\" operationerna utf\u00f6rde vi en profilering av koden. Med de insamlade uppgifterna f\u00f6rs\u00f6kte vi antingen justera mypyc f\u00f6r att generera snabbare C-kod f\u00f6r s\u00e5dana operationer, eller skriva om motsvarande Python-kod med hj\u00e4lp av snabbare operationer (ibland hade vi helt enkelt inte n\u00e5gon enkel l\u00f6sning f\u00f6r n\u00e5got problem). Att skriva om Python-koden visade sig ofta vara en enklare l\u00f6sning p\u00e5 problemet \u00e4n att implementera en automatisk utf\u00f6rande av samma transformation i kompileraren. P\u00e5 l\u00e5ng sikt ville vi automatisera m\u00e5nga av dessa transformationer, men f\u00f6r tillf\u00e4llet var vi fokuserade p\u00e5 att with minimal effort accelerera mypy. Med den ambitionen skar vi n\u00e5gra h\u00f6rn.<\/p>\n<p>Forts\u00e4ttning f\u00f6ljer\u2026<\/p>\n<p><b>K\u00e4ra l\u00e4sare!<\/b> Vilka intryck fick projektet mypy n\u00e4r du f\u00f6rst fick k\u00e4nnedom om dess existens?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"V\u00e4gen till typkontroll av 4 miljoner rader Python-kod. 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=\"V\u00e4gen till typkontroll av 4 miljoner rader Python-kod. Del 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>K\u00e4lla: <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\/sv\/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=\"sv_SE\" \/>\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\/sv\/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\udd47V\u00e4gen till typkontroll av 4 miljoner rader Python-kod. Del 2 | ProHoster","description":"Idag publicerar vi den andra delen av \u00f6vers\u00e4ttningen av materialet om hur Dropbox organiserade typkontroll f\u00f6r flera miljoner rader Python-kod.","canonical_url":"https:\/\/prohoster.info\/sv\/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":"sv_SE","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\/sv\/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\/sv\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}