Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3

Przedstawiamy Państwu trzecią część tłumaczenia materiału o ścieżce, jaką obrała Dropbox przy wdrażaniu systemu sprawdzania typów kodu Pythona.

Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3

→ Poprzednie części: pierwszy и drugi

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

Kolejnym poważnym wyzwaniem (i drugą najczęstszą obawą wśród ankietowanych wewnętrznie) było zwiększenie ilości kodu objętego kontrolą typu w Dropbox. Wypróbowaliśmy kilka podejść, aby rozwiązać ten problem, od naturalnego zwiększania rozmiaru bazy kodu z typem po skupienie wysiłków zespołu mypy na statycznym i dynamicznym zautomatyzowanym wnioskowaniu o typach. Ostatecznie wydawało się, że nie ma prostej zwycięskiej strategii, ale udało nam się osiągnąć szybki wzrost liczby kodu z adnotacjami, łącząc wiele podejść.

W rezultacie nasze największe repozytorium języka Python (z kodem zaplecza) zawiera prawie 4 miliony linii kodu z adnotacjami. Prace nad statycznym typowaniem kodu zakończono w ciągu około trzech lat. Mypy obsługuje teraz różne typy raportów pokrycia kodu, które ułatwiają monitorowanie postępu pisania. W szczególności możemy generować raporty dotyczące kodu z niejasnościami w typach, jak np. jawne użycie typu Any w adnotacjach, których nie można zweryfikować, lub przy importowaniu bibliotek innych firm, które nie mają adnotacji typu. W ramach projektu mającego na celu poprawę dokładności sprawdzania typów w Dropbox przyczyniliśmy się do ulepszenia definicji typów (tzw. plików pośredniczących) dla niektórych popularnych bibliotek open source w scentralizowanym repozytorium Pythona wpisano na maszynie.

Wdrożyliśmy (i ujednoliciliśmy w kolejnych PEP) nowe funkcje systemu typów, które pozwalają na bardziej precyzyjne typy dla niektórych specyficznych wzorców Pythona. Godnym uwagi przykładem jest TypeDict, który udostępnia typy słowników typu JSON, które mają stały zestaw kluczy ciągów, każdy z wartością własnego typu. Będziemy nadal rozwijać system typów. Naszym następnym krokiem będzie prawdopodobnie ulepszenie obsługi numerycznych możliwości Pythona.

Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3
Liczba linii kodu z adnotacjami: serwer

Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3
Liczba linii kodu z adnotacjami: klient

Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3
Całkowita liczba linii kodu z adnotacjami

Oto przegląd głównych funkcji, które zrobiliśmy, aby zwiększyć ilość kodu z adnotacjami w Dropbox:

Rygoryzm adnotacji. Stopniowo zwiększaliśmy wymagania dotyczące rygoru opisywania nowego kodu. Zaczęliśmy od wskazówek dotyczących lintera, które sugerowały dodawanie adnotacji do plików, które miały już pewne adnotacje. Obecnie wymagamy adnotacji typów w nowych plikach Pythona i większości istniejących plików.

Wpisywanie raportów. Wysyłamy zespołom cotygodniowe raporty na temat poziomu wpisywania kodu i doradzamy, co należy zanotować w pierwszej kolejności.

Popularyzacja mypy. Rozmawiamy o mypy na wydarzeniach i rozmawiamy z zespołami, aby pomóc im rozpocząć pracę z adnotacjami typów.

Ankiety. Przeprowadzamy okresowe ankiety wśród użytkowników w celu identyfikacji głównych problemów. Jesteśmy gotowi posunąć się dość daleko w rozwiązaniu tych problemów (nawet tworząc nowy język, aby przyspieszyć mypy!).

Wydajność. Znacząco poprawiliśmy wydajność mypy, używając demona i mypyc. Dokonano tego, aby załagodzić niedogodności pojawiające się podczas procesu adnotacji i aby móc pracować z dużą ilością kodu.

Integracja z redaktorami. Stworzyliśmy narzędzia do obsługi uruchamiania mypy w edytorach popularnych w Dropbox. Obejmuje to PyCharm, Vim i VS Code. To znacznie uprościło proces opisywania kodu i sprawdzania jego funkcjonalności. Tego typu akcje są częste podczas dodawania adnotacji do istniejącego kodu.

Analiza statyczna. Stworzyliśmy narzędzie do wnioskowania o sygnaturach funkcji za pomocą narzędzi do analizy statycznej. To narzędzie może działać tylko w stosunkowo prostych sytuacjach, ale pomogło nam zwiększyć zasięg typów kodu bez większego wysiłku.

Obsługa bibliotek stron trzecich. Wiele naszych projektów korzysta z zestawu narzędzi SQLAlchemy. Wykorzystuje dynamiczne możliwości Pythona, których typy PEP 484 nie są w stanie bezpośrednio modelować. My, zgodnie z PEP 561, utworzyliśmy odpowiedni plik pośredniczący i napisaliśmy wtyczkę do mypy (otwarte źródło), co poprawia obsługę SQLAlchemy.

Trudności, które napotkaliśmy

Droga do 4 milionów linii wpisywanego kodu nie zawsze była dla nas łatwa. Na tej ścieżce napotkaliśmy wiele dziur i popełniliśmy kilka błędów. Oto niektóre z problemów, które napotkaliśmy. Mamy nadzieję, że opowiedzenie o nich pomoże innym uniknąć podobnych problemów.

Brakujące pliki. Naszą pracę rozpoczęliśmy od sprawdzenia niewielkiej liczby plików. Wszystko, co nie zostało zawarte w tych plikach, nie zostało sprawdzone. Pliki zostały dodane do listy skanowania w momencie pojawienia się w nich pierwszych adnotacji. Jeśli coś zostało zaimportowane z modułu znajdującego się poza zakresem weryfikacji, wtedy mówimy o pracy z wartościami typu Anyktóre w ogóle nie były testowane. Doprowadziło to do znacznej utraty dokładności pisania, zwłaszcza na wczesnych etapach migracji. To podejście jak dotąd sprawdziło się zaskakująco dobrze, chociaż typowa sytuacja jest taka, że ​​dodanie plików do zakresu przeglądu ujawnia problemy w innych częściach bazy kodu. W najgorszym przypadku, gdy połączono dwa izolowane obszary kodu, w których niezależnie od siebie zostały już sprawdzone typy, okazało się, że typy tych obszarów były ze sobą niezgodne. Spowodowało to konieczność wprowadzenia wielu zmian w adnotacjach. Patrząc wstecz, zdajemy sobie sprawę, że powinniśmy wcześniej dodać moduły biblioteki podstawowej do obszaru sprawdzania typów mypy. Dzięki temu nasza praca będzie znacznie bardziej przewidywalna.

Adnotowanie starego kodu. Kiedy zaczynaliśmy, mieliśmy około 4 milionów linii istniejącego kodu Pythona. Było jasne, że opisanie całego tego kodu nie było łatwym zadaniem. Stworzyliśmy narzędzie o nazwie PyAnnotate, które może zbierać informacje o typach podczas uruchamiania testów i dodawać adnotacje o typach do kodu na podstawie zebranych informacji. Nie zaobserwowaliśmy jednak szczególnie powszechnego zastosowania tego narzędzia. Zbieranie informacji o typie było powolne, a automatycznie generowane adnotacje często wymagały wielu ręcznych edycji. Zastanawialiśmy się nad automatycznym uruchamianiem tego narzędzia za każdym razem, gdy przeglądamy kod lub zbieramy informacje o typach na podstawie analizy niewielkiej liczby rzeczywistych żądań sieciowych, ale zdecydowaliśmy się tego nie robić, ponieważ jedno i drugie podejście było zbyt ryzykowne.

W rezultacie można zauważyć, że większość kodu została ręcznie opatrzona adnotacjami przez jego właścicieli. Aby poprowadzić ten proces we właściwym kierunku, przygotowujemy raporty dotyczące szczególnie ważnych modułów i funkcji, które wymagają adnotacji. Na przykład ważne jest zapewnienie adnotacji typu dla modułu bibliotecznego, który jest używany w setkach miejsc. Jednak opisywanie starej usługi zastępowanej przez nową nie jest już tak ważne. Eksperymentujemy również z wykorzystaniem analizy statycznej do generowania adnotacji typu dla starszego kodu.

Import cykliczny. Powyżej mówiłem o importach cyklicznych („splątaniach zależności”), których istnienie utrudniało przyspieszenie mypy. Musieliśmy także ciężko pracować, aby mypy obsługiwał wszelkiego rodzaju idiomy spowodowane cyklicznym importem. Niedawno zakończyliśmy duży projekt przeprojektowania systemu, który rozwiązał większość problemów mypy związanych z importem cyklicznym. Problemy te w rzeczywistości wynikały z początków projektu, jeszcze z języka Alore, języka edukacyjnego, na którym pierwotnie skupiał się projekt mypy. Składnia Alore ułatwia rozwiązywanie problemów z cyklicznymi poleceniami importu. Nowoczesne mypy odziedziczyło pewne ograniczenia po swojej wcześniejszej, prostolinijnej implementacji (która świetnie pasowała do Alore). Python utrudnia pracę z importami cyklicznymi, głównie dlatego, że wyrażenia są niejednoznaczne. Na przykład operacja przypisania może w rzeczywistości zdefiniować alias typu. Mypy nie zawsze jest w stanie wykryć takie rzeczy, dopóki większość pętli importu nie zostanie przetworzona. W Alore takich dwuznaczności nie było. Złe decyzje podjęte na wczesnych etapach rozwoju systemu mogą sprawić programiście niemiłą niespodziankę wiele lat później.

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

Projekt mypy przeszedł długą drogę - od wczesnych prototypów do systemu kontrolującego 4 miliony linii typów kodu produkcyjnego. W miarę ewolucji mypy wskazówki dotyczące typów Pythona zostały ujednolicone. Obecnie wokół pisania kodu w języku Python rozwinął się potężny ekosystem. Posiada miejsce na obsługę bibliotek, zawiera narzędzia pomocnicze dla IDE i edytorów, posiada kilka systemów kontroli typów, z których każdy ma swoje wady i zalety.

Mimo że sprawdzanie typów jest już w Dropbox czymś normalnym, uważam, że wciąż jesteśmy na początku pisania kodu w języku Python. Myślę, że technologie sprawdzania typu będą nadal ewoluować i udoskonalać.

Jeśli nie korzystałeś jeszcze ze sprawdzania typu w swoim dużym projekcie w języku Python, wiedz, że teraz jest bardzo dobry moment, aby zacząć przechodzić na pisanie statyczne. Rozmawiałem z osobami, które dokonały podobnej zmiany. Żadne z nich tego nie żałowało. Sprawdzanie typów sprawia, że ​​Python jest językiem znacznie lepiej przystosowanym do tworzenia dużych projektów niż „zwykły Python”.

Drodzy Czytelnicy! Czy używasz sprawdzania typów w swoich projektach w Pythonie?

Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3
Ścieżka do sprawdzania typów 4 milionów linii kodu Pythona. Część 3

Źródło: www.habr.com

Dodaj komentarz