Vă prezentăm partea a treia a traducerii materialului despre parcursul companiei Dropbox în implementarea sistemului de verificare a tipurilor de cod Python.
→ Părțile anterioare: și
Realizarea a 4 milioane de linii de cod tipizat
O altă sarcină importantă (aceasta fiind a doua problemă ca popularitate care a stârnit interesul celor care au participat la sondajele interne) a fost creșterea volumului de cod din Dropbox acoperit de verificările de tipuri. Am încercat mai multe abordări pentru a rezolva această problemă — de la creșterea naturală a volumului bazei de cod tipizată până la concentrare pe eforturile membrilor echipei mypy pe inferența statică și dinamică a tipurilor automatizate. În final, a fost creată impresia că nu există o strategie câștigătoare simplă, dar am reușit să obținem o creștere rapidă a volumului de cod anunțat, combinând mai multe abordări.
Astfel, în cel mai mare depozit Python al nostru (cu cod de backend), numărul de linii de cod anunțat a ajuns la aproape 4 milioane. Lucrările privind tipizarea statică a codului au fost desfășurate pe parcursul a aproximativ trei ani. Mypy acum suportă diverse tipuri de rapoarte despre acoperirea codului cu tipuri, care simplifică monitorizarea progresului tipizării. În special, putem genera rapoarte pentru cod cu ambiguități în tipuri, cum ar fi utilizarea explicită a tipului Any în adnotări, care nu pot fi verificate, sau în cazul importului de biblioteci externe care nu au adnotări de tipuri. În cadrul proiectului nostru de îmbunătățire a preciziei verificării tipurilor în Dropbox, am contribuit la îmbunătățirea definițiilor de tipuri (așa-numitele fișiere stub) pentru unele biblioteci popolare open-source în depozitul centralizat Python .
Am implementat (și standardizat în PEP-urile ulterioare) noi capabilități ale sistemului de tipuri, care permit utilizarea unor tipuri mai precise pentru anumite tipare specifice Python. Un exemplu notabil al acestui lucru este TypeDict, care oferă tipuri pentru dicționarele asemănătoare JSON cu un set fix de chei string, fiecare având o valoare de tip propriu. Vom continua să extindem sistemul de tipuri. Probabil că următorul nostru pas va fi îmbunătățirea suportului pentru capabilitățile Python de gestionare a numerelor.

Numărul de linii de cod anunțat: server

Numărul de linii de cod annotate: client

Numărul total de linii de cod annotate
Iată o prezentare generală a principalelor caracteristici ale acțiunilor pe care le-am desfășurat pentru a crește volumul de cod annotate în Dropbox:
Rigurozitatea anotării. Am crescut treptat cerințele privind rigurozitatea anotării noului cod. Am început cu sugestiile linters-ului, care recomandau adăugarea de anotații în fișierele care aveau deja unele anotații. Acum cerem prezența anotațiilor de tip în fișierele noi Python și în majoritatea fișierelor existente.
Rapoartele de tipare. Trimitem echipelor rapoarte săptămânale privind nivelul de tipare a codului lor și oferim sugestii despre ce anume ar trebui să fie annotate mai întâi.
Promovarea mypy. Vorbim despre mypy la diferite evenimente și comunicăm cu echipele, ajutându-le să înceapă să folosească anotațiile de tip.
Sondajele. Efectuăm sondaje periodice în rândul utilizatorilor pentru a identifica problemele principale. Suntem pregătiți să mergem destul de departe în soluționarea acestor probleme (chiar și creând o nouă limbă pentru a accelera mypy!).
Performanța. Am îmbunătățit semnificativ performanța mypy prin utilizarea demonului și a mypyc. Acest lucru a fost realizat pentru a netezi disconforturile apărute în procesul de annotare și pentru a permite lucrul cu volume mari de cod.
Integrarea cu editorii. Am creat instrumente pentru a sprijini rularea mypy în editorii populari în Dropbox. Acestea includ PyCharm, Vim și VS Code. Acest lucru a simplificat semnificativ procesul de realizare a lucrărilor de annotare a codului și de verificare a funcționării sale. Astfel de acțiuni sunt de obicei caracteristice atunci când se anotează cod existent.
Analiza statică. Am creat un instrument pentru generarea semnăturilor 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 codului cu tipuri fără prea mult efort.
Suport pentru biblioteci externe. În multe dintre proiectele noastre folosim un set de instrumente SQLAlchemy. Acesta utilizează capabilitățile dinamice ale Python, pe care tipurile PEP 484 nu le pot modela direct. Conform PEP 561, am creat un fișier stub corespunzător și am scris un plugin pentru mypy.), îmbunătățind suportul pentru SQLAlchemy.
Dificultățile întâmpinate
Drumul către 4 milioane de linii de cod tipizat nu a fost întotdeauna ușor. Pe acest parcurs, am întâlnit multe obstacole și am făcut câteva greșeli. Iată câteva dintre problemele cu care ne-am confruntat. Sperăm că povestirea acestora va ajuta alții să evite probleme similare.
Fișiere omise. Am început lucrul verificând doar un volum mic de fișiere. Tot ce nu se afla în numărul acestor fișiere nu era verificat. Fișierele erau adăugate pe lista de verificare atunci când apăreau primele comentarii. Dacă ceva era importat dintr-un modul situat în afara domeniului de verificare, era vorba despre lucrul cu valori de tip Any, care nu erau verificate deloc. Aceasta a dus la o pierdere semnificativă a preciziei tipizării, în special în etapele timpurii ale migrației. Această abordare a funcționat surprinzător de bine, deși situația tipică era că adăugarea fișierelor în domeniul de verificare scotea la iveală probleme în alte părți ale bazei de cod. În cel mai rău caz, când se îmbinau două domenii izolate de cod, în care, în mod independent unul de celălalt, tipurile fuseseră deja verificate, se descoperea că tipurile acestor domenii nu erau compatibile între ele. Aceasta a dus la necesitatea de a face multe modificări în comentarii. Acum, privind înapoi, ne dăm seama că ar fi trebuit să adăugăm cât mai curând posibil modulele bibliotecare de bază în domeniul de verificare a tipurilor mypy. Aceasta ar fi făcut munca noastră mult mai previzibilă.
Annotarea codului vechi. Când am început lucrul, aveam aproximativ 4 milioane de linii de cod Python existent. Era clar că annotarea întregului acestui cod era o sarcină dificilă. Am creat un instrument numit PyAnnotate, care poate colecta informații despre tipuri în timpul testelor și poate adăuga în cod anotări de tip, bazându-se pe informațiile colectate. Cu toate acestea, nu am observat o adoptare foarte largă a acestui instrument. Colectarea informațiilor despre tipuri era lentă, iar anotările generate automat necesitau adesea multe modificări manuale. Ne-am gândit la lansarea automată a acestui instrument la fiecare verificare a codului sau să colectăm informațiile despre tipuri, bazându-ne pe analiza unui volum mic de cereri reale de rețea, dar am decis să nu facem acest lucru, deoarece oricare dintre aceste abordări era prea riscant.
În concluzie, se poate menționa că cea mai mare parte a codului a fost annotată manual de către proprietarii săi. Noi, pentru a orienta acest proces în direcția corectă, pregătim rapoarte pentru modulele și funcțiile deosebit de importante care trebuie annotate. De exemplu, este important să furnizăm anotări de tip pentru un modul de bibliotecă utilizat în sute de locuri. În schimb, un serviciu vechi, care este înlocuit cu unul nou, nu mai este la fel de important să fie annotat. De asemenea, experimentăm cu utilizarea analizei statice pentru a genera anotări de tip pentru codul vechi.
Importuri circulare. Am menționat mai sus despre importurile circulare (despre "ghemotocurile de dependențe"), al căror existență a complicat accelerarea mypy. De asemenea, a fost necesar să lucrăm serios pentru a oferi mypy suport pentru toate tipurile de idiomuri, cauzate de aceste importuri circulare. Recent, am finalizat un proiect major de redesign al sistemului, care a remediat majoritatea problemelor mypy legate de importurile circulare. Aceste probleme, de fapt, își au rădăcinile în vremurile foarte timpurii ale proiectului, încă din Alore, limbajul de învățare pe care s-a concentrat inițial proiectul mypy. Sintaxa Alore permite rezolvarea ușoară a problemelor cu importurile circulare. Mypy modern a moștenit anumite limitări de la implementarea sa timpurie simplă (care se potrivea perfect pentru Alore). Python complică lucrul cu importurile circulare, în principal din cauza ambiguității expresiilor. De exemplu, în timpul operațiunii de atribuție a unei valori, aliasul tipului poate fi, de fapt, definit. Mypy nu este întotdeauna capabil să identifice astfel de lucruri până când cea mai mare parte a ciclului de import nu este procesată. În Alore, astfel de ambiguități nu existau. Deciziile nereușite luate în etapele timpurii de dezvoltare a sistemului pot aduce programatorului o surpriză neplăcută mulți ani mai târziu.
Concluzii: drumul către 5 milioane de linii de cod și noi orizonturi
Proiectul mypy a parcurs un drum lung — de la prototipuri timpurii până la un sistem care controlează tipurile codului de producție de 4 milioane de linii. Pe parcursul dezvoltării mypy, s-a realizat standardizarea sugestiilor de tip în Python. În zilele noastre, în jurul tipizării codului Python s-a dezvoltat un ecosistem puternic. Acesta include suport pentru biblioteci, instrumente pentru IDE și editori, și există mai multe sisteme de control al tipurilor, fiecare având propriile plusuri și minusuri.
Deși verificarea tipurilor este deja percepută în Dropbox ca un lucru evident, sunt sigur că încă trăim la începuturile tipizării codului Python. Cred că tehnologiile de verificare a tipurilor vor continua să se dezvolte și să se perfeționeze.
Dacă nu ați folosit până acum verificările de tip în proiectul dumneavoastră Python la scară largă, acum este un moment foarte potrivit să începeți tranziția la tipizarea statică. Am discutat cu cei care au realizat o astfel de tranziție. Nimeni dintre ei nu a regretat acest lucru. Controlul tipurilor transformă Python într-o limbaj care este mult mai potrivit pentru dezvoltarea de proiecte mari decât "Python normal".
Stimați cititori! Folosiți controlul tipurilor în proiectele dumneavoastră Python?
Sursa: habr.com
