Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3

Przedstawiamy Państwu trzecią część tłumaczenia materiału o drodze, jaką przeszła firma Dropbox, wdrażając u siebie system sprawdzania typów kodu Python.

Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3

→ Poprzednie części: pierwsza i druga

Osiągnięcie 4 milionów linii typowanego kodu

Kolejnym ważnym zadaniem (było to drugą najpopularniejszą kwestią, która nurtowała uczestników wewnętrznych ankiet) było zwiększenie objętości kodu w Dropbox, pokrytego sprawdzeniami typów. Wypróbowaliśmy kilka podejść do rozwiązania tego zadania — od naturalnego wzrostu objętości bazy kodu typowanego do skoncentrowania wysiłków członków zespołu mypy na statycznym i dynamicznym automatycznym wnioskowaniu typów. Ostatecznie sprawiało to wrażenie, że nie ma prostej strategii zyskownej, ale udało nam się osiągnąć szybki wzrost objętości kodu z adnotacjami, łącząc wiele podejść.

W rezultacie w naszym największym repozytorium Python (z kodem backendowym) liczba linii kodu z adnotacjami osiągnęła niemal 4 miliony. Prace nad statyczną typizacją kodu trwały około trzech lat. Mypy teraz wspiera różne rodzaje raportów o pokryciu kodu typami, które ułatwiają monitorowanie postępu typizacji. W szczególności możemy generować raporty dotyczące kodu z niepewnościami typów, takimi jak jawne użycie typu Jak w adnotacjach, których nie można zweryfikować, lub takich, jak import zewnętrznych bibliotek, w których brak adnotacji typów. W ramach projektu mającego na celu poprawę dokładności sprawdzania typów w Dropbox, przyczyniliśmy się do ulepszenia definicji typów (tak zwanych plików stubbowych) dla niektórych popularnych otwartych bibliotek w zcentralizowanym repozytorium Pythona typeshed.

Wdrożyliśmy (i ustandaryzowaliśmy w kolejnych PEP) nowe możliwości systemu typów, które pozwalają na użycie bardziej precyzyjnych typów dla niektórych specyficznych wzorców Pythona. Zauważalnym przykładem tego jest TypeDict, który oferuje typy dla słowników podobnych do JSON, mających ustalony zestaw kluczy tekstowych, z których każdy ma wartość swojego typu. Będziemy kontynuować rozwijanie systemu typów. Prawdopodobnie naszym następnym krokiem będzie poprawa wsparcia dla możliwości Pythona w zakresie operacji na liczbach.

Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3
Liczba linii kodu z adnotacjami: serwer

Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3
Liczba linii kodu z adnotacjami: klient

Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3
Całkowita liczba linii kodu z adnotacjami

Oto przegląd głównych cech działań, które podjęliśmy w celu zwiększenia liczby adnotowanych linii kodu w Dropboxie:

Ścisłość adnotacji. Stopniowo podnosiliśmy wymagania dotyczące ścisłości adnotacji nowego kodu. Zaczęliśmy od sugestii linterskich, które zalecały dodawanie adnotacji do plików, w których już były pewne adnotacje. Teraz wymagamy obecności adnotacji typów w nowych plikach Python i w większości istniejących plików.

Raporty o typowaniu. Co tydzień wysyłamy zespołom raporty na temat poziomu typowania ich kodu i dajemy wskazówki dotyczące tego, co powinno być adnotowane w pierwszej kolejności.

Promocja mypy. Opowiadamy o mypy na różnych wydarzeniach i komunikujemy się z zespołami, pomagając im zacząć korzystać z adnotacji typów.

Ankiety. Przeprowadzamy regularne ankiety użytkowników, aby zidentyfikować główne problemy. Jesteśmy gotowi pójść wystarczająco daleko w ich rozwiązaniu (aż do stworzenia nowego języka, aby przyspieszyć mypy!).

Wydajność. Znacząco poprawiliśmy wydajność mypy dzięki zastosowaniu demona i mypyc. Zrobiliśmy to, aby zniwelować niedogodności związane z adnotowaniem oraz aby móc pracować z dużymi ilościami kodu.

Integracja z edytorami. Stworzyliśmy narzędzia wspierające uruchamianie mypy w popularnych edytorach używanych w Dropboxie. Należą do nich PyCharm, Vim i VS Code. Znacząco ułatwiło to proces adnotowania kodu oraz weryfikacji jego działania. Takie działania są zwykle charakterystyczne podczas adnotowania istniejącego kodu.

Analiza statyczna. Stworzyliśmy narzędzie do generowania sygnatur funkcji przy użyciu środków analizy statycznej. To narzędzie może działać tylko w stosunkowo prostych sytuacjach, ale pomogło nam bez większego wysiłku zwiększyć pokrycie kodu typami.

Wsparcie dla bibliotek zewnętrznych. W wielu naszych projektach używany jest zestaw narzędzi SQLAlchemy. Wykorzystuje on dynamiczne możliwości Pythona, które typy PEP 484 nie są w stanie odwzorować bezpośrednio. Stworzyliśmy odpowiedni plik stub zgodnie z PEP 561 i napisaliśmy wtyczkę do mypy.otwartoźródłowy), poprawiający obsługę SQLAlchemy.

Trudności, z którymi się spotkaliśmy

Droga do 4 milionów wierszy typowanego kodu nie zawsze była dla nas łatwa. Na tej drodze napotkaliśmy wiele przeszkód i popełniliśmy kilka błędów. Oto niektóre z problemów, z którymi się zmierzyliśmy. Mamy nadzieję, że opowieść o nich pomoże innym uniknąć podobnych problemów.

Brakujące pliki. Zaczynaliśmy pracę od sprawdzania tylko niewielkiej liczby plików. Wszystko, co nie wchodziło w skład tej liczby, nie było sprawdzane. Pliki dodawano do listy kontrolnej, gdy pojawiały się w nich pierwsze adnotacje. Jeśli coś było importowane z modułu znajdującego się poza obszarem kontroli, mowa była o pracy z wartościami typu Jak, które w ogóle nie były sprawdzane. Prowadziło to do znacznej utraty precyzji typowania, szczególnie na wczesnych etapach migracji. Taki sposób działania nadal zaskakująco dobrze funkcjonował, chociaż typową sytuacją było to, że dodanie plików do obszaru kontroli ujawniało problemy w innych częściach bazy kodu. W najgorszym przypadku, gdy łączono dwa izolowane obszary kodu, w których typy były już niezależnie sprawdzone, okazywało się, że typy tych obszarów były ze sobą niekompatybilne. To prowadziło do konieczności wprowadzenia wielu zmian w adnotacjach. Patrząc wstecz, rozumiemy, że powinniśmy jak najszybciej dodać do obszaru kontroli typów mypy podstawowe moduły biblioteczne. To uczyniłoby naszą pracę znacznie bardziej przewidywalną.

Annotacja starego kodu. Kiedy zaczynaliśmy pracę, mieliśmy około 4 milionów już istniejących linii kodu w Pythonie. Było jasne, że annotacja całego tego kodu to niełatwe zadanie. Stworzyliśmy narzędzie o nazwie PyAnnotate, które może zbierać informacje o typach w trakcie wykonywania testów i potrafi dodawać do kodu adnotacje typów, opierając się na zebranych danych. Jednak nie zauważyliśmy szczególnego rozpowszechnienia tego narzędzia. Zbieranie informacji o typach było wolne, a automatycznie generowane adnotacje często wymagały wielu ręcznych poprawek. Myśleliśmy o automatycznym uruchomieniu tego narzędzia przy każdej kontroli kodu lub o zbieraniu informacji o typach na podstawie analizy niewielkiej ilości rzeczywistych zapytań sieciowych, ale postanowiliśmy tego nie robić, ponieważ którykolwiek z tych podejść byłby zbyt ryzykowny.

W końcu można zauważyć, że większość kodu została ręcznie adnotowana przez jego właścicieli. Aby skierować ten proces w właściwym kierunku, przygotowujemy raporty na temat szczególnie ważnych modułów i funkcji, które trzeba adnotować. Na przykład, ważne jest, aby dostarczyć adnotacje typów do modułu bibliotecznego używanego w setkach miejsc. Natomiast stary serwis, który jest zastępowany nowym, nie jest już tak ważny do adnotowania. Dodatkowo eksperymentujemy z wykorzystaniem analizy statycznej do generowania adnotacji typów dla starego kodu.

Importy cykliczne. Wcześniej wspomniałem o cyklicznych importach (o „plecionkach zależności”), których istnienie skomplikowało przyspieszenie mypy. Musieliśmy także wykonać poważną pracę, aby zapewnić mypy wsparcie dla wszystkich rodzajów idiomów, których przyczyną są te cykliczne importy. Niedawno zakończyliśmy duży projekt redesignu systemu, który naprawił większość problemów mypy związanych z cyklicznymi importami. Problemy te wywodziły się w rzeczywistości z bardzo wczesnych dni projektu, jeszcze z Alore, języka nauczania, na którym pierwotnie oparty był projekt mypy. Składnia Alore łatwo rozwiązywała problemy cyklicznych importów. Nowoczesny mypy odziedziczył pewne ograniczenia ze swojej wczesnej, nieco prymitywnej implementacji (która świetnie pasowała do Alore). Python utrudnia pracę z cyklicznymi importami głównie z powodu niejednoznaczności wyrażeń. Na przykład, w trakcie operacji przypisania wartość może rzeczywiście zostać określona przez alias typu. Mypy nie zawsze jest w stanie wykryć tego typu rzeczy, dopóki większość cyklu importu nie zostanie przetworzona. W Alore takich niejednoznaczności nie było. Niefortunne decyzje podjęte na wczesnych etapach rozwoju systemu mogą zaskoczyć programistę po wielu latach.

Podsumowanie: droga do 5 milionów linii kodu i nowych horyzontów

Projekt mypy przeszedł długą drogę — od wczesnych prototypów po system, który kontroluje typy kodu produkcyjnego o objętości 4 milionów linii. W trakcie rozwoju mypy została zrealizowana standaryzacja wskazówek dotyczących typów w Pythonie. Dziś wokół typizacji kodu Pythona rozwinęła się potężna ekosystem. Mieści on wsparcie dla bibliotek, zawiera narzędzia pomocnicze dla IDE i edytorów, a także kilka systemów kontroli typów, z których każdy ma swoje plusy i minusy.

Pomimo że sprawdzanie typów jest już postrzegane w Dropbox jako coś oczywistego, jestem pewny, że nadal żyjemy na początku ery typizacji kodu Pythona. Myślę, że technologie sprawdzania typów będą nadal się rozwijać i doskonalić.

Jeśli jeszcze nie korzystałeś z kontroli typów w swoim dużym projekcie Python, wiedz, że teraz jest doskonały moment, aby rozpocząć przejście na statyczne typowanie. Rozmawiałem z osobami, które dokonały podobnego przejścia. Nikt z nich tego nie żałował. Kontrola typów sprawia, że Python staje się językiem znacznie lepiej przystosowanym do tworzenia dużych projektów niż "zwykły Python".

Drodzy Czytelnicy! Czy korzystasz z kontroli typów w swoich projektach Python?

Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3
Droga do weryfikacji typów 4 milionów wierszy kodu Python. Część 3

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster