Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3

Vă prezentăm atenției a treia parte a traducerii de material despre calea pe care a urmat-o Dropbox la implementarea unui sistem de verificare a tipului pentru codul Python.

Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3

→ Piese anterioare: în primul rând и în al doilea rând

Se ajunge la 4 milioane de linii de cod tastat

O altă provocare majoră (și a doua cea mai frecventă preocupare printre cei chestionați intern) a fost creșterea cantității de cod acoperite de verificările de tip în Dropbox. Am încercat mai multe abordări pentru a rezolva această problemă, de la creșterea naturală a dimensiunii bazei de cod tipat până la concentrarea eforturilor echipei mypy pe inferența de tip automată statică și dinamică. În cele din urmă, părea că nu există o strategie simplă de câștig, dar am reușit să obținem o creștere rapidă a volumului de cod adnotat prin combinarea mai multor abordări.

Ca rezultat, cel mai mare depozit Python al nostru (cu cod backend) are aproape 4 milioane de linii de cod adnotat. Lucrarea de tastare statică a codului a fost finalizată în aproximativ trei ani. Mypy acceptă acum diverse tipuri de rapoarte de acoperire a codului, care facilitează monitorizarea progresului de tastare. În special, putem genera rapoarte privind codul cu ambiguități în tipuri, cum ar fi, de exemplu, utilizarea explicită a unui tip Any în adnotări care nu pot fi verificate sau cu lucruri precum importarea bibliotecilor terță parte care nu au adnotări de tip. Ca parte a unui proiect de îmbunătățire a acurateței verificării tipului în Dropbox, am contribuit la îmbunătățirea definițiilor de tip (așa-numitele fișiere stub) pentru unele biblioteci open source populare într-un depozit Python centralizat. dactilografiat.

Am implementat (și am standardizat în PEP-urile ulterioare) noi caracteristici ale sistemului de tipuri care permit tipuri mai precise pentru anumite modele Python specifice. Un exemplu notabil în acest sens este TypeDict, care oferă tipuri pentru dicționare asemănătoare JSON care au un set fix de chei șir, fiecare cu o valoare de tipul său. Vom continua să extindem sistemul de tip. Următorul nostru pas va fi probabil îmbunătățirea suportului pentru capacitățile numerice ale lui Python.

Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3
Numărul de linii de cod adnotat: server

Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3
Numărul de linii de cod adnotat: client

Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3
Numărul total de linii de cod adnotat

Iată o prezentare generală a principalelor caracteristici ale lucrurilor pe care le-am făcut pentru a crește cantitatea de cod adnotat în Dropbox:

Rigoarea adnotărilor. Am crescut treptat cerințele pentru rigoarea adnotării noului cod. Am început cu sfaturi linter care sugerau adăugarea de adnotări la fișierele care aveau deja unele adnotări. Acum avem nevoie de adnotări de tip în fișierele Python noi și în majoritatea fișierelor existente.

Rapoarte de tastare. Trimitem echipelor rapoarte săptămânale despre nivelul de tastare a codului lor și oferim sfaturi despre ce ar trebui adnotat mai întâi.

Popularizarea lui mypy. Vorbim despre mypy la evenimente și vorbim cu echipele pentru a le ajuta să înceapă cu adnotări de tip.

Sondaje. Efectuăm sondaje periodice ale utilizatorilor pentru a identifica probleme majore. Suntem gata să mergem destul de departe în rezolvarea acestor probleme (chiar creând un nou limbaj pentru a accelera mypy!).

Performanţă. Am îmbunătățit considerabil performanța mypy utilizând demonul și mypyc. Acest lucru a fost făcut pentru a netezi inconvenientele care apar în timpul procesului de adnotare și pentru a putea lucra cu cantități mari de cod.

Integrare cu editorii. Am creat instrumente pentru a sprijini rularea mypy în editoarele care sunt populare pe Dropbox. Aceasta include PyCharm, Vim și VS Code. Acest lucru a simplificat foarte mult procesul de adnotare a codului și verificarea funcționalității acestuia. Aceste tipuri de acțiuni sunt frecvente atunci când se adnotă codul existent.

Analiza statica. Am creat un instrument pentru a deduce semnăturile funcțiilor folosind instrumente de analiză statică. Acest instrument poate funcționa doar în situații relativ simple, dar ne-a ajutat să creștem acoperirea tipului de cod fără prea mult efort.

Suport pentru biblioteci terțe. Multe dintre proiectele noastre folosesc setul de instrumente SQLAlchemy. Profită de capacitățile dinamice ale Python pe care tipurile PEP 484 nu le pot modela direct. Noi, în conformitate cu PEP 561, am creat fișierul stub corespunzător și am scris un plugin pentru mypy (sursa deschisa), care îmbunătățește suportul SQLAlchemy.

Dificultăți pe care le-am întâlnit

Calea către 4 milioane de linii de cod nu a fost întotdeauna ușoară pentru noi. Pe această potecă am întâlnit multe gropi și am făcut mai multe greșeli. Acestea sunt câteva dintre problemele pe care le-am întâlnit. Sperăm că vorbirea despre ele îi va ajuta pe alții să evite probleme similare.

Fișiere lipsă. Ne-am început munca verificând doar o cantitate mică de fișiere. Orice nu este inclus în aceste fișiere nu a fost verificat. Fișierele au fost adăugate la lista de scanare când au apărut primele adnotări în ele. Dacă ceva a fost importat dintr-un modul aflat în afara domeniului de verificare, atunci vorbeam despre lucrul cu valori precum Any, care nu au fost testate deloc. Acest lucru a dus la o pierdere semnificativă a preciziei de tastare, mai ales în primele etape ale migrației. Această abordare a funcționat surprinzător de bine până acum, deși o situație tipică este aceea că adăugarea de fișiere în domeniul de aplicare a revizuirii dezvăluie probleme în alte părți ale bazei de cod. În cel mai rău caz, atunci când două zone izolate de cod au fost comasate, în care, independent una de alta, tipurile erau deja verificate, s-a dovedit că tipurile acestor zone erau incompatibile între ele. Acest lucru a condus la necesitatea de a face multe modificări adnotărilor. Privind în urmă acum, ne dăm seama că ar fi trebuit să adăugam mai devreme module de bibliotecă de bază în zona de verificare a tipului a lui mypy. Acest lucru ne-ar face munca mult mai previzibilă.

Adnotarea codului vechi. Când am început, aveam aproximativ 4 milioane de linii de cod Python existent. Era clar că adnotarea întregului cod nu era o sarcină ușoară. Am creat un instrument numit PyAnnotate care poate colecta informații de tip pe măsură ce testele rulează și poate adăuga adnotări de tip la codul dvs. pe baza informațiilor colectate. Cu toate acestea, nu am observat o adoptare deosebit de răspândită a acestui instrument. Colectarea informațiilor de tip a fost lentă, iar adnotările generate automat au necesitat adesea multe editări manuale. Ne-am gândit să rulăm automat acest instrument de fiecare dată când examinăm codul sau să colectăm informații de tip pe baza analizei unui volum mic de solicitări reale de rețea, dar am decis să nu o facem, deoarece oricare abordare era prea riscantă.

Drept urmare, se poate observa că cea mai mare parte a codului a fost adnotat manual de proprietarii săi. Pentru a ghida acest proces în direcția corectă, pregătim rapoarte despre module și funcții deosebit de importante care trebuie adnotate. De exemplu, este important să furnizați adnotări de tip pentru un modul de bibliotecă care este utilizat în sute de locuri. Dar un serviciu vechi care este înlocuit cu unul nou nu mai este atât de important de adnotat. De asemenea, experimentăm utilizarea analizei statice pentru a genera adnotări de tip pentru codul moștenit.

Importuri ciclice. Mai sus, am vorbit despre importurile ciclice („încurcăturile de dependență”), a căror existență a făcut dificilă accelerarea mypy. De asemenea, a trebuit să muncim din greu pentru ca mypy să accepte tot felul de expresii care sunt cauzate de aceste importuri ciclice. Am finalizat recent un proiect major de reproiectare a sistemului care a remediat majoritatea problemelor mypy legate de importurile circulare. Aceste probleme au provenit de fapt din primele zile ale proiectului, înapoi de la Alore, limbajul educațional pe care sa concentrat inițial proiectul mypy. Sintaxa Alore facilitează rezolvarea problemelor cu comenzile de import ciclic. Mypy modern a moștenit unele limitări de la implementarea sa anterioară, simplă (care se potrivea foarte bine pentru Alore). Python îngreunează lucrul cu importurile circulare, în principal pentru că expresiile sunt ambigue. De exemplu, o operație de atribuire poate defini de fapt un alias de tip. Mypy nu este întotdeauna capabil să detecteze astfel de lucruri până când cea mai mare parte a buclei de import nu a fost procesată. Nu existau astfel de ambiguități în Alore. Deciziile proaste luate în etapele incipiente ale dezvoltării sistemului pot prezenta o surpriză neplăcută pentru programator mulți ani mai târziu.

Rezultate: calea către 5 milioane de linii de cod și noi orizonturi

Proiectul mypy a parcurs un drum lung - de la prototipuri timpurii la un sistem care controlează 4 milioane de linii de tipuri de cod de producție. Pe măsură ce mypy a evoluat, indicațiile de tip Python au fost standardizate. În zilele noastre, s-a dezvoltat un ecosistem puternic în jurul tastării codului Python. Are un loc pentru suportul bibliotecii, conține instrumente auxiliare pentru IDE-uri și editori, are mai multe sisteme de control de tip, fiecare având propriile sale avantaje și dezavantaje.

Chiar dacă verificarea tipului este deja o dată la Dropbox, cred că suntem încă în primele zile ale tastării codului Python. Cred că tehnologiile de verificare a tipului vor continua să evolueze și să se îmbunătățească.

Dacă nu ați folosit deja verificarea tipului în proiectul dvs. Python la scară largă, atunci știți că acum este un moment foarte bun pentru a începe să treceți la tastarea statică. Am vorbit cu cei care au făcut o tranziție similară. Niciunul dintre ei nu a regretat. Verificarea tipului face din Python un limbaj care este mult mai potrivit pentru dezvoltarea proiectelor mari decât „Python-ul obișnuit”.

Dragi cititori! Folosiți verificarea tipului în proiectele dvs. Python?

Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3
Calea către verificarea tipului a 4 milioane de linii de cod Python. Partea 3

Sursa: www.habr.com

Adauga un comentariu