{"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\/de\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Der Weg zur \u00dcberpr\u00fcfung von Typen in 4 Millionen Zeilen Python-Code. Teil 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Heute ver\u00f6ffentlichen wir den zweiten Teil der \u00dcbersetzung des Materials dar\u00fcber, wie bei Dropbox die Typkontrolle f\u00fcr mehrere Millionen Zeilen Python-Code organisiert wurde.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Der Weg zur \u00dcberpr\u00fcfung von Typen in 4 Millionen Zeilen Python-Code. Teil 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\/\">Ersten Teil lesen<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Offizielle Typunterst\u00fctzung (PEP 484)<\/h2>\n<p>\nWir haben w\u00e4hrend der Hack Week 2014 die ersten ernsthaften Experimente mit mypy bei Dropbox durchgef\u00fchrt. Die Hack Week ist eine Veranstaltung, die Dropbox eine Woche lang durchf\u00fchrt. In dieser Zeit k\u00f6nnen die Mitarbeiter an allem arbeiten, was sie m\u00f6chten! Einige der ber\u00fchmtesten technischen Projekte von Dropbox begannen an solchen Veranstaltungen. Aus diesen Experimenten haben wir die Erkenntnis gewonnen, dass mypy vielversprechend aussieht, obwohl dieses Projekt noch nicht f\u00fcr den breiten Einsatz bereit war.<\/p>\n<p>Zu dieser Zeit schwebte die Idee, die Systeme zur Typunterst\u00fctzung in Python zu standardisieren. Wie ich bereits erw\u00e4hnt habe, konnten mit Python 3.0 Typannotations f\u00fcr Funktionen verwendet werden, aber das waren nur willk\u00fcrliche Ausdr\u00fccke, ohne definierte Syntax und Semantik. W\u00e4hrend der Programmausf\u00fchrung wurden diese Anmerkungen gr\u00f6\u00dftenteils einfach ignoriert. Nach der Hack Week haben wir an der Standardisierung der Semantik gearbeitet. Diese Arbeit f\u00fchrte zur Entstehung von <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (dieses Dokument wurde gemeinsam von Guido van Rossum, \u0141ukasz Langa und mir erstellt.)<\/p>\n<p>Unsere Motive konnten aus zwei Perspektiven betrachtet werden. Erstens hofften wir, dass das gesamte Python-\u00d6kosystem einen gemeinsamen Ansatz f\u00fcr die Verwendung von Typ-Hinweisen (type hints \u2013 ein in Python verwendeter Begriff, der als \u00c4quivalent zu \"Typannotationen\" fungiert) annehmen k\u00f6nnte. Dies w\u00e4re, unter Ber\u00fccksichtigung der m\u00f6glichen Risiken, besser als die Verwendung vieler inkompatibler Ans\u00e4tze. Zweitens wollten wir die Mechanismen zur Typannotierung offen mit vielen Vertretern der Python-Community diskutieren. Teils wurde dieses Verlangen davon diktiert, dass wir nicht als \u201eAbtr\u00fcnnige\u201c von den grundlegenden Ideen der Sprache in den Augen der breiten Masse der Python-Programmierer erscheinen wollten. Es handelt sich um eine dynamisch typisierte Sprache, die f\u00fcr ihre \u201eEnten-Typisierung\u201c bekannt ist. In der Community gab es anfangs ein gewisses Misstrauen gegen\u00fcber der Idee der statischen Typisierung. Doch diese Haltung hat sich letztlich abgeschw\u00e4cht \u2013 nachdem klar wurde, dass die statische Typisierung nicht verpflichtend gemacht werden soll (und nachdem die Menschen verstanden, dass sie wirklich n\u00fctzlich ist).<\/p>\n<p>Die letztlich angenommene Syntax der Typ-Hinweise \u00e4hnelte stark der, die zu dieser Zeit von mypy unterst\u00fctzt wurde. Das Dokument PEP 484 wurde zusammen mit Python 3.5 im Jahr 2015 ver\u00f6ffentlicht. Python war nicht l\u00e4nger eine Sprache, die nur dynamische Typisierung unterst\u00fctzte. Ich denke gern an dieses Ereignis als einen bedeutenden Meilenstein in der Geschichte von Python.<\/p>\n<h2>Beginn der Migration<\/h2>\n<p>\nEnde 2015 wurde bei Dropbox ein dreik\u00f6pfiges Team f\u00fcr die Arbeit an mypy gegr\u00fcndet. Dazu geh\u00f6rten Guido van Rossum, Greg Price und David Fischer. Von diesem Zeitpunkt an begann sich die Situation sehr schnell zu entwickeln. Das erste Hindernis f\u00fcr das Wachstum von mypy war die Leistung. Wie ich bereits angedeutet habe, dachte ich in der fr\u00fchen Entwicklungsphase des Projekts dar\u00fcber nach, die Implementierung von mypy in die Programmiersprache C zu portieren, aber diese Idee wurde vorerst von der Liste gestrichen. Wir waren darauf angewiesen, dass das System mit dem Interpreter CPython betrieben wurde, der nicht die Geschwindigkeit aufweist, die f\u00fcr Werkzeuge wie mypy erforderlich ist. (Das Projekt PyPy, eine alternative Implementierung von Python mit einem JIT-Compiler, half uns auch nicht.)<\/p>\n<p>Gl\u00fccklicherweise kamen uns hier einige algorithmische Verbesserungen zur Hilfe. Der erste leistungsstarke \u201eBeschleuniger\u201c war die Implementierung der inkrementellen \u00dcberpr\u00fcfung. Die Idee dieser Verbesserung war einfach: Wenn sich alle Abh\u00e4ngigkeiten eines Moduls seit dem letzten Start von mypy nicht ge\u00e4ndert haben, k\u00f6nnen wir w\u00e4hrend der Arbeit mit den Abh\u00e4ngigkeiten die w\u00e4hrend des vorherigen Arbeitszyklus zwischengespeicherten Daten verwenden. Wir mussten nur die Typpr\u00fcfung in den ge\u00e4nderten Dateien und in den Dateien, die von ihnen abh\u00e4ngen, durchf\u00fchren. Mypy ging sogar noch einen Schritt weiter: Wenn sich die externe Schnittstelle des Moduls nicht ge\u00e4ndert hat, betrachtet mypy es als unn\u00f6tig, andere Module, die dieses Modul importieren, erneut zu \u00fcberpr\u00fcfen.<\/p>\n<p>Die inkrementelle \u00dcberpr\u00fcfung hat uns erheblich beim Annotieren gro\u00dfer Mengen bestehender Codes geholfen. Das liegt daran, dass dieser Prozess normalerweise viele iterative mypy-Durchl\u00e4ufe umfasst, da die Annotations schrittweise in den Code eingef\u00fcgt und allm\u00e4hlich verbessert werden. Der erste mypy-Durchlauf war immer noch sehr langsam, da dabei viele Abh\u00e4ngigkeiten \u00fcberpr\u00fcft werden mussten. Um die Situation zu verbessern, haben wir einen Mechanismus f\u00fcr das Remote-Caching implementiert. Wenn mypy feststellt, dass der lokale Cache wahrscheinlich veraltet ist, l\u00e4dt es den aktuellen Snapshot des Caches f\u00fcr die gesamte Codebasis aus einem zentralisierten Repository herunter. Anschlie\u00dfend f\u00fchrt es mit diesem Snapshot die inkrementelle \u00dcberpr\u00fcfung durch. Das hat uns einen erheblichen Schritt in Richtung einer h\u00f6heren mypy-Leistung gebracht.<\/p>\n<p>Dies war eine Phase der schnellen und nat\u00fcrlichen Einf\u00fchrung des Typsystems bei Dropbox. Ende 2016 hatten wir bereits etwa 420000 Zeilen Python-Code mit Typannotationen. Viele Benutzer waren begeistert von der Typ\u00fcberpr\u00fcfung. Bei Dropbox wurde mypy immer mehr von Entwicklerteams genutzt.<\/p>\n<p>Damals sah alles gut aus, aber es stand uns noch viel bevor. Wir begannen mit regelm\u00e4\u00dfigen internen Umfragen unter den Nutzern, um die Schwachstellen des Projekts zu identifizieren und zu verstehen, welche Fragen priorit\u00e4r gel\u00f6st werden mussten (diese Praxis wird noch heute im Unternehmen angewendet). Die beiden wichtigsten Aufgaben wurden schnell klar. Erstens \u2014 wir ben\u00f6tigten eine gr\u00f6\u00dfere Abdeckung des Codes mit Typen, zweitens \u2014 wir mussten sicherstellen, dass mypy schneller arbeitet. Es war offensichtlich, dass unser Bestreben, mypy zu beschleunigen und in die Projekte des Unternehmens zu integrieren, noch lange nicht abgeschlossen war. In vollem Bewusstsein der Bedeutung dieser beiden Aufgaben machten wir uns an deren L\u00f6sung.<\/p>\n<h2>Mehr Leistung!<\/h2>\n<p>\nInkrementelle \u00dcberpr\u00fcfungen haben mypy beschleunigt, aber dieses Werkzeug war immer noch nicht schnell genug. Viele inkrementelle \u00dcberpr\u00fcfungen dauerten rund eine Minute. Der Grund daf\u00fcr waren zirkul\u00e4re Importe. Dies wird wahrscheinlich niemanden \u00fcberraschen, der schon einmal mit gro\u00dfen Codebasen in Python gearbeitet hat. Wir hatten S\u00e4tze mit Hunderten von Modulen, von denen jedes indirekt alle anderen importierte. Wenn eine Datei innerhalb eines Importzyklus ge\u00e4ndert wurde, musste mypy alle Dateien in diesem Zyklus verarbeiten, und oft auch alle Module, die Module aus diesem Zyklus importierten. Einer dieser Zyklen war das ber\u00fcchtigte \u201eKn\u00e4uel von Abh\u00e4ngigkeiten\u201c, das viele Probleme bei Dropbox verursachte. Eines Tages umfasste diese Struktur mehrere Hundert Module, die direkt oder indirekt von vielen Tests importiert wurden und auch im Produktionscode verwendet wurden.<\/p>\n<p>Wir haben die M\u00f6glichkeit der \"Entwirrung\" zyklischer Abh\u00e4ngigkeiten in Betracht gezogen, hatten jedoch nicht die Ressourcen, um dies umzusetzen. Es gab zu viel Code, mit dem wir nicht vertraut waren. Schlie\u00dflich sind wir auf einen alternativen Ansatz gekommen. Wir haben beschlossen, dass mypy auch bei \"Abh\u00e4ngigkeitskn\u00e4ueln\" schnell arbeiten sollte. Dies haben wir durch den mypy-Daemon erreicht. Der Daemon ist ein serverseitiger Prozess, der zwei interessante Funktionen bietet. Erstens speichert er alle Informationen \u00fcber den gesamten Code in seinem Speicher. Das bedeutet, dass mypy bei jedem Start keine zwischengespeicherten Daten zu Tausenden von importierten Abh\u00e4ngigkeiten laden muss. Zweitens analysiert er die Abh\u00e4ngigkeiten zwischen Funktionen und anderen Entit\u00e4ten auf einer feinen strukturellen Ebene. Zum Beispiel, wenn die Funktion <code>foo<\/code> die Funktion <code>bar<\/code>, besteht eine Abh\u00e4ngigkeit <code>foo<\/code> ab <code>bar<\/code>. Wenn sich eine Datei \u00e4ndert, verarbeitet der Daemon zun\u00e4chst isoliert nur die ge\u00e4nderte Datei. Dann \u00fcberpr\u00fcft er die sichtbaren \u00c4nderungen dieser Datei, wie etwa ge\u00e4nderte Funktionssignaturen. Der Daemon nutzt detaillierte Informationen \u00fcber Importe nur zur \u00dcberpr\u00fcfung der Funktionen, die tats\u00e4chlich die ge\u00e4nderte Funktion verwenden. In der Regel muss bei diesem Ansatz nur eine sehr geringe Anzahl von Funktionen \u00fcberpr\u00fcft werden.<\/p>\n<p>Die Umsetzung all dessen war eine anspruchsvolle Aufgabe, da die urspr\u00fcngliche Implementierung von mypy stark darauf ausgelegt war, jeweils nur eine Datei zu verarbeiten. Wir mussten uns mit zahlreichen Grenzf\u00e4llen auseinandersetzen, deren Auftreten wiederholte \u00dcberpr\u00fcfungen erforderte, wenn sich im Code etwas \u00e4nderte. Zum Beispiel geschieht dies, wenn einer Klasse eine neue Basisklasse zugewiesen wird. Nachdem wir erreicht hatten, was wir wollten, konnten wir die Ausf\u00fchrungszeit der meisten inkrementellen \u00dcberpr\u00fcfungen auf einige Sekunden reduzieren. Das erschien uns als gro\u00dfer Erfolg.<\/p>\n<h2>Noch mehr Leistung!<\/h2>\n<p>\nZusammen mit dem oben beschriebenen entfernten Caching hat die mypy-Demonstration nahezu alle Probleme beseitigt, die auftraten, wenn Programmierer h\u00e4ufig Typpr\u00fcfungen durchf\u00fchren und \u00c4nderungen an einer kleinen Anzahl von Dateien vornehmen. Allerdings war die Systemleistung im ung\u00fcnstigsten Einsatzszenario nach wie vor weit von optimal entfernt. Ein sauberer mypy-Start konnte mehr als 15 Minuten in Anspruch nehmen. Das war weit mehr, als wir akzeptabel fanden. Jede Woche verschlechterte sich die Situation, da die Programmierer weiterhin neuen Code schrieben und Annotationen zu bestehendem Code hinzuf\u00fcgten. Unsere Benutzer sehnten sich immer noch nach h\u00f6herer Leistung, und wir waren bereit, ihnen entgegenzukommen.<\/p>\n<p>Wir haben beschlossen, zu einer der fr\u00fchen Ideen bez\u00fcglich mypy zur\u00fcckzukehren. N\u00e4mlich zur Umwandlung von Python-Code in C-Code. Experimente mit Cython (das ist ein System, das es erm\u00f6glicht, in Python geschriebenen Code in C-Code zu \u00fcbersetzen) haben uns keine sichtbare Beschleunigung eingebracht, daher haben wir uns entschieden, die Idee, einen eigenen Compiler zu schreiben, wiederzubeleben. Da die mypy-Codebasis (die in Python geschrieben wurde) bereits alle erforderlichen Typannotationen enthielt, hielten wir es f\u00fcr wertvoll, zu versuchen, diese Annotationen zu nutzen, um die Systemleistung zu verbessern. Ich erstellte schnell einen Prototyp zur \u00dcberpr\u00fcfung dieser Idee. Dieser zeigte bei verschiedenen Mikro-Benchmarks eine Leistungssteigerung von mehr als dem Zehnfachen. Unsere Idee war es, Python-Module mit Hilfe von Cython in C-Module zu kompilieren und die Typannotationen in Typpr\u00fcfungen umzuwandeln, die zur Laufzeit des Programms durchgef\u00fchrt werden (normalerweise werden Typannotationen w\u00e4hrend der Programmausf\u00fchrung ignoriert und nur von Typpr\u00fcfsystemen verwendet). Tats\u00e4chlich planten wir, die Implementierung von mypy von Python in eine statisch typisierte Sprache zu \u00fcbersetzen, die genau so aussehen (und gr\u00f6\u00dftenteils so funktionieren) w\u00fcrde wie Python. (Diese Art der intersprachlichen Migration wurde zu einer Art Tradition des mypy-Projekts. Die urspr\u00fcngliche Implementierung von mypy war in Alore geschrieben, dann gab es eine syntaktische Hybride aus Java und Python.)<\/p>\n<p>Die Ausrichtung auf die API der CPython-Erweiterungen war der Schl\u00fcssel, um die M\u00f6glichkeiten des Projektmanagements nicht zu verlieren. Wir mussten keine virtuelle Maschine oder irgendwelche Bibliotheken implementieren, die mypy ben\u00f6tigte. Dar\u00fcber hinaus h\u00e4tten wir weiterhin auf das gesamte \u00d6kosystem von Python zugreifen k\u00f6nnen, alle Werkzeuge (wie pytest) w\u00e4ren verf\u00fcgbar gewesen. Das bedeutete, dass wir weiterhin interpretierbaren Python-Code w\u00e4hrend der Entwicklung verwenden konnten, was uns erm\u00f6glichte, sehr schnell \u00c4nderungen im Code vorzunehmen und ihn zu testen, anstatt auf die Kompilierung des Codes zu warten. Es sah so aus, als w\u00fcrde es uns hervorragend gelingen, um es so zu sagen, auf zwei St\u00fchlen zu sitzen, und wir mochten es sehr.<\/p>\n<p>Der Compiler, den wir mypyc genannt haben (da er als Frontend mypy zur Typanalyse verwendet), erwies sich als ein sehr erfolgreiches Projekt. Insgesamt haben wir eine etwa vierfache Beschleunigung der h\u00e4ufigen mypy-Starts ohne Einsatz von Caching erreicht. Die Entwicklung des Kernprojekts mypyc dauerte f\u00fcr ein kleines Team, zu dem Michael Sullivan, Ivan Levkovsky, Hugh Han und ich geh\u00f6rten, etwa vier Kalendermonate. Dieses Ma\u00df an Arbeit war weit weniger umfangreich als das, das f\u00fcr eine Umschreibung von mypy, beispielsweise in C++ oder Go, erforderlich gewesen w\u00e4re. Auch mussten wir deutlich weniger \u00c4nderungen am Projekt vornehmen als bei einer Umschreibung in einer anderen Sprache. Dar\u00fcber hinaus hofften wir, mypyc auf ein Niveau zu bringen, auf dem es auch andere Entwickler von Dropbox zur Kompilierung und Beschleunigung ihres Codes nutzen k\u00f6nnten.<\/p>\n<p>Um ein solches Leistungsniveau zu erreichen, mussten wir einige interessante ingenieurtechnische L\u00f6sungen anwenden. So kann der Compiler die Ausf\u00fchrung vieler Operationen durch die Verwendung schneller, niederleveliger C-Konstrukte beschleunigen. Zum Beispiel wird der Aufruf einer kompilierten Funktion in einen C-Funktionsaufruf \u00fcbersetzt. Dieser Aufruf wird deutlich schneller ausgef\u00fchrt als ein Aufruf einer interpretierten Funktion. Einige Operationen, wie das Suchen in W\u00f6rterb\u00fcchern, beruhten jedoch weiterhin auf den gew\u00f6hnlichen C-API-Aufrufen aus CPython, die nach der Kompilierung nur geringf\u00fcgig schneller waren. Wir konnten die zus\u00e4tzliche Belastung des Systems, die durch die Interpretation verursacht wurde, beseitigen, aber in diesem Fall brachte das nur einen kleinen Gewinn in Bezug auf die Leistung.<\/p>\n<p>Um die am h\u00e4ufigsten auftretenden \"langsamen\" Operationen zu identifizieren, f\u00fchrten wir eine Code-Profilierung durch. Ausgestattet mit den gewonnenen Daten versuchten wir entweder, mypyc so \u201ezu optimieren\u201c, dass es schnelleren C-Code f\u00fcr solche Operationen generiert, oder den entsprechenden Python-Code so umzuschreiben, dass schnellere Operationen verwendet werden (manchmal hatten wir schlichtweg nicht die passende einfache L\u00f6sung f\u00fcr ein bestimmtes Problem). Das Umschreiben des Python-Codes stellte sich oft als die einfachere L\u00f6sung des Problems heraus, als die automatisierte Durchf\u00fchrung derselben Transformation im Compiler zu implementieren. Langfristig wollten wir viele dieser Transformationen automatisieren, aber zu diesem Zeitpunkt konzentrierten wir uns darauf, mypy mit minimalem Aufwand zu beschleunigen. Und auf diesem Weg schnitten wir ein paar Ecken ab.<\/p>\n<p>Fortsetzung folgt\u2026<\/p>\n<p><b>Liebe Leser!<\/b> Welche Eindr\u00fccke hatte das Projekt mypy auf Sie, als Sie von dessen Existenz erfuhren?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Der Weg zur \u00dcberpr\u00fcfung von Typen in 4 Millionen Zeilen Python-Code. Teil 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=\"Der Weg zur \u00dcberpr\u00fcfung von Typen in 4 Millionen Zeilen Python-Code. Teil 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Quelle: <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.1.1 - 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\/de\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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\/de\/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\udd47Der Weg zur Typ\u00fcberpr\u00fcfung von 4 Millionen Zeilen Python-Code. Teil 2 | ProHoster","description":"Heute ver\u00f6ffentlichen wir den zweiten Teil der \u00dcbersetzung des Materials dar\u00fcber, wie bei Dropbox die Typkontrolle f\u00fcr mehrere Millionen Zeilen Python-Code organisiert wurde.","canonical_url":"https:\/\/prohoster.info\/de\/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":"de_DE","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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}