CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 1. daļa

Šodien mēs piedāvājam jūsu uzmanībai pirmo daļu no materiāla par to, kā Dropbox nodarbojas ar Python koda tipa kontroli.

CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 1. daļa

Dropbox daudz raksta Python. Å Ä« ir valoda, ko mēs izmantojam ļoti plaÅ”i ā€” gan aizmugures pakalpojumiem, gan darbvirsmas klientu lietojumprogrammām. Mēs arÄ« daudz lietojam Go, TypeScript un Rust, bet Python ir mÅ«su galvenā valoda. Ņemot vērā mÅ«su mērogu, un mēs runājam par miljoniem Python koda rindu, izrādÄ«jās, ka Ŕāda koda dinamiskā rakstÄ«Å”ana nevajadzÄ«gi sarežģīja tā izpratni un sāka nopietni ietekmēt darba ražīgumu. Lai mazinātu Å”o problēmu, esam sākuÅ”i pakāpeniski pārvietot savu kodu uz statisko tipa pārbaudi, izmantojot mypy. Å Ä«, iespējams, ir vispopulārākā Python atseviŔķa tipa pārbaudes sistēma. Mypy ir atvērtā koda projekts, tā galvenie izstrādātāji strādā Dropbox.

Dropbox bija viens no pirmajiem uzņēmumiem, kas ieviesa statisko tipa pārbaudi Python kodā Ŕādā mērogā. MÅ«sdienās Mypy tiek izmantots tÅ«kstoÅ”iem projektu. Å is rÄ«ks, kā saka, ir neskaitāmas reizes ā€œpārbaudÄ«ts kaujāā€. Mums bija jānoiet garÅ” ceļŔ, lai nokļūtu tur, kur esam tagad. Pa ceļam ir bijuÅ”i daudzi neveiksmÄ«gi centieni un neveiksmÄ«gi eksperimenti. Å ajā rakstā ir aprakstÄ«ta Python statiskās tipa pārbaudes vēsture, sākot no tās klinÅ”ainajiem pirmsākumiem kā daļa no mana akadēmiskā izpētes projekta lÄ«dz mÅ«sdienām, kad tipa pārbaudes un tipa mājieni ir kļuvuÅ”i par ierastu parādÄ«bu neskaitāmu Python izstrādātāju vidÅ«. Å os mehānismus tagad atbalsta dažādi rÄ«ki, piemēram, IDE un kodu analizatori.

ā†’ Izlasi otro daļu

Kāpēc ir nepiecieÅ”ama tipa pārbaude?

Ja kādreiz esat izmantojis dinamiski drukātu Python, jÅ«s varētu bÅ«t nedaudz neizpratnē par to, kāpēc pēdējā laikā ir bijis tik daudz satraukumu par statisko rakstÄ«Å”anu un mypy. Vai arÄ« var gadÄ«ties, ka Python jums patÄ«k tieÅ”i tā dinamiskās rakstÄ«Å”anas dēļ, un notiekoÅ”ais jÅ«s vienkārÅ”i apbēdina. Statiskās rakstÄ«Å”anas vērtÄ«bas atslēga ir lēmumu mērogs: jo lielāks ir jÅ«su projekts, jo vairāk jÅ«s sliecaties uz statisko rakstÄ«Å”anu un galu galā, jo vairāk jums tas patieŔām ir vajadzÄ«gs.

Pieņemsim, ka projekts ir sasniedzis desmitiem tÅ«kstoÅ”u lÄ«niju lielumu, un izrādās, ka pie tā strādā vairāki programmētāji. Apsverot Ŕādu projektu, pamatojoties uz mÅ«su pieredzi, mēs varam teikt, ka tā koda izpratne bÅ«s atslēga izstrādātāja produktivitātes uzturÄ“Å”anai. Bez tipa anotācijām var bÅ«t grÅ«ti izdomāt, piemēram, kādi argumenti jānodod funkcijai vai kāda veida vērtÄ«bas noteikta funkcija var atgriezt. Å eit ir tipiski jautājumi, uz kuriem bieži ir grÅ«ti atbildēt, neizmantojot veida anotācijas:

  • Vai Ŕī funkcija var atgriezties None?
  • Kādam vajadzētu bÅ«t Å”im argumentam? items?
  • Kas ir atribÅ«ta veids id: int vai tas ir, str, vai varbÅ«t kāds pielāgots veids?
  • Vai Å”im argumentam vajadzētu bÅ«t sarakstam? Vai tajā ir iespējams nodot korteču?

Ja skatāties uz tālāk norādÄ«to tipa anotēto koda fragmentu un mēģināt atbildēt uz Ŕādiem jautājumiem, izrādās, ka tas ir vienkārÅ”s uzdevums:

class Resource:
    id: bytes
    ...
    def read_metadata(self, 
                      items: Sequence[str]) -> Dict[str, MetadataItem]:
        ...

  • read_metadata neatgriežas Nonejo atgrieÅ”anās veids nav Optional[ā€¦].
  • Arguments items ir lÄ«niju secÄ«ba. To nevar atkārtot jebkurā secÄ«bā.
  • AtribÅ«ts id ir baitu virkne.

Ideālā pasaulē varētu sagaidÄ«t, ka visi Ŕādi smalkumi bÅ«tu aprakstÄ«ti iebÅ«vētajā dokumentācijā (docstring). Taču pieredze sniedz daudz piemēru, ka kodā, ar kuru jāstrādā, Ŕāda dokumentācija bieži netiek ievērota. Pat ja Ŕāda dokumentācija ir kodā, jÅ«s nevarat paļauties uz tās absolÅ«to pareizÄ«bu. Å Ä« dokumentācija var bÅ«t neskaidra, neprecÄ«za un iespējama pārpratumiem. Lielās komandās vai lielos projektos Ŕī problēma var kļūt ārkārtÄ«gi aktuāla.

Lai gan Python ir izcils agrīnā vai vidējā posma projektos, kādā brīdī veiksmīgi projekti un uzņēmumi, kas izmanto Python, var saskarties ar būtisku jautājumu: "Vai mums viss ir jāpārraksta statiski drukātā valodā?"

Tipa pārbaudes sistēmas, piemēram, mypy, atrisina iepriekÅ” minēto problēmu, nodroÅ”inot izstrādātājam formālu valodu tipu aprakstÄ«Å”anai un pārbaudot, vai tipa deklarācijas atbilst programmas ievieÅ”anai (un, pēc izvēles, pārbaudot to esamÄ«bu). Kopumā mēs varam teikt, ka Ŕīs sistēmas sniedz mums kaut ko lÄ«dzÄ«gu rÅ«pÄ«gi pārbaudÄ«tai dokumentācijai.

Šādu sistēmu izmantoÅ”anai ir arÄ« citas priekÅ”rocÄ«bas, un tās ir pilnÄ«gi nenozÄ«mÄ«gas:

  • Tipa pārbaudes sistēma var atklāt dažas nelielas (un arÄ« ne tik nelielas) kļūdas. Tipisks piemērs ir gadÄ«jumi, kad viņi aizmirst apstrādāt vērtÄ«bu None vai kāds cits Ä«paÅ”s nosacÄ«jums.
  • PārveidoÅ”anas kods ir ievērojami vienkārÅ”ots, jo tipa pārbaudÄ«tājs bieži vien precÄ«zi norāda, kāds kods ir jāmaina. Tajā paŔā laikā mums nav jāpaļaujas uz 100% koda pārbaudes pārklājumu, kas parasti jebkurā gadÄ«jumā nav iespējams. Mums nav jāiedziļinās steka izsekoÅ”anas pārskatos, lai noskaidrotu, kas ir nepareizi.
  • Pat lielos projektos mypy bieži vien var veikt pilnÄ«gu tipa pārbaudi sekundes daļā. Un testu izpilde parasti aizņem desmitiem sekunžu vai pat minÅ«Å”u. Tipa pārbaudes sistēma programmētājam sniedz tÅ«lÄ«tēju atgriezenisko saiti un ļauj ātrāk paveikt savu darbu. Viņam vairs nav jāraksta trausli un grÅ«ti uzturējami vienÄ«bu testi, kas reālas entÄ«tijas aizstāj ar viltojumiem un ielāpiem, lai tikai iegÅ«tu ātrākus koda pārbaudes rezultātus.

