Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3

Wir präsentieren Ihnen den dritten Teil der Übersetzung des Materials über den Weg, den das Unternehmen Dropbox bei der Einführung eines Typsystems für Python-Code eingeschlagen hat.

Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3

→ Frühere Teile: erste und zweite

Erreichung von 4 Millionen Zeilen typisiertem Code

Eine weitere wichtige Aufgabe (dies war das zweithäufigste Problem, das Teilnehmer an internen Umfragen beschäftigte) war die Erhöhung des Umfangs des Codes in Dropbox, der durch Typprüfungen abgedeckt ist. Wir haben mehrere Ansätze ausprobiert, um dieses Ziel zu erreichen — von natürlichem Wachstum der typisierten Codebasis bis hin zur Konzentration der Bemühungen der mypy-Teammitglieder auf statische und dynamische automatisierte Typableitungen. Letztendlich entstand der Eindruck, dass es keine einfache Strategie zum Erfolg gibt, aber wir konnten ein schnelles Wachstum des angrenzten Codes erreichen, indem wir eine Vielzahl von Ansätzen kombinierten.

In unserem größten Python-Repository (mit Backend-Code) hat die Anzahl der annotierten Codezeilen fast 4 Millionen erreicht. Die Arbeit an der statischen Typisierung des Codes wurde über etwa drei Jahre durchgeführt. Mypy unterstützt jetzt verschiedene Arten von Berichten zur Typabdeckung, die die Überwachung des Typisierungsprozesses erleichtern. Insbesondere können wir Berichte über Code mit Unklarheiten in den Typen erstellen, wie etwa die explizite Verwendung des Typs Any in Anmerkungen, die nicht überprüft werden können, oder wie bei der Einfuhr von Drittanbieterbibliotheken, die keine Typanmerkungen enthalten. Im Rahmen des Projekts zur Verbesserung der Typprüfungsgenauigkeit in Dropbox haben wir zur Verbesserung der Typdefinitionen (sogenannten Stub-Dateien) für einige beliebte Open-Source-Bibliotheken im zentralisierten Python-Repository beigetragen. typeshed.

Wir haben neue Funktionen des Typsystems implementiert (und in späteren PEP standardisiert), die es ermöglichen, präzisere Typen für bestimmte spezifische Python-Muster zu verwenden. Ein bemerkenswertes Beispiel dafür ist TypeDict, das Typen für JSON-ähnliche Wörterbücher bereitstellt, die eine feste Menge von String-Schlüsseln haben, von denen jeder einen Wert eines eigenen Typs hat. Wir werden das Typsystem weiterhin erweitern. Wahrscheinlich wird unser nächster Schritt die Verbesserung der Unterstützung für die numerischen Fähigkeiten von Python sein.

Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3
Anzahl der annotierten Codezeilen: Server

Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3
Anzahl der annotierten Codezeilen: Kunde

Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3
Gesamtzahl der annotierten Codezeilen

Hier ist eine Übersicht über die wichtigsten Merkmale der Maßnahmen, die wir zur Erhöhung des Umfangs des annotierten Codes in Dropbox ergriffen haben:

Strenge der Annotation. Wir haben die Anforderungen an die Strenge der Annotation neuer Codes schrittweise erhöht. Wir haben mit den Empfehlungen des Linters begonnen, die anrieten, Annotationen in Dateien hinzuzufügen, in denen bereits einige Annotationen vorhanden sind. Jetzt fordern wir Typannotationen in neuen Python-Dateien und in den meisten bestehenden Dateien.

Berichte über die Typisierung. Wöchentlich senden wir den Teams Berichte über den Grad der Typisierung ihres Codes und geben Ratschläge, was zuerst annotiert werden sollte.

Popularisierung von mypy. Wir sprechen auf verschiedenen Veranstaltungen über mypy und kommunizieren mit den Teams, um ihnen zu helfen, Typannotations zu verwenden.

Umfragen. Wir führen regelmäßige Umfragen unter den Nutzern durch, um die Hauptprobleme zu identifizieren. Wir sind bereit, weit zu gehen, um diese Probleme zu lösen (bis hin zur Schaffung einer neuen Sprache zur Beschleunigung von mypy!).

Leistung. Wir haben die Leistung von mypy erheblich verbessert, indem wir einen Daemon und mypyc eingesetzt haben. Dies wurde getan, um die Unannehmlichkeiten während des Annotierens zu minimieren und um die Arbeit mit großen Codebasen zu ermöglichen.

Integration mit Editoren. Wir haben Werkzeuge zur Unterstützung der Ausführung von mypy in den bei Dropbox beliebten Editoren entwickelt. Dazu gehören PyCharm, Vim und VS Code. Dies hat den Prozess des Annotierens und Testens des Codes erheblich erleichtert. Solche Maßnahmen sind normalerweise charakteristisch für das Annotieren von vorhandenem Code.

Statische Analyse. Wir haben ein Tool zur Ausgabe von Funktionssignaturen unter Verwendung statischer Analysetools entwickelt. Dieses Tool kann nur in vergleichsweise einfachen Situationen arbeiten, hat uns aber geholfen, den Typabdeckungsgrad des Codes ohne großen Aufwand zu erhöhen.

Unterstützung von Drittanbieterbibliotheken. In vielen unserer Projekte verwenden wir das Toolset SQLAlchemy. Dabei kommen die dynamischen Möglichkeiten von Python zum Einsatz, die die Typen von PEP 484 nicht direkt modellieren können. Entsprechend PEP 561 haben wir die entsprechende Stub-Datei erstellt und ein Plugin für mypy geschrieben.Open Source), die die SQLAlchemy-Unterstützung verbessert.

Die Schwierigkeiten, mit denen wir konfrontiert waren

Der Weg zu 4 Millionen Zeilen typisierten Codes war nicht immer einfach für uns. Auf diesem Weg sind wir auf zahlreiche Stolpersteine gestoßen und haben einige Fehler gemacht. Hier sind einige der Probleme, mit denen wir konfrontiert waren. Wir hoffen, dass unser Bericht über diese Probleme anderen helfen kann, ähnliche Herausforderungen zu vermeiden.

Fehlende Dateien. Wir haben mit der Überprüfung nur einer kleinen Anzahl von Dateien begonnen. Alles, was nicht zu diesen Dateien gehörte, wurde nicht überprüft. Dateien wurden zur Überprüfung hinzugefügt, sobald die ersten Annotations vorhanden waren. Wenn etwas aus einem Modul importiert wurde, das außerhalb des Überprüfungsbereichs lag, handelte es sich um die Arbeit mit Werten vom Typ Any, die überhaupt nicht überprüft wurden. Dies führte zu einem erheblichen Verlust an Typgenauigkeit, insbesondere in den frühen Phasen der Migration. Dieser Ansatz hat überraschend gut funktioniert, obwohl es typisch war, dass die Hinzufügung von Dateien zur Überprüfung Probleme in anderen Teilen der Codebasis aufdeckte. Im schlimmsten Fall, wenn zwei isolierte Codebereiche zusammengeführt wurden, in denen die Typen unabhängig voneinander bereits geprüft worden waren, stellte sich heraus, dass die Typen dieser Bereiche nicht miteinander kompatibel waren. Dies erforderte viele Änderungen an den Annotations. Jetzt, im Rückblick, verstehen wir, dass wir die grundlegenden Bibliotheksmodule so früh wie möglich in den Typprüfungsbereich von mypy einfügen sollten. Das hätte unsere Arbeit viel vorhersehbarer gemacht.

Annotierung von altem Code. Als wir mit der Arbeit begannen, hatten wir etwa 4 Millionen Zeilen bereits bestehenden Python-Codes. Es war klar, dass die Annotierung all dieses Codes eine schwierige Aufgabe war. Wir entwickelten ein Tool namens PyAnnotate, das zur Laufzeit während der Tests Informationen über Typen sammeln kann und in der Lage ist, basierend auf den gesammelten Informationen Typannotationen in den Code hinzuzufügen. Eine besonders breite Einführung dieses Tools haben wir jedoch nicht wahrgenommen. Das Sammeln von Typinformationen verlief langsam, und automatisch generierte Annotationen erforderten oft viele manuelle Anpassungen. Wir dachten darüber nach, dieses Tool bei jedem Code-Check automatisch auszuführen oder Typinformationen basierend auf der Analyse einer kleinen Menge realer Netzwerkabfragen zu sammeln, haben uns jedoch dagegen entschieden, da jeder dieser Ansätze zu riskant war.

Abschließend kann gesagt werden, dass der größte Teil des Codes manuell von seinen Besitzern annotiert wurde. Um diesen Prozess in die richtige Richtung zu lenken, erstellen wir Berichte über besonders wichtige Module und Funktionen, die annotiert werden müssen. Es ist beispielsweise wichtig, den Bibliotheksmodul, der an vielen Stellen verwendet wird, mit Typannotationen zu versehen. Ein alter Service, der durch einen neuen ersetzt wird, muss hingegen nicht mehr so wichtig annotiert werden. Außerdem experimentieren wir mit dem Einsatz statischer Analysen zur Generierung von Typannotationen für alten Code.

Zyklische Importe. Ich habe bereits über zyklische Importe (über "Abhängigkeitsstränge") gesprochen, deren Existenz die Beschleunigung von mypy erschwert hat. Darüber hinaus mussten wir intensiv daran arbeiten, mypy mit Unterstützung für alle Arten von Idiomen auszustatten, die durch diese zyklischen Importe verursacht werden. Kürzlich haben wir ein großes Redesign-Projekt abgeschlossen, das die meisten Probleme von mypy in Bezug auf zyklische Importe behoben hat. Diese Probleme stammen tatsächlich aus den sehr frühen Tagen des Projekts, noch von Alore, der Lehrsprache, auf die mypy ursprünglich ausgerichtet war. Die Syntax von Alore ermöglicht es, Probleme zyklischer Importanweisungen leicht zu lösen. Modernes mypy hat einige Einschränkungen von seiner frühen, schlichten Implementierung geerbt (die sich gut für Alore eignete). Python erschwert die Arbeit mit zyklischen Importen hauptsächlich aufgrund von Mehrdeutigkeiten in den Ausdrücken. Zum Beispiel kann bei einer Zuweisung der tatsächliche Typalias bestimmt werden. Mypy ist nicht immer in der Lage, solche Dinge zu erkennen, bis ein großer Teil des Importzyklus bearbeitet ist. In Alore gab es solche Mehrdeutigkeiten nicht. Fehlentscheidungen, die in frühen Entwicklungsphasen des Systems getroffen wurden, können Programmierern viele Jahre später unangenehme Überraschungen bereiten.

Ergebnisse: Der Weg zu 5 Millionen Codezeilen und neuen Horizonten

Das mypy-Projekt hat einen langen Weg hinter sich — von frühen Prototypen bis zu einem System, mit dem die Typen von Produktionscode mit insgesamt 4 Millionen Zeilen kontrolliert werden. Im Laufe der Entwicklung von mypy wurde eine Standardisierung der Typ-Hinweise in Python umgesetzt. Heutzutage hat sich um die Typisierung von Python-Code ein leistungsfähiges Ökosystem entwickelt. Es gibt Unterstützung für Bibliotheken, Hilfsmittel für IDEs und Editoren, und mehrere Typprüfungssysteme, von denen jedes seine eigenen Vor- und Nachteile hat.

Obwohl die Typprüfung bei Dropbox bereits als selbstverständlich angesehen wird, bin ich mir sicher, dass wir uns immer noch in der Frühzeit der Typisierung von Python-Code befinden. Ich denke, dass sich die Technologien zur Typprüfung weiterhin entwickeln und verbessern werden.

Wenn Sie Typüberprüfungen in Ihrem großangelegten Python-Projekt noch nicht verwendet haben, wissen Sie, dass jetzt der passende Zeitpunkt ist, um auf statische Typisierung umzusteigen. Ich habe mit vielen gesprochen, die diesen Übergang vollzogen haben. Niemand von ihnen hat es bereut. Die Typkontrolle verwandelt Python in eine Sprache, die deutlich besser geeignet ist für die Entwicklung großer Projekte als „normales Python“.

Liebe Leser! Nutzen Sie die Typkontrolle in Ihren Python-Projekten?

Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3
Der Weg zur Typüberprüfung von 4 Millionen Zeilen Python-Code. Teil 3

Quelle: habr.com

60GB SSD 8Gb DDR4