Vi præsenterer hermed den tredje del af oversættelsen af materialet om den rejse, som Dropbox har gennemgået med implementeringen af deres Python-kode typekontrolsystem.
→ Tidligere dele: og
Opnåelse af 4 millioner linjer typeannoteret kode
En anden vigtig opgave (dette var det næstmest populære emne blandt dem, der deltog i de interne spørgeskemaer) var at øge mængden af kode i Dropbox, der var dækket af typekontrol. Vi prøvede flere tilgange til at løse denne opgave - fra den naturlige vækst af den typeannoterede kodebase til at fokusere på indsatsen fra teamet mypy på statisk og dynamisk automatiseret typedeclaration. I sidste ende havde vi indtrykket, at der ikke var nogen simpel vindende strategi, men vi formåede hurtigt at øge mængden af annoteret kode ved at kombinere mange tilgange.
Som resultat er antallet af linjer af annoteret kode i vores største Python-repository (med backend-kode) nået næsten op på 4 millioner. Arbejdet med statisk typekontrol blev udført over cirka tre år. Mypy understøtter nu forskellige former for dækningsrapporter om typer, der gør det lettere at overvåge fremskridtene i typeannotationen. Især kan vi generere rapporter om kode med usikkerheder i typer, såsom eksplicit brug af typer Any i annotationer, der ikke kan verificeres, eller når der importeres tredjepartsbiblioteker, hvor der ikke er nogen typeannotering. Som en del af projektet for at forbedre nøjagtigheden af typekontrol i Dropbox har vi bidraget til forbedringen af type-definitionerne (de såkaldte stub-filer) for nogle populære open source-biblioteker i et centraliseret Python-repository .
Vi har implementeret (og standardiseret i de efterfølgende PEP'er) nye funktioner i typesystemet, der muliggør brugen af mere præcise typer til nogle specifikke Python-mønstre. Et bemærkelsesværdigt eksempel på dette er TypeDict, som tilbyder typer til JSON-lignende ordbøger med et fast sæt af strengnøgler, hvor hver enkelt har en værdi af sin egen type. Vi vil fortsætte med at udvide typesystemet. Vores næste skridt vil sandsynligvis være at forbedre understøttelsen af Python-funktioner til at arbejde med tal.

Antallet af linjer af annoteret kode: server

Antallet af annoteret kode: klient

Det samlede antal linjer af annoteret kode
Her er et overblik over de vigtigste funktioner i de handlinger, vi har udført for at øge mængden af annoteret kode i Dropbox:
Strenghed i annotering. Vi har gradvist hævet kravene til strengheden ved annotering af ny kode. Vi startede med at anbefale linsteren, som foreslog at tilføje annotationer i filer, der allerede har nogle annotationer. Nu kræver vi, at der er typeannotationer i nye Python-filer og i de fleste eksisterende filer.
Typingsrapporter. Vi sender ugentligt rapporter til teams om niveauet af typinger i deres kode og giver råd om, hvad der bør annoteres først.
Popularisering af mypy. Vi præsenterer mypy ved forskellige arrangementer og interagerer med teams for at hjælpe dem med at begynde at bruge typeannotationer.
Undersøgelser. Vi gennemfører periodiske brugerundersøgelser for at identificere de største problemer. Vi er villige til at gå langt for at løse disse problemer (inklusive at skabe et nyt sprog for at fremskynde mypy!).
Ydeevne. Vi har markant forbedret ydeevnen af mypy ved at bruge en daemon og mypyc. Dette blev gjort for at udjævne ulemperne ved annotering og for at kunne arbejde med store mængder kode.
Integration med redaktører. Vi har skabt værktøjer til støtte for kørsel af mypy i redaktører, der er populære i Dropbox. Dette inkluderer PyCharm, Vim og VS Code. Det har betydeligt forenklet processen med at annotere kode og kontrollere dens funktionalitet. Sådanne handlinger er typiske, når man annoterer eksisterende kode.
Statisk analyse. Vi har skabt et værktøj til at generere funktionssignaturer ved hjælp af statisk analyse. Dette værktøj kan kun fungerer i forholdsvis enkle situationer, men det har hjulpet os med at øge type-dækningen af koden uden større anstrengelser.
Support til tredjepartsbiblioteker. I mange af vores projekter bruger vi værktøjsættet SQLAlchemy. Det bruger de dynamiske funktioner i Python, som PEP 484-typerne ikke kan modellere direkte. I henhold til PEP 561 har vi oprettet den relevante stub-fil og skrevet et plugin til mypy.), som forbedrer SQLAlchemy-support.
Udfordringer, vi stødte på
Vejen til 4 millioner linjer af typet kode har ikke altid været let for os. På denne rejse har vi mødt mange forhindringer og begået flere fejl. Her er nogle af de problemer, vi har stået overfor. Vi håber, at fortællingen om dem vil hjælpe andre med at undgå lignende problemer.
Manglede filer. Vi begyndte arbejdet med at kontrollere kun et lille antal filer. Alt, der ikke var inkluderet i disse filer, blev ikke kontrolleret. Filer blev tilføjet til kontrollisten, når de første annotationer dukkede op i dem. Hvis noget blev importeret fra et modul uden for kontrolområdet, var der tale om at arbejde med værdier, der slet ikke blev verificeret. Any, hvilket førte til betydelige tab af typepræcision, især i de tidlige faser af migrationen. Denne tilgang fungerede stadig overraskende godt, selvom det typisk var en situation, hvor tilføjelsen af filer til kontrolområdet afdækkede problemer i andre dele af kodebasen. I værste fald, når to isolerede kodeområder, hvor typerne allerede uafhængigt var blevet kontrolleret, blev sammenlagt, viste det sig, at typerne i disse områder var inkompatible med hinanden. Dette førte til, at vi måtte foretage mange ændringer i annotationerne. Nu, når vi ser tilbage, forstår vi, at vi burde have tilføjet grundlæggende biblioteksmoduler til typekontrolområdet med det samme. Det ville have gjort vores arbejde meget mere forudsigeligt.
Annotering af gammel kode. Da vi begyndte vores arbejde, havde vi omkring 4 millioner linjer eksisterende Python-kode. Det var klart, at annotering af al denne kode var en udfordrende opgave. Vi skabte et værktøj kaldet PyAnnotate, som kan indsamle typeoplysninger under testkørsler og tilføje typeannoteringer til koden baseret på de indsamlede oplysninger. Dog så vi ikke nogen bred implementering af dette værktøj. Indsamlingen af typeoplysninger var langsom, og automatisk genererede annoteringer krævede ofte mange manuelle korrektioner. Vi overvejede at køre dette værktøj automatisk ved hver kodegennemgang eller at indsamle typeoplysninger baseret på analysen af et lille antal faktiske netværksanmodninger, men besluttede ikke at gøre dette, da nogen af disse tilgange var for risikable.
Som resultat kan det bemærkes, at størstedelen af koden blev annoteret manuelt af dens ejere. For at styre denne proces i den rigtige retning forbereder vi rapporter om især vigtige moduler og funktioner, der skal annoteres. For eksempel er det vigtigt at tilføje typeannoteringer til biblioteksmodulet, der bruges mange steder. Men for den gamle service, som bliver erstattet af den nye, er annoteringen ikke så vigtig længere. Derudover eksperimenterer vi med at bruge statisk analyse til at generere typeannoteringer for den gamle kode.
Cirkulære importer. Tidligere har jeg talt om cirkulære importer (om "afhængighedsknuder"), hvis eksistens har gjort det sværere at accelerere mypy. Vi har desuden været nødt til at arbejde hårdt på at give mypy støtte til alle former for idiomer, hvis opståen skyldes disse cirkulære importer. For nylig afsluttede vi et stort redesign af systemet, som rettede de fleste problemer med mypy relateret til cirkulære importer. Disse problemer stammer faktisk fra de tidlige dage af projektet, helt tilbage fra Alore, et undervisningssprog, som projektet mypy oprindeligt blev orienteret imod. Alore-syntaxen gør det let at løse problemer med cirkulære importbefalinger. Moderne mypy har arvet nogle begrænsninger fra sin tidlige enkelthed (som var ideel til Alore). Python komplicerer arbejdet med cirkulære importer hovedsageligt på grund af tvetydighed i udtryk. For eksempel kan en typealias faktisk blive defineret under en tildelingsoperation. Mypy er ikke altid i stand til at identificere sådanne ting, indtil det meste af importcyklussen er blevet behandlet. I Alore fandtes der ikke sådanne tvetydigheder. Uheldige beslutninger truffet i de tidlige udviklingsfaser af systemet kan overraske programmørerne mange år senere.
Konklusioner: Vejen til 5 millioner linjer kode og nye horisonter
Projektet mypy har gennemgået en lang rejse - fra tidlige prototyper til et system, der kontrollerer typerne af produktionskode på 4 millioner linjer. Undervejs er der blevet standardiseret type-hints i Python. I dag er der udviklet et kraftfuldt økosystem omkring typificering af Python-kode. Det rummer støtte til biblioteker, der findes værktøjer til IDE'er og redaktører, og der er flere systems til typekontrol, som hver har sine fordele og ulemper.
Selvom typekontrol allerede opfattes som en selvfølgelighed i Dropbox, er jeg sikker på, at vi stadig lever i den tidlige fase af Python-kodetypering. Jeg tror, at teknologierne til typekontrol vil fortsætte med at udvikle sig og forbedre sig.
Hvis du endnu ikke har brugt typekontrol i dit store Python-projekt, så ved, at nu er et fantastisk tidspunkt at begynde overgangen til statisk typning. Jeg har talt med dem, der har gennemført en sådan overgang. Ingen af dem har fortrudt det. Typekontrol gør Python til et sprog, der er meget bedre egnet til udvikling af store projekter end "almindelig Python".
Kære læsere! Bruger du typekontrol i dine Python-projekter?
Kilde: habr.com