IDE un redaktori, piemēram, PyCharm vai Visual Studio Code, izmanto tipa anotācijas, lai izstrādātājiem nodroÅ”inātu automātisku koda pabeigÅ”anu, kļūdu izcelÅ”anu un atbalstu bieži lietotām valodas konstrukcijām. Un Ŕīs ir tikai dažas no priekÅ”rocÄ«bām, ko sniedz tipizācija. Dažiem programmētājiem tas viss ir galvenais arguments par labu rakstÄ«Å”anai. Tas ir kaut kas tāds, kas dod labumu tÅ«lÄ«t pēc ievieÅ”anas. Å im tipu lietoÅ”anas gadÄ«jumam nav nepiecieÅ”ama atseviŔķa tipa pārbaudes sistēma, piemēram, mypy, lai gan jāņem vērā, ka mypy palÄ«dz uzturēt konsekvenci starp tipa anotācijām un kodu.

Mypy fons

Neparastais stāsts sākās Apvienotajā Karalistē, Kembridžā, dažus gadus pirms pievienoÅ”anās Dropbox. Promocijas darba ietvaros es strādāju pie jautājuma par statiski tipizētu un dinamisku valodu apvienoÅ”anu. Mani iedvesmoja Džeremija SÄ«ka un Valida Tah raksts par pakāpenisku rakstÄ«Å”anu, kā arÄ« projekts Typed Racket. Es mēģināju atrast veidus, kā izmantot vienu un to paÅ”u programmÄ“Å”anas valodu dažādiem projektiem - no maziem skriptiem lÄ«dz kodu bāzēm, kas sastāv no daudziem miljoniem lÄ«niju. Tajā paŔā laikā es gribēju nodroÅ”ināt, lai jebkura mēroga projektā man nebÅ«tu jāieiet pārāk daudz kompromisu. SvarÄ«ga tā visa sastāvdaļa bija ideja pakāpeniski pāriet no netipēta prototipa projekta uz vispusÄ«gi pārbaudÄ«tu, statiski drukātu gatavo produktu. Å Ä«s idejas mÅ«sdienās lielākoties tiek uzskatÄ«tas par paÅ”saprotamām, taču 2010. gadā tas bija jautājums, kas joprojām tika aktÄ«vi pētÄ«ts.

Mans sākotnējais darbs tipa pārbaudē nebija vērsts uz Python. Tā vietā es izmantoju nelielu "mājas" valodu Alore. Å is ir piemērs, lai sniegtu priekÅ”statu par to, par ko mēs runājam (tipa anotācijas nav obligātas):

def Fib(n as Int) as Int
  if n <= 1
    return n
  else
    return Fib(n - 1) + Fib(n - 2)
  end
end

Sava dizaina vienkārÅ”otas valodas izmantoÅ”ana ir izplatÄ«ta pieeja zinātniskajos pētÄ«jumos. Tas tā ir ne tikai tāpēc, ka tas ļauj ātri veikt eksperimentus, kā arÄ« tāpēc, ka to, kas neattiecas uz pētÄ«jumu, var viegli ignorēt. Reālās dzÄ«ves programmÄ“Å”anas valodas mēdz bÅ«t liela mēroga parādÄ«bas ar sarežģītām implementācijām, kas palēnina eksperimentÄ“Å”anu. Tomēr visi rezultāti, kas balstÄ«ti uz vienkārÅ”otu valodu, ir nedaudz aizdomÄ«gi, jo, iegÅ«stot Å”os rezultātus, pētnieks, iespējams, ir upurējis apsvērumus, kas ir svarÄ«gi valodu praktiskajam lietojumam.

Mans Alore tipa pārbaudÄ«tājs izskatÄ«jās ļoti daudzsoloÅ”s, taču es gribēju to pārbaudÄ«t, eksperimentējot ar reālu kodu, kas patiesÄ«bā nebija rakstÄ«ts Alore. Man par laimi, Alore valoda lielā mērā balstÄ«jās uz tām paŔām idejām kā Python. Bija pietiekami viegli pārveidot tipa pārbaudÄ«tāju, lai tas darbotos ar Python sintaksi un semantiku. Tas ļāva mums mēģināt veikt tipa pārbaudi atvērtā pirmkoda Python kodā. Es arÄ« uzrakstÄ«ju transpilatoru, lai pārveidotu Alore kodu Python kodā, un izmantoju to, lai tulkotu sava tipa pārbaudÄ«tāja kodu. Tagad man bija Python rakstÄ«ta tipa pārbaudes sistēma, kas atbalstÄ«ja Python apakÅ”kopu, dažas Ŕīs valodas variācijas! (Noteikti arhitektÅ«ras lēmumi, kas bija saprātÄ«gi Alore, bija slikti piemēroti Python; tas joprojām ir acÄ«mredzams dažās mypy kodu bāzes daļās.)

PatiesÄ«bā valodu, ko atbalsta mana tipa sistēma, Å”obrÄ«d nevarēja saukt par Python: tas bija Python variants dažu Python 3 tipa anotācijas sintakses ierobežojumu dēļ.

Tas izskatījās kā Java un Python maisījums:

int fib(int n):
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Viena no manām idejām tajā laikā bija izmantot tipa anotācijas, lai uzlabotu veiktspēju, apkopojot Å”o Python versiju C formātā vai, iespējams, JVM baitkodā. Es nonācu lÄ«dz prototipa kompilatora rakstÄ«Å”anas stadijai, bet atteicos no Ŕīs idejas, jo pati tipa pārbaude izskatÄ«jās diezgan noderÄ«ga.

Es beidzot prezentēju savu projektu PyCon 2013 Santaklārā. Es par to runāju arÄ« ar Gvido van Rosumu, Pitona labvēlÄ«go diktatoru uz mūžu. ViņŔ pārliecināja mani atteikties no pielāgotās sintakses un pieturēties pie standarta Python 3 sintakses. Python 3 atbalsta funkciju anotācijas, tāpēc manu piemēru var pārrakstÄ«t, kā norādÄ«ts tālāk, kā rezultātā tiks izveidota parasta Python programma:

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Man bija jāpieņem daži kompromisi (vispirms vēlos norādÄ«t, ka tāpēc es izgudroju savu sintaksi). Jo Ä«paÅ”i Python 3.3, tajā laikā jaunākā valodas versija, neatbalstÄ«ja mainÄ«gās anotācijas. Ar Gvido pa e-pastu apspriedu dažādas sintaktiskās iespējas Ŕādām anotācijām. Mēs nolēmām mainÄ«gajiem izmantot tipa komentārus. Tas sasniedza mērÄ·i, taču izskatÄ«jās nedaudz apgrÅ«tinoÅ”i (Python 3.6 deva mums jaukāku sintaksi):

products = []  # type: List[str]  # Eww

Rakstu komentāri ir noderīgi arī Python 2 atbalstam, kuram nav iebūvēta veida anotāciju atbalsta:

f fib(n):
    # type: (int) -> int
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

IzrādÄ«jās, ka Å”iem (un citiem) kompromisiem Ä«sti nebija nozÄ«mes ā€“ statiskās rakstÄ«Å”anas priekÅ”rocÄ«bas nozÄ«mēja, ka lietotāji drÄ«z vien aizmirsa par ne tik perfekto sintaksi. Tā kā Python kods, kas kontrolēja tipus, vairs neizmantoja Ä«paÅ”u sintaksi, esoÅ”ie Python rÄ«ki un koda procesi turpināja darboties normāli, padarot izstrādātājiem daudz vieglāk apgÅ«t jauno rÄ«ku.

Gvido arī pārliecināja mani pievienoties Dropbox pēc tam, kad pabeidzu savu vecāko darbu. Šeit sākas visinteresantākā lieta mypy vēsturē.

Lai varētu turpināt ...

Cienījamie lasītāji! Ja izmantojat Python, lūdzu, pastāstiet mums par projektu apjomu, ko izstrādājat Ŕajā valodā.

CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 1. daļa
CeļŔ uz tipa pārbaudi 4 miljonos Python koda rindiņu. 1. daļa

Avots: www.habr.com

Pievieno komentāru