Veien til typekontroll av 4 millioner linjer Python-kode. Del 3

Vi presenterer tredje del av oversettelsen av materialet om veien Dropbox har tatt for å implementere typekontroll i sin Python-kode.

Veien til typekontroll av 4 millioner linjer Python-kode. Del 3

→ Forrige deler: første og andre

Oppnåelse av 4 millioner linjer typet kode

En annen viktig oppgave (denne var den nest mest populære problemstillingen blant deltakerne i interne undersøkelser) var å øke volumet av kode i Dropbox som dekkes av typekontroller. Vi har forsøkt flere tilnærminger for å løse denne oppgaven – fra naturlig vekst av det typede kodegrunnlaget til å fokusere teammedlemmene på mypy på statisk og dynamisk automatisk typeutvinning. Til slutt fikk vi inntrykk av at det ikke er en enkel vinnende strategi, men vi klarte å oppnå rask vekst av volumet av annotert kode ved å kombinere flere tilnærminger.

Som et resultat nådde antallet linjer med annotert kode i vårt største Python-repositorium (med backend-kode) nesten 4 millioner. Arbeidet med statisk typisering av koden ble gjennomført over omtrent tre år. Mypy støtter nå forskjellige typer rapporter om typekodedekning som forenkler overvåkingen av typiseringsprosessen. Spesielt kan vi lage rapporter om kode med usikkerheter i typer, slik som eksplisitt bruk av typer Any i annotasjoner som ikke kan sjekkes, eller fra import av tredjepartsbiblioteker som ikke har typeannotasjoner. I forbindelse med et prosjekt for å forbedre nøyaktigheten av typekontroll i Dropbox, bidro vi til forbedring av type-definisjoner (såkalte stub-filer) for noen populære open-source biblioteker i et sentralisert Python-repositorium typeshed.

Vi implementerte (og standardiserte i senere PEP) nye muligheter for typesystemet som gjør det mulig å bruke mer presise typer for noen spesifikke Python-mønstre. Et bemerkelsesverdig eksempel på dette er TypeDict, som gir typer for JSON-lignende ordbøker med et fast sett av strenge nøkler, hver av dem har sin egen typeverdi. Vi vil fortsette å utvide typesystemet. Sannsynligvis vil vårt neste skritt være å forbedre støtten for Python-funksjoner relatert til tall.

Veien til typekontroll av 4 millioner linjer Python-kode. Del 3
Antall linjer med annotert kode: server

Veien til typekontroll av 4 millioner linjer Python-kode. Del 3
Antall linjer med annotert kode: klient

Veien til typekontroll av 4 millioner linjer Python-kode. Del 3
Totalt antall linjer med annotert kode

Her er en oversikt over hovedfunksjonene til de tiltakene vi har utført for å øke volumet av annotert kode i Dropbox:

Strenghet i annotering. Vi har gradvis økt kravene til strenghet i annoteringen av ny kode. Vi begynte med linters råd som foreslo å legge til annoteringer i filer som allerede har noen annoteringer. Nå krever vi tilstedeværelse av typeannoteringer i nye Python-filer og i de fleste eksisterende filer.

Typeanalyserapporter. Vi sender ukentlig ut rapporter til teamene om nivået på typeannoteringene i koden deres, og gir råd om hva som bør prioriteres i annoteringen.

Popularisering av mypy. Vi informerer om mypy på ulike arrangementer og samarbeider med teamene for å hjelpe dem med å begynne å bruke typeannoteringer.

Undersøkelser. Vi gjennomfører periodiske brukerundersøkelser for å identifisere de viktigste problemene. Vi er villige til å gå langt for å løse disse problemene (inkludert å lage et nytt språk for å akselerere mypy!).

Ytelse. Vi har betydelig forbedret ytelsen til mypy ved å bruke en demon og mypyc. Dette er gjort for å jevne ut ulemper som oppstår under annoteringen og gi mulighet til å arbeide med store mengder kode.

Integrasjon med redaktører. Vi har laget verktøy for å støtte kjøring av mypy i redaktører som er populære i Dropbox. Dette inkluderer PyCharm, Vim og VS Code. Dette har betydelig forenklet prosessen med å annotere koden og sjekke dens funksjonalitet. Slike tiltak er vanlig ved annotering av eksisterende kode.

Statisk analyse. Vi har utviklet et verktøy for å gi funksjonssignaturer ved hjelp av statisk analyse. Dette verktøyet kan bare fungere i relativt enkle situasjoner, men det har hjulpet oss med å øke dekningen av koden med typer uten mye ekstra arbeid.

Støtte for tredjepartsbiblioteker. I mange av prosjektene våre bruker vi verktøysettet SQLAlchemy. Det benytter seg av de dynamiske mulighetene i Python som typen PEP 484 ikke kan modellere direkte. I samsvar med PEP 561 har vi laget en passende stub-fil og skrevet et plugin for mypy (åpen kildekode), som forbedrer støtten for SQLAlchemy.

Utfordringene vi møtte

Veien til 4 millioner linjer med typet kode har ikke alltid vært enkel for oss. På denne veien har vi møtt mange hull og gjort flere feil. Her er noen av problemene vi har støtt på. Vi håper at fortellingen om dem kan hjelpe andre med å unngå lignende problemer.

Glemte filer. Vi begynte arbeidet med å sjekke bare et lite antall filer. Alt som ikke var inkludert i disse filene, ble ikke kontrollert. Filer ble lagt til sjekklisten når de fikk sine første annoteringer. Hvis noe ble importert fra et modul som lå utenfor sjekkområdet, handlet det om arbeid med typer Any, som ikke ble kontrollert i det hele tatt. Dette førte til betydelig tap av typepresisjon, spesielt i de tidlige migrasjonsfasene. Denne tilnærmingen har fortsatt fungert overraskende bra, selv om det vanligvis var en situasjon der tillegg av filer til sjekkområdet avdekket problemer i andre deler av kodebasen. I det verste tilfellet, når to isolerte kodeområder ble slått sammen, hvor typene allerede var kontrollert uavhengig av hverandre, viste det seg at typene i disse områdene var inkompatible med hverandre. Dette krevde at en rekke endringer måtte gjøres i annoteringene. Nå, når vi ser tilbake, forstår vi at vi skulle ha lagt de grunnleggende bibliotekmodulene i mypy-typekontrollen så tidlig som mulig. Dette ville gjort arbeidet vårt mye mer forutsigbart.

Annotering av gammel kode. Da vi begynte arbeidet, hadde vi omtrent 4 millioner linjer med eksisterende Python-kode. Det var klart at annotering av all denne koden ville være en krevende oppgave. Vi skapte et verktøy kalt PyAnnotate, som kan samle inn informasjon om typer under kjøring av tester og kan legge til typeannotasjoner i koden basert på innsamlet informasjon. Imidlertid la vi merke til at dette verktøyet ikke ble spesielt mye brukt. Innsamling av typeinformasjon var treg, og automatisk genererte annotasjoner krevde ofte mye manuell redigering. Vi vurderte å kjøre dette verktøyet automatisk ved hver kodesjekk, eller å samle inn typeinformasjon basert på analysen av en viss liten mengde reelle nettverksforespørsel, men bestemte oss for ikke å gjøre dette, da noen av disse tilnærmingene var for risikable.

Til slutt kan det bemerkes at det meste av koden ble manuelt annotert av eierne. For å styre denne prosessen i riktig retning forbereder vi rapporter om spesielt viktige moduler og funksjoner som må annoteres. For eksempel er det viktig å gi typeannotasjoner til bibliotekmodulen som brukes på hundrevis av steder. Men det er ikke så viktig å annotere en gammel tjeneste som blir erstattet av en ny. I tillegg eksperimenterer vi med bruk av statisk analyse for å generere typeannotasjoner for gammel kode.

Sirkulære importerer. Ovenfor snakket jeg om sirkulære importerer (eller "knotter av avhengigheter"), eksistensen av hvilke kompliserte akselerasjonen av mypy. I tillegg måtte vi jobbe hardt for å gi mypy støtte for alle typer idiomer, hvor årsaken til at disse sirkulære importene oppstod. Nylig fullførte vi et stort prosjekt for redesign av systemet, som løste de fleste av mypy-problemene relatert til sirkulære importer. Disse problemene stammer faktisk fra de tidlige dagene av prosjektet, helt fra Alore, programmeringsspråket som prosjektet mypy opprinnelig var rettet mot. Syntaksen til Alore gjør det enkelt å løse problemer med sirkulære importkommandoer. Den moderne mypy har arvet noen begrensninger fra sin tidlige enkle implementering (som fungerte utmerket for Alore). Python kompliserer arbeidet med sirkulære importerer hovedsakelig på grunn av tvetydighet i uttrykk. For eksempel kan et typealias faktisk bli definert i løpet av en tildelingsoperasjon. Mypy er ikke alltid i stand til å oppdage slike ting før mesteparten av importsyklusen er behandlet. I Alore var det ingen slike tvetydigheter. Uheldige avgjørelser tatt i de tidlige utviklingsfasene av systemet kan gi programmereren en ubehagelig overraskelse mange år senere.

Konklusjoner: Veien til 5 millioner linjer med kode og nye horisonter

Prosjektet mypy har kommet en lang vei — fra tidlige prototyper til et system som kontrollerer typer i produksjonskode på 4 millioner linjer. Under utviklingen av mypy ble det gjort standardisering av typehint i Python. I dag har det utviklet seg et kraftig økosystem rundt typingen av Python-kode. Det er rom for støtte for biblioteker, det finnes hjelpemidler for IDE-er og redaktører, og det er flere typekontrollsystemer, hver med sine egne fordeler og ulemper.

Selv om typekontroll allerede betraktes som en selvfølge i Dropbox, er jeg sikker på at vi fortsatt lever i begynnelsen av typingen av Python-kode. Jeg tror at teknologien for typekontroll vil fortsette å utvikle seg og forbedres.

Dersom du ikke har brukt typekontroll i ditt storskala Python-prosjekt, vet at nå er et utmerket tidspunkt for å begynne overgangen til statisk typing. Jeg har snakket med de som har gjort en slik overgang, og ingen av dem har angret. Typekontroll gjør Python til et språk som egner seg mye bedre enn "vanlig Python" for utvikling av store prosjekter.

Kjære lesere! Bruker du typekontroll i Python-prosjektene dine?

Veien til typekontroll av 4 millioner linjer Python-kode. Del 3
Veien til typekontroll av 4 millioner linjer Python-kode. Del 3

Kilde: habr.com

Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere 🔥 Kjøp pålitelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster