{"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\/da\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Vejen til typekontrol af 4 millioner linjer Python-kode. Del 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I dag offentligg\u00f8r vi den anden del af overs\u00e6ttelsen af materialet om, hvordan Dropbox organiserede typekontrol for flere millioner linjer Python-kode.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Vejen til typekontrol af 4 millioner linjer 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\/\">L\u00e6s f\u00f8rste del<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Officiel underst\u00f8ttelse af typer (PEP 484)<\/h2>\n<p>\nVi foretog de f\u00f8rste seri\u00f8se eksperimenter med mypy i Dropbox under Hack Week 2014. Hack Week er et arrangement, der afholdes af Dropbox i l\u00f8bet af en uge, hvor medarbejderne kan arbejde p\u00e5, hvad de \u00f8nsker! Nogle af de mest ber\u00f8mte teknologiprojekter i Dropbox startede netop ved s\u00e5danne arrangementer. Som resultat af dette eksperiment kom vi frem til, at mypy ser lovende ud, selvom dette projekt endnu ikke var klart til bred anvendelse.<\/p>\n<p>P\u00e5 det tidspunkt luftede man ideen om at standardisere systemer til typehinting i Python. Som jeg allerede har n\u00e6vnt, kunne man fra Python 3.0 bruge typeannoteringer til funktioner, men de var kun arbitr\u00e6re udtryk uden specifik syntaks og semantik. Under programudf\u00f8relsen blev disse annoteringer for det meste blot ignoreret. Efter Hack Week begyndte vi at arbejde p\u00e5 at standardisere semantikken. Dette arbejde f\u00f8rte til <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (dokumentet er blevet udarbejdet i samarbejde med Guido van Rossum, Luk\u00e1\u0161 Lal\u00edk og mig).<\/p>\n<p>Vores motiver kunne ses fra to sider. For det f\u00f8rste h\u00e5bede vi, at hele Python-\u00f8kosystemet kunne adoptere en f\u00e6lles tilgang til brug af type hints (type hints - et udtryk, der bruges i Python som en analog til \"typeannoteringer\"). Dette ville, i betragtning af de mulige risici, v\u00e6re bedre end at bruge mange inkompatible tilgange. For det andet \u00f8nskede vi at diskutere mekanismerne bag typeannotering med mange repr\u00e6sentanter fra Python-samfundet. En del af dette \u00f8nske var drevet af, at vi ikke \u00f8nskede at fremst\u00e5 som \"afvigere\" fra sproglets grundprincipper i \u00f8jnene p\u00e5 de mange Python-udviklere. Dette er et dynamisk typet sprog, kendt for \"andetype-typering\". I samfundet kunne der i starten ikke undg\u00e5s at opst\u00e5 en vis mistro til ideen om statisk typet. Men denne indstilling svandt ind, efterh\u00e5nden som det blev klart, at statisk typet ikke ville blive gjort obligatorisk (og efter at folk inds\u00e5, at det faktisk er nyttigt).<\/p>\n<p>Den vedtagne syntaks for type hints lignede meget den, som mypy p\u00e5 det tidspunkt underst\u00f8ttede. PEP 484-dokumentet blev frigivet sammen med Python 3.5 i 2015. Python var ikke l\u00e6ngere bare et sprog med dynamisk typetydighed. Jeg kan godt lide at t\u00e6nke p\u00e5 denne begivenhed som en vigtig milep\u00e6l i Pythons historie.<\/p>\n<h2>Starten p\u00e5 migrationen<\/h2>\n<p>\nI slutningen af 2015 blev der i Dropbox oprettet et team best\u00e5ende af tre personer for at arbejde p\u00e5 mypy. Teamet bestod af Guido van Rossum, Greg Price og David Fisher. Fra det tidspunkt begyndte situationen at udvikle sig meget hurtigt. Det f\u00f8rste forhindring for mypys v\u00e6kst var ydeevnen. Som jeg tidligere n\u00e6vnte, overvejede jeg i den tidlige fase af projektet at omkode mypy i C, men denne id\u00e9 blev midlertidigt str\u00f8get. Vi var fanget i den situation, at systemet k\u00f8rte p\u00e5 CPython, som ikke tilb\u00f8d den hastighed, der var tilstr\u00e6kkelig for v\u00e6rkt\u00f8jer som mypy. (Projektet PyPy, en alternativ implementering af Python med JIT-kompilator, hjalp os heller ikke.)<\/p>\n<p>Heldigvis kom der nogle algorithmiske forbedringer til hj\u00e6lp. Den f\u00f8rste kraftige \"accelerator\" var implementeringen af inkrementel kontrol. Ideen bag denne forbedring var enkel: hvis alle moduls afh\u00e6ngigheder ikke havde \u00e6ndret sig siden den sidste k\u00f8rsel af mypy, kunne vi bruge de data, der blev cachet under den tidligere session, mens vi arbejdede med afh\u00e6ngighederne. Vi skulle kun udf\u00f8re typekontrol i de \u00e6ndrede filer og i de filer, der var afh\u00e6ngige af dem. Mypy gik endda et skridt videre: hvis et mods\u00e6ndt interface for modulet ikke \u00e6ndrede sig, ans\u00e5 mypy, at andre moduler, der importerede dette modul, ikke skulle kontrolleres igen.<\/p>\n<p>Den inkrementelle kontrol hjalp os betydeligt, da vi annoterede store m\u00e6ngder eksisterende kode. Processen involverede typisk mange iterative k\u00f8rsel af mypy, da annotationer blev tilf\u00f8jet til koden gradvist. Den f\u00f8rste k\u00f8rsel af mypy var stadig meget langsom, da dens udf\u00f8relse kr\u00e6vede kontrol af mange afh\u00e6ngigheder. For at forbedre situationen implementerede vi en mekanisme til fjerncache. Hvis mypy opdager, at den lokale cache sandsynligvis er for\u00e6ldet, henter den det aktuelle cache-snapshot for hele kodebasen fra et centraliseret repository. Derefter udf\u00f8rer den, ved hj\u00e6lp af dette snapshot, en inkrementel kontrol. Dette har betydet endnu et stort skridt fremad i at \u00f8ge mypys ydeevne.<\/p>\n<p>Det var en periode med hurtig og naturlig implementering af typekontrolsystemet i Dropbox. Ved udgangen af 2016 havde vi allerede omkring 420.000 linjer Python-kode med typeannotationer. Mange brugere tog entusiastisk imod typekontrol. I Dropbox blev mypy brugt af stadig flere udviklingsteams.<\/p>\n<p>Alt s\u00e5 godt ud dengang, men der var stadig meget, der skulle g\u00f8res. Vi begyndte at gennemf\u00f8re periodiske interne brugerunders\u00f8gelser for at identificere problemomr\u00e5der i projektet og forst\u00e5, hvilke sp\u00f8rgsm\u00e5l der skulle l\u00f8ses f\u00f8rst (denne praksis anvendes stadig i virksomheden i dag). Det blev klart, at de vigtigste opgaver var to. Den f\u00f8rste var et st\u00f8rre d\u00e6kning af koden med typer, den anden var, at mypy skulle arbejde hurtigere. Det var helt tydeligt, at vores arbejde med at fremskynde mypy og implementere det i virksomhedens projekter stadig var langt fra fuldf\u00f8rt. Vi, fuldt ud bevidste om vigtigheden af disse to opgaver, gik i gang med at l\u00f8se dem.<\/p>\n<h2>Mere ydeevne!<\/h2>\n<p>\nInkrementelle kontroller har accelereret mypy, men dette v\u00e6rkt\u00f8j var stadig ikke hurtigt nok. Mange inkrementelle kontroller tog omkring et minut. \u00c5rsagen til dette var cykliske imports. Det vil sandsynligvis ikke overraske dem, der har arbejdet med store kodebaser skrevet i Python. Vi havde s\u00e6t af hundreder af moduler, hver af dem indirekte importerede alle de andre. Hvis en fil i en importcyklus blev \u00e6ndret, m\u00e5tte mypy behandle alle filer i denne cyklus, og ofte ogs\u00e5 eventuelle moduler, der importerede moduler fra denne cyklus. En af disse cykler var den berygtede \"afh\u00e6ngighedsknude\", som for\u00e5rsagede mange problemer i Dropbox. En gang indeholdt denne struktur flere hundrede moduler, som blev importeret, direkte eller indirekte, af mange tests, og den blev ogs\u00e5 brugt i produktionskoden.<\/p>\n<p>Vi overvejede muligheden for at \"udrede\" cykliske afh\u00e6ngigheder, men vi havde ikke ressourcerne til at g\u00f8re det. Der var for meget kode, vi ikke var bekendte med. I sidste ende bev\u00e6gede vi os mod en alternativ tilgang. Vi besluttede at g\u00f8re s\u00e5dan, at mypy ville arbejde hurtigt selv i n\u00e6rv\u00e6r af \"afh\u00e6ngighedsknuder\". Vi opn\u00e5ede dette m\u00e5l ved hj\u00e6lp af mypy-d\u00e6monen. D\u00e6monen er en serverproces, der implementerer to interessante funktioner. For det f\u00f8rste holder den information om hele kodebasen i hukommelsen. Dette betyder, at mypy ikke beh\u00f8ver at indl\u00e6se cachede data, der vedr\u00f8rer tusinder af importerede afh\u00e6ngigheder, ved hver k\u00f8rsel. For det andet analyserer den n\u00f8je, p\u00e5 niveau med sm\u00e5 strukturelle enheder, afh\u00e6ngigheder mellem funktioner og andre enheder. For eksempel, hvis funktionen <code>foo<\/code> kalder funktionen <code>bar<\/code>, s\u00e5 er der en afh\u00e6ngighed <code>foo<\/code> fra <code>bar<\/code>. N\u00e5r en fil \u00e6ndres, behandler d\u00e6monen f\u00f8rst, isoleret, kun den \u00e6ndrede fil. Derefter ser den p\u00e5 \u00e6ndringerne i denne fil, der er synlige udefra, s\u00e5som \u00e6ndrede funktionssignaturer. D\u00e6monen bruger detaljeret information om imports kun til at genkontrollere de funktioner, der virkelig bruger den \u00e6ndrede funktion. Normalt skal der kun kontrolleres meget f\u00e5 funktioner ved en s\u00e5dan tilgang.<\/p>\n<p>Implementeringen af alt dette var ingen nem opgave, da den oprindelige implementering af mypy var meget fokuseret p\u00e5 at bearbejde \u00e9n fil ad gangen. Vi m\u00e5tte st\u00e5 over for mange gr\u00e6nsetilf\u00e6lde, som kr\u00e6vede gentagne kontroller, n\u00e5r der blev foretaget \u00e6ndringer i koden. For eksempel sker dette, n\u00e5r en klasse tildeles en ny baseklasse. Efter at vi havde lavet det \u00f8nskede, kunne vi reducere k\u00f8rselstiden for de fleste inkrementelle kontroller til kun et par sekunder. Dette f\u00f8ltes som en stor sejr for os.<\/p>\n<h2>Endnu mere ydeevne!<\/h2>\n<p>\nSammen med det fjerncache, jeg n\u00e6vnte ovenfor, har mypy-daemonen n\u00e6sten helt l\u00f8st de problemer, der opst\u00e5r, n\u00e5r en programmerer ofte k\u00f8rer typekontrol, mens der kun foretages \u00e6ndringer i et lille antal filer. Dog var systemets ydeevne i den mindst gunstige anvendelse stadig langt fra optimal. Et rent k\u00f8rsel af mypy kunne tage mere end 15 minutter. Og det var meget mere, end hvad vi ville have v\u00e6ret tilfredse med. Hver uge blev situationen v\u00e6rre, da programmerne fortsatte med at skrive ny kode og tilf\u00f8je annotationer til eksisterende kode. Vores brugere t\u00f8rstede stadig efter mere ydeevne, og vi var glade for at im\u00f8dekomme deres \u00f8nsker.<\/p>\n<p>Vi besluttede at vende tilbage til en af de tidlige ideer omkring mypy. Nemlig at konvertere Python-kode til C-kode. Eksperimenter med Cython (et system, der tillader overs\u00e6ttelse af kode skrevet i Python til C-kode) gav os ikke synlige hastighedsforbedringer, s\u00e5 vi besluttede at genoplive ideen om at skrive vores egen compiler. Da mypy's kodebase (skrevet i Python) allerede indeholdt alle n\u00f8dvendige typeannoteringer, syntes det som en v\u00e6rdifuld indsats at bruge disse annoteringer til at forbedre systemets ydelse. Jeg lavede hurtigt en prototype for at teste denne id\u00e9. Den viste mere end 10 gange bedre ydeevne i forskellige mikro-benchmark tests. Vores id\u00e9 var at kompilere Python-moduler til C-moduler ved hj\u00e6lp af Cython, og at oms\u00e6tte typeannoteringer til typekontroller, der udf\u00f8res under programudf\u00f8relsen (normalt ignoreres typeannoteringerne under programudf\u00f8relsen og bruges kun af typekontrolsystemer). Vi planlagde faktisk at oms\u00e6tte implementeringen af mypy fra Python til et sprog, der var statisk typet, og som ville se ud (og for det meste fungere) pr\u00e6cis som Python. (Denne form for mellem sproglig migration er blevet noget af en tradition for mypy-projektet. Den oprindelige implementering af mypy var skrevet i Alore, derefter blev der udviklet en syntaktisk hybrid af Java og Python).<\/p>\n<p>Fokus p\u00e5 CPython-extensions API'en var n\u00f8glen til ikke at miste mulighederne for projektstyring. Vi beh\u00f8vede ikke at implementere en virtuel maskine eller nogen biblioteker, som mypy havde brug for. Desuden ville hele Python-\u00f8kosystemet stadig v\u00e6re tilg\u00e6ngeligt, inklusiv alle v\u00e6rkt\u00f8jer (s\u00e5som pytest). Det bet\u00f8d, at vi kunne forts\u00e6tte med at bruge tolked Python-kode under udviklingen, hvilket ville give os mulighed for at forts\u00e6tte med meget hurtige \u00e6ndringer i koden og dens testning i stedet for at vente p\u00e5 kompilering af koden. Det f\u00f8ltes, som om vi klarerede os rigtig godt, s\u00e5 at sige at sidde p\u00e5 to stole, og det kunne vi lide.<\/p>\n<p>Compilatoren, som vi kaldte mypyc (da den bruger mypy til typeanalyse i frontenden), viste sig at v\u00e6re et meget succesfuldt projekt. Generelt har vi opn\u00e5et cirka firedobbelt hastighedsfor\u00f8gelse af hyppige mypy-k\u00f8rsler uden at benytte caching. Udviklingen af kernen i mypyc tog et lille team, som bestod af Michael Sullivan, Ivan Levkiwsky, Hugh Han, og mig, cirka 4 kalender m\u00e5neder. Dette arbejdsm\u00e6ngde var langt mindre omfattende end det, der ville have v\u00e6ret n\u00f8dvendigt for at omskrive mypy, f.eks. til C++ eller Go. Derudover m\u00e5tte vi lave langt f\u00e6rre \u00e6ndringer i projektet end vi ville have skulle lave, hvis det blev omskrevet p\u00e5 et andet sprog. Vi h\u00e5bede ogs\u00e5, at vi kunne bringe mypyc op p\u00e5 et niveau, s\u00e5 andre programm\u00f8rer fra Dropbox kunne bruge det til at kompilere og accelerere deres kode.<\/p>\n<p>For at opn\u00e5 et s\u00e5dant niveau af ydeevne m\u00e5tte vi anvende nogle interessante ingeni\u00f8rl\u00f8sninger. Kompilatoren kan accelerere udf\u00f8relsen af mange operationer ved at bruge hurtige, lave niveauer C-konstruktioner. For eksempel overs\u00e6ttes kaldet til den kompilerede funktion til et C-funktionsanrop. Og et s\u00e5dant kald udf\u00f8res meget hurtigere end et kald af en fortolket funktion. Nogle operationer, s\u00e5som opkald til ordb\u00f8ger, involverede stadig almindelige kald til C-API fra CPython, som efter kompilering kun blev lidt hurtigere. Vi var i stand til at fjerne den ekstra belastning p\u00e5 systemet, der blev skabt af fortolkningen, men dette gav kun en lille gevinst i ydeevnen.<\/p>\n<p>For identifying the most common 'slow' operations, we performed code profiling. Armed with the data obtained, we attempted to either tweak mypyc so that it generated faster C code for such operations, or rewrite the corresponding Python code using faster operations (and sometimes we simply did not have a straightforward solution to a particular problem). Rewriting the Python code often turned out to be a simpler solution to the problem than implementing the same transformation automatically in the compiler. In the long term, we wanted to automate many of these transformations, but at that moment we focused on speeding up mypy with minimal effort. And, moving toward this goal, we cut a few corners.<\/p>\n<p>Forts\u00e6tter\u2026<\/p>\n<p><b>K\u00e6re l\u00e6sere!<\/b> What were your impressions of the mypy project when you first learned about its existence?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Vejen til typekontrol af 4 millioner linjer 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=\"Vejen til typekontrol af 4 millioner linjer Python-kode. Del 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Kilde: <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.3 - 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\/da\/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.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\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\/da\/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\udd47The Path to Type Checking 4 Million Lines of Python Code. Part 2 | ProHoster","description":"I dag offentligg\u00f8r vi den anden del af overs\u00e6ttelsen af materialet om, hvordan Dropbox organiserede typekontrol for flere millioner linjer Python-kode.","canonical_url":"https:\/\/prohoster.info\/da\/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":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}