{"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\/no\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Veien til \u00e5 teste typer 4 millioner linjer med Python-kode. Del 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>I dag publiserer vi den andre delen av oversettelsen av materialet om hvordan Dropbox organiserte typetilgang for flere millioner linjer med Python-kode.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Veien til \u00e5 teste typer 4 millioner linjer med 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\/\">Les f\u00f8rste del<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Offisiell st\u00f8tte for typer (PEP 484)<\/h2>\n<p>\nVi gjennomf\u00f8rte de f\u00f8rste alvorlige eksperimentene med mypy i Dropbox under Hack Week 2014. Hack Week er et arrangement som Dropbox holder i l\u00f8pet av en uke. I l\u00f8pet av denne tiden kan ansatte jobbe med hva som helst! Noen av de mest kjente teknologiprosjektene i Dropbox startet nettopp under slike arrangementer. Som et resultat av dette eksperimentet konkluderte vi med at mypy virker lovende, selv om dette prosjektet ikke var klart for bred bruk.<\/p>\n<p>P\u00e5 den tiden sirkulerte ideen om \u00e5 standardisere systemer for typehinting i Python. Som jeg allerede har nevnt, fra og med Python 3.0 kunne man bruke typeannotasjoner for funksjoner, men disse var bare tilfeldige uttrykk, uten spesifikk syntaks og semantikk. Under kj\u00f8ring av programmet ble disse annotasjonene stort sett ignorert. Etter Hack Week begynte vi arbeidet med \u00e5 standardisere semantikken. Dette arbeidet ledet til fremveksten av <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (dette dokumentet ble utviklet i samarbeid mellom Guido van Rossum, \u0141ukasz Langa og meg).<\/p>\n<p>V\u00e5re motiver kan sees fra to sider. For det f\u00f8rste h\u00e5pet vi at hele Python-\u00f8kosystemet ville kunne adoptere en felles tiln\u00e6rming til bruk av typehinting. Dette ville, med tanke p\u00e5 mulige risikoer, v\u00e6rt bedre enn \u00e5 bruke mange gjensidig uforenlige metoder. For det andre \u00f8nsket vi \u00e5 diskutere mekanismene for typeannotasjon \u00e5pent med mange representanter fra Python-samfunnet. Dels var dette \u00f8nsket drevet av at vi ikke \u00f8nsket \u00e5 fremst\u00e5 som \"fravikere\" fra de grunnleggende ideene i spr\u00e5ket i \u00f8ynene til et bredt publikum av Python-utviklere. Dette er et dynamisk typet spr\u00e5k kjent for sin \"anda-typet\". I samfunnet, i starten, kunne man ikke unng\u00e5 en viss mistro til ideen om statisk typisering. Men denne holdningen ble til slutt svekket \u2014 etter at det ble klart at statisk typisering ikke var ment \u00e5 bli obligatorisk (og etter at folk forsto at den faktisk er nyttig).<\/p>\n<p>Det vedtatte syntaksen for type hinting lignet veldig p\u00e5 det som mypy st\u00f8tte p\u00e5 den tiden. PEP 484-dokumentet ble lansert sammen med Python 3.5 i 2015. Python var ikke lenger et spr\u00e5k som kun st\u00f8ttet dynamisk typet i \u00e5 f\u00f8lge opp. Jeg liker \u00e5 tenke p\u00e5 dette som en betydelig milep\u00e6l i Python-historien.<\/p>\n<h2>Starten p\u00e5 migrasjonen<\/h2>\n<p>\nP\u00e5 slutten av 2015 ble det opprettet et team p\u00e5 tre personer i Dropbox for \u00e5 arbeide med mypy. Teamet besto av Guido van Rossum, Greg Price og David Fisher. Fra dette punktet begynte situasjonen \u00e5 utvikle seg ekstremt raskt. Det f\u00f8rste hinderet for veksten av mypy var ytelsen. Som jeg antydet tidligere, vurderte jeg i den tidlige utviklingen av prosjektet \u00e5 oversette implementeringen av mypy til C, men denne ideen ble midlertidig str\u00f8ket fra listen. Vi var l\u00e5st til at systemet ble kj\u00f8rt med CPython-tolkeren, som ikke hadde hastigheten som var tilstrekkelig for verkt\u00f8y som mypy. (PyPy-prosjektet, en alternativ implementering av Python med JIT-kompilator, hjalp oss heller ikke.)<\/p>\n<p>Heldigvis fikk vi hjelp av noen algoritmiske forbedringer. Den f\u00f8rste kraftige \u2018akseleratoren\u2019 var implementeringen av inkrementell sjekking. Ideen bak denne forbedringen var enkel: hvis alle avhengighetene til modulet har v\u00e6rt uendret siden forrige kj\u00f8ring av mypy, s\u00e5 kan vi bruke data som ble hurtigbufret under forrige \u00f8kt n\u00e5r vi jobber med avhengighetene. Vi m\u00e5tte bare utf\u00f8re typekontroll p\u00e5 endrede filer og de filene som avhang av dem. Mypy gikk til og med et skritt videre: hvis det eksterne grensesnittet til modulen ikke hadde endret seg \u2014 antok mypy at andre moduler som importerte denne modulen, ikke m\u00e5tte kontrolleres p\u00e5 nytt.<\/p>\n<p>Inkrementell kontroll har hjulpet oss mye med \u00e5 annotere store mengder eksisterende kode. Prosessen inneb\u00e6rer vanligvis mange iterasjoner av mypy, ettersom annotasjonene legges til gradvis og forbedres over tid. Den f\u00f8rste kj\u00f8ringen av mypy var fortsatt veldig treg, da den m\u00e5tte verifisere et stort antall avhengigheter. For \u00e5 forbedre situasjonen, implementerte vi en mekanisme for fjernlagring. Hvis mypy oppdager at den lokale cachen sannsynligvis er foreldet, laster den opp et n\u00e5v\u00e6rende snapshot av cachen for hele kodebasen fra et sentralisert repository. Deretter utf\u00f8rer den, med dette snapshotet, den inkrementelle kontrollen. Dette har v\u00e6rt et betydelig steg fremover for \u00e5 \u00f8ke ytelsen til mypy.<\/p>\n<p>Dette var en periode med rask og naturlig implementering av typesystemet i Dropbox. Ved slutten av 2016 hadde vi allerede n\u00e6rmere 420000 linjer med Python-kode med typeannotasjoner. Mange brukere mottok typesjekken med entusiasme. Mypy ble brukt av stadig flere utviklerteam i Dropbox.<\/p>\n<p>Alt s\u00e5 bra ut da, men vi hadde fortsatt mye arbeid foran oss. Vi begynte \u00e5 gjennomf\u00f8re periodiske interne unders\u00f8kelser blandt brukerne for \u00e5 identifisere problemomr\u00e5der i prosjektet og for \u00e5 forst\u00e5 hvilke sp\u00f8rsm\u00e5l som m\u00e5tte l\u00f8ses f\u00f8rst (denne praksisen brukes fortsatt i selskapet i dag). De to mest kritiske oppgavene, som ble klare, var at vi trengte bedre dekningsgrad av koden med typer, og at mypy m\u00e5tte fungere raskere. Det var helt klart at arbeidet v\u00e5rt med \u00e5 forbedre hastigheten p\u00e5 mypy og implementeringen i selskapets prosjekter fortsatt var langt fra fullf\u00f8rt. Med full forst\u00e5else for viktigheten av disse to oppgavene, satte vi i gang med \u00e5 l\u00f8se dem.<\/p>\n<h2>Mer ytelse!<\/h2>\n<p>\nInkrementelle sjekker har akselerert mypy, men dette verkt\u00f8yet har fortsatt ikke v\u00e6rt raskt nok. Mange inkrementelle sjekker varte i omtrent ett minutt. \u00c5rsaken til dette var sirkul\u00e6re importeringer. Dette vil sannsynligvis ikke overraske dem som har jobbet med store kodebaser skrevet i Python. Vi hadde sett med hundrevis av moduler, hver av dem importerte indirekte alle de andre. Hvis noen filer i importsyklusen ble endret, m\u00e5tte mypy behandle alle filer som inngikk i denne syklusen, og ofte ogs\u00e5 alle moduler som importerer moduler fra denne syklusen. En av disse syklusene var den beryktede 'avhengighetsknuten', som for\u00e5rsaket mange problemer i Dropbox. En gang inneholdt denne strukturen flere hundre moduler, og den ble importert, enten direkte eller indirekte, av mange tester, og den ble brukt i produksjonskoden.<\/p>\n<p>Vi vurderte muligheten for \u00e5 'l\u00f8se opp' de sirkul\u00e6re avhengighetene, men vi hadde ikke ressurser til \u00e5 gj\u00f8re det. Det var for mye kode vi ikke var kjent med. Til slutt kom vi til en alternativ tiln\u00e6rming. Vi bestemte oss for \u00e5 f\u00e5 mypy til \u00e5 fungere raskt selv med 'avhengighetsknuter'. Vi oppn\u00e5dde dette ved hjelp av mypy-demonen. Demonen er en serverprosess som implementerer to interessante funksjoner. For det f\u00f8rste lagrer den informasjon om hele kodebasen i minnet. Dette betyr at ved hver start av mypy trenger vi ikke \u00e5 laste inn cached data relatert til tusenvis av importerte avhengigheter. For det andre analyserer den n\u00f8ye, p\u00e5 niv\u00e5et av sm\u00e5 strukturenheter, avhengighetene mellom funksjoner og andre entiteter. For eksempel, hvis funksjonen <code>foo<\/code> kaller funksjonen <code>bar<\/code>, s\u00e5 er det en avhengighet <code>foo<\/code> fra <code>bar<\/code>. N\u00e5r en fil endres, behandler demon f\u00f8rst, isolert, bare den endrede filen. Deretter ser den p\u00e5 endringene i denne filen, synlige utenfra, som endrede funksjonssignaturer. Demonen bruker detaljert informasjon om importene kun for \u00e5 verifisere de funksjonene som faktisk bruker den endrede funksjonen. Vanligvis m\u00e5 man sjekke ganske f\u00e5 funksjoner med denne tiln\u00e6rmingen.<\/p>\n<p>Implementeringen av alt dette var ingen enkel oppgave, ettersom den opprinnelige implementeringen av mypy var sterkt fokusert p\u00e5 behandling av \u00e9n fil om gangen. Vi m\u00e5tte h\u00e5ndtere mange grensekaser som krevde gjentatt sjekking n\u00e5r noe ble endret i koden. For eksempel skjer dette n\u00e5r en klasse tildeles en ny baseklasse. Etter at vi hadde gjort det vi \u00f8nsket, klarte vi \u00e5 redusere kj\u00f8retiden for de fleste inkrementelle sjekker til et par sekunder. Vi s\u00e5 p\u00e5 dette som en stor seier.<\/p>\n<h2>Enda mer ytelse!<\/h2>\n<p>\nSammen med den eksterne hurtigbufringen jeg nevnte tidligere, l\u00f8ste mypy-demonstrasjonen praktisk talt problemene som oppsto n\u00e5r en programmerer ofte kj\u00f8rte typekontroll mens de gjorde endringer i et lite antall filer. Imidlertid var systemets ytelse i det minst gunstige bruksomr\u00e5det fortsatt langt fra optimal. En ren oppstart av mypy kunne ta mer enn 15 minutter. Dette var langt mer enn vi var komfortable med. Hver uke ble situasjonen verre ettersom programmerere fortsatte \u00e5 skrive ny kode og legge til annotasjoner til eksisterende kode. V\u00e5re brukere lengtet fortsatt etter bedre ytelse, og vi var glade for \u00e5 im\u00f8tekomme dem.<\/p>\n<p>Vi har besluttet \u00e5 g\u00e5 tilbake til en av de tidlige ideene om mypy. Nemlig konvertering av Python-kode til C-kode. Eksperimentene med Cython (dette er et system som lar oss oversette kode skrevet i Python til C-kode) ga oss ingen merkbar hastighetsforbedring, s\u00e5 vi bestemte oss for \u00e5 gjenopplive ideen om \u00e5 skrive v\u00e5r egen kompilator. Siden mypy-kodebasen (skrevet i Python) allerede hadde alle n\u00f8dvendige typeannotasjoner, virket det som et verdt fors\u00f8k \u00e5 bruke disse annotasjonene for \u00e5 \u00f8ke systemets ytelse. Jeg laget raskt en prototype for \u00e5 teste denne ideen. Den viste mer enn ti ganger ytelsesforbedring i forskjellige mikro-benchmarktester. V\u00e5r id\u00e9 var \u00e5 kompilere Python-moduler til C-moduler ved hjelp av Cython, og \u00e5 omdanne typeannotasjoner til typekontroller utf\u00f8rt under kj\u00f8ring av programmet (vanligvis blir typeannotasjoner ignorert under programkj\u00f8ring og brukes bare av typekontrollsystemer). Vi planla faktisk \u00e5 oversette mypy-implementeringen fra Python til et spr\u00e5k som var statisk typet, og som ville se ut (og for det meste fungere) akkurat som Python. (Denne typen mellomspr\u00e5klig migrasjon har blitt noe av en tradisjon i mypy-prosjektet. Den opprinnelige implementeringen av mypy ble skrevet i Alore, og deretter var det en syntaktisk hybrid av Java og Python).<\/p>\n<p>Fokuset p\u00e5 CPython-utvidelses-API-et var n\u00f8kkelen til at vi ikke mistet mulighetene for prosjektledelse. Vi trengte ikke \u00e5 implementere en virtuell maskin eller noen biblioteker som mypy trengte. I tillegg ville hele Python-\u00f8kosystemet fortsatt v\u00e6re tilgjengelig for oss, og alle verkt\u00f8yene (som pytest) ville v\u00e6re tilgjengelige. Dette betydde at vi kunne fortsette \u00e5 bruke den tolket Python-koden under utviklingen, noe som ville tillate oss \u00e5 jobbe med en veldig rask tilbakemeldingssl\u00f8yfe p\u00e5 kodeendringer og testing, i stedet for \u00e5 vente p\u00e5 kompilering av koden. Det s\u00e5 ut som vi klarte \u00e5 sitte p\u00e5 to stoler, s\u00e5 \u00e5 si, og vi likte det.<\/p>\n<p>Kompilatoren vi kalte mypyc (siden den bruker mypy til typanalyse som frontend) viste seg \u00e5 v\u00e6re et ganske vellykket prosjekt. Generelt sett oppn\u00e5dde vi omtrent en 4-dobling av hastigheten ved hyppige kj\u00f8ringer av mypy uten \u00e5 bruke caching. Utviklingen av kjernen i prosjektet mypyc tok en liten gruppe, best\u00e5ende av Michael Sullivan, Ivan Levkivskiy, Hugh Han og meg selv, omtrent 4 m\u00e5neder. Dette arbeidsomfanget var betydelig mindre enn det som ville v\u00e6rt n\u00f8dvendig for \u00e5 omskrive mypy, for eksempel til C++ eller Go. Og endringene vi m\u00e5tte gj\u00f8re i prosjektet var ogs\u00e5 langt f\u00e6rre enn de som ville v\u00e6rt n\u00f8dvendige ved omskrivning til et annet spr\u00e5k. I tillegg h\u00e5pet vi \u00e5 kunne utvikle mypyc til et niv\u00e5 der andre programmerere hos Dropbox kunne bruke det til \u00e5 kompilere og akselerere sin egen kode.<\/p>\n<p>For \u00e5 oppn\u00e5 et slikt ytelsesniv\u00e5 m\u00e5tte vi anvende noen interessante ingeni\u00f8rl\u00f8sninger. Kompilatoren kan for eksempel akselerere utf\u00f8relsen av mange operasjoner ved \u00e5 bruke raske lavniv\u00e5 C-konstruksjoner. For eksempel blir anropet til en kompilert funksjon oversatt til et anrop av en C-funksjon. Et slikt anrop utf\u00f8res mye raskere enn anropet til en tolket funksjon. Noen operasjoner, som ordboks\u00f8k, kom likevel ned til \u00e5 bruke vanlige C-API-anrop fra CPython, som etter kompilering viste seg \u00e5 v\u00e6re bare litt raskere. Vi klarte \u00e5 kvitte oss med den ekstra belastningen p\u00e5 systemet som ble for\u00e5rsaket av tolkning, men dette ga bare en liten gevinst i ytelse.<\/p>\n<p>For identifying the most common 'slow' operations, we performed code profiling. Armed with the data we obtained, we attempted either to 'tweak' mypyc so that it would generate faster C code for such operations, or to rewrite the corresponding Python code using faster operations (sometimes we simply did not have a straightforward solution for a particular problem). Rewriting the Python code often turned out to be a more straightforward solution than implementing an automatic execution of the same transformation in the compiler. In the long run, we wanted to automate many of these transformations, but at that moment we were focused on speeding up mypy with minimal effort. As we pursued this goal, we cut some corners.<\/p>\n<p>Fortsettelse f\u00f8lger...<\/p>\n<p><b>Kj\u00e6re lesere!<\/b> Hvilke inntrykk fikk du av mypy-prosjektet da du f\u00f8rst fikk vite om det?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Veien til \u00e5 teste typer 4 millioner linjer med 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=\"Veien til \u00e5 teste typer 4 millioner linjer med 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.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\/no\/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=\"nn_NO\" \/>\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\/no\/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\udd47Veien til typekontroll av 4 millioner linjer Python-kode. Del 2 | ProHoster","description":"I dag publiserer vi den andre delen av oversettelsen av materialet om hvordan Dropbox organiserte typetilgang for flere millioner linjer med Python-kode.","canonical_url":"https:\/\/prohoster.info\/no\/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":"nn_NO","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\/no\/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\/no\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/no\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}