La vojo al tipokontrolado de 4 milionoj da linioj de Python-kodo. Parto 1

Hodiaŭ ni atentigas pri la unua parto de la traduko de materialo pri kiel Dropbox traktas Python-kodtipan kontrolon.

La vojo al tipokontrolado de 4 milionoj da linioj de Python-kodo. Parto 1

Dropbox skribas multe en Python. Ĉi tio estas lingvo, kiun ni uzas ege vaste - kaj por backend servoj kaj labortablaj klientaj aplikoj. Ni ankaŭ multe uzas Go, TypeScript kaj Rust, sed Python estas nia ĉefa lingvo. Konsiderante nian skalon, kaj ni parolas pri milionoj da linioj de Python-kodo, montriĝis, ke dinamika tajpado de tia kodo nenecese malfaciligis ĝian komprenon kaj komencis grave influi laborproduktecon. Por mildigi ĉi tiun problemon, ni komencis iom post iom transiri nian kodon al senmova tipo-kontrolado uzante mypy. Ĉi tio verŝajne estas la plej populara memstara tipkontrolsistemo por Python. Mypy estas malfermkoda projekto, ĝiaj ĉefaj programistoj laboras ĉe Dropbox.

Dropbox estis unu el la unuaj kompanioj se temas pri efektivigi statikan tipon-kontroladon en Python-kodo ĉe ĉi tiu skalo. Mypy estas uzata en miloj da projektoj nuntempe. Ĉi tiu ilo estis, kiel oni diras, "provita en batalo" sennombraj fojoj. Ni devis iri longan vojon por atingi kie ni estas nun. Okazis multaj malsukcesaj klopodoj kaj malsukcesaj eksperimentoj survoje. Ĉi tiu peco kronikas la historion de senmova tipkontrolado en Python, de ĝiaj ŝtonaj komencoj kiel parto de mia akademia esplorprojekto ĝis hodiaŭ, kiam tipkontroloj kaj tipaj sugestoj fariĝis ordinaraj inter sennombraj Python-programistoj. Tiuj mekanismoj nun estas apogitaj per gamo da iloj, kiel ekzemple IDEoj kaj kodanaliziloj.

Legu la duan parton

Kial necesas tajpa kontrolo?

Se vi iam uzis dinamike tajpitan Python, vi eble estas iom konfuzita pri kial estas tiom da tumulto pri senmova tajpado kaj mypy lastatempe. Aŭ povas esti, ke vi ŝatas Python ĝuste pro ĝia dinamika tajpado, kaj tio, kio okazas, simple ĝenas vin. La ŝlosilo al la valoro de senmova tajpado estas la skalo de la decidoj: ju pli granda via projekto, des pli vi klinas sin al senmova tajpado, kaj, finfine, des pli vi vere bezonas ĝin.

Ni diru, ke projekto atingis dekojn da miloj da linioj en grandeco, kaj rezultas, ke pluraj programistoj laboras pri ĝi. Konsiderante tian projekton, surbaze de nia sperto, ni povas diri, ke kompreni ĝian kodon estos ŝlosilo por konservi la produktivecon de la programistoj. Sen tipaj komentarioj, povas esti malfacile eltrovi, ekzemple, kiajn argumentojn oni devas transdoni al funkcio, aŭ kiajn valorojn certa funkcio povas redoni. Jen tipaj demandoj, kiujn ofte malfacilas respondi sen uzi tipajn komentadojn:

  • Ĉu ĉi tiu funkcio povas reveni None?
  • Kio devus esti ĉi tiu argumento? items?
  • Kio estas la atributa tipo id: int ĉu, str, aŭ eble iu kutima tipo?
  • Ĉu ĉi tiu argumento estu listo? Ĉu eblas pasigi opon en ĝin?

Se vi rigardas la jenan tipon komentita kodpeceto kaj provas respondi demandojn kiel ĉi tiuj, rezultas, ke ĉi tio estas simpla tasko:

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

  • read_metadata ne revenas Noneĉar la revena tipo ne estas Optional[…].
  • argumenton items estas vico de linioj. Ĝi ne povas esti ripetata en ajna ordo.
  • Atributo id estas ĉeno de bajtoj.

En ideala mondo, oni atendus, ke ĉiuj tiaj subtilaĵoj estus priskribitaj en la enkonstruita dokumentaro (docstring). Sed sperto provizas multajn ekzemplojn, ke tia dokumentado ofte ne estas observata en la kodo, kun kiu vi devas labori. Eĉ se tia dokumentaro ĉeestas en la kodo, vi ne povas kalkuli je ĝia absoluta ĝusteco. Ĉi tiu dokumentaro povas esti neklara, malpreciza kaj malferma al miskompreno. En grandaj teamoj aŭ grandaj projektoj, ĉi tiu problemo povas fariĝi ekstreme akra.

Dum Python elstaras je fruaj aŭ mezfazaj projektoj, iam sukcesaj projektoj kaj kompanioj, kiuj uzas Python, povas renkonti esencan demandon: "Ĉu ni reverku ĉion en statike tajpita lingvo?"

Tipkontrolaj sistemoj kiel mypy solvas ĉi-supran problemon provizante al la programisto formalan lingvon por priskribi tipojn, kaj kontrolante ke tipdeklaroj kongruas kun la programefektivigo (kaj, laŭvole, kontrolante sian ekziston). Ĝenerale, ni povas diri, ke ĉi tiuj sistemoj provizas al ni ion kiel zorge kontrolitan dokumentadon.

La uzo de tiaj sistemoj havas aliajn avantaĝojn, kaj ili estas tute ne bagatelaj:

  • La tipkontrola sistemo povas detekti iujn etajn (kaj ankaŭ ne tiom malgrandajn) erarojn. Tipa ekzemplo estas kiam ili forgesas prilabori valoron None aŭ iu alia speciala kondiĉo.
  • Refaktoriga kodo estas tre simpligita ĉar la tipkontrolilo ofte diras al vi precize kian kodon devas esti ŝanĝita. Samtempe, ni ne bezonas fidi je 100% prova priraportado de la kodo, kio estas kutime neebla ĉiukaze. Ni ne bezonas fosi profunde en stakspuraj raportoj por eltrovi kio estas malĝusta.
  • Eĉ sur grandaj projektoj, mypy ofte povas plenumi kompletan tipkontrolon en frakcio de sekundo. Kaj ruli testojn kutime daŭras dekojn da sekundoj aŭ eĉ minutojn. La tipo-kontrolsistemo donas al la programisto tujan retrosciigon kaj permesas al li fari sian laboron pli rapide. Li ne plu bezonas skribi delikatajn kaj malfacile konserveblajn unutestojn, kiuj anstataŭigas realajn entojn per mokoj kaj flikaĵoj nur por akiri pli rapidajn kodtestrezultojn.

IDEoj kaj redaktiloj kiel ekzemple PyCharm aŭ Visual Studio Code uzas la potencon de tipokotadoj por provizi programistojn per aŭtomata kodkompletigo, erarstarigado kaj subteno por ofte uzataj lingvokonstruoj. Kaj ĉi tiuj estas nur kelkaj el la avantaĝoj, kiujn havigas tipado. Por iuj programistoj, ĉio ĉi estas la ĉefa argumento favore al tajpado. Ĉi tio estas io, kio alportas avantaĝojn tuj post efektivigo. Ĉi tiu uzkazo por tipoj ne postulas apartan tipkontrolsistemon kiel mypy, kvankam oni devas rimarki, ke mypy helpas konservi konsistencon inter tipkomentoj kaj kodo.

Mia fono

La mypy rakonto komenciĝis en Britio, en Kembriĝo, kelkajn jarojn antaŭ ol mi aliĝis al Dropbox. Kadre de mia doktora esploro, mi laboris pri la temo de unuigo de statike tajpitaj kaj dinamikaj lingvoj. Mi estis inspirita de artikolo pri pliiga tajpado de Jeremy Siek kaj Walid Tah, kaj ankaŭ de la projekto Typed Racket. Mi provis trovi manierojn uzi la saman programlingvon por malsamaj projektoj - de malgrandaj skriptoj ĝis kodbazoj konsistantaj el multaj milionoj da linioj. Samtempe mi volis certigi, ke en iu ajn skalo projekto mi ne devos fari tro da kompromisoj. Grava parto de ĉio ĉi estis la ideo iom post iom moviĝi de netajpita prototipa projekto al amplekse provita, statike tajpita preta produkto. Ĉi tiuj ideoj estas plejparte konsiderataj nuntempe, sed en 2010 ĝi estis temo kiu ankoraŭ estis aktive esplorita.

Mia komenca laboro en tipokontrolado ne celis Python. Anstataŭe mi uzis malgrandan "memfaritan" lingvon Alore. Jen ekzemplo por doni al vi ideon pri tio, pri kio ni parolas (tipaj komentarioj estas laŭvolaj):

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

Uzi simpligitan lingvon de via propra dezajno estas ofta aliro uzata en scienca esplorado. Tio estas tiel, ne laste ĉar tio ebligas rapide fari eksperimentojn, kaj ankaŭ ĉar tio, kio ne rilatas al la esplorado, povas esti facile ignorita. Realvivaj programlingvoj tendencas esti grandskalaj fenomenoj kun kompleksaj efektivigoj, kiuj malrapidigas eksperimentadon. Tamen iuj rezultoj bazitaj sur simpligita lingvo estas iom suspektindaj, ĉar akirinte ĉi tiujn rezultojn la esploristo eble oferis konsiderojn gravajn por la praktika uzo de lingvoj.

Mia tajpkontrolilo por Alore aspektis tre promesplena, sed mi volis testi ĝin per eksperimentado kun reala kodo, kiu fakte ne estis skribita en Alore. Feliĉe por mi, la lingvo Alore plejparte baziĝis sur la samaj ideoj kiel Python. Estis sufiĉe facile restrukturi la tipkontrolilon por ke ĝi povu funkcii kun Python-sintakso kaj semantiko. Ĉi tio permesis al ni provi fari tajpkontroladon en malfermfonta Python-kodo. Mi ankaŭ skribis transpililon por konverti Alore-kodon en Python-kodon kaj uzis ĝin por traduki mian tipkontrolan kodon. Nun mi havis tipkontrolsistemon skribitan en Python, kiu subtenis subaron de Python, iun variaĵon de tiu lingvo! (Certaj arkitekturaj decidoj kiuj havis sencon por Alore estis nebone konvenitaj por Python; tio daŭre estas evidenta en kelkaj partoj de la mypy-kodbazo. )

Fakte, la lingvo subtenata de mia tipsistemo ne povus tute esti nomita Python ĉi-momente: ĝi estis varianto de Python pro kelkaj limigoj de la tipanota sintakso de Python 3.

Ĝi aspektis kiel miksaĵo de Java kaj Python:

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

Unu el miaj ideoj tiutempe estis uzi tipajn komentadojn por plibonigi rendimenton kompilante ĉi tiun guston de Python en C, aŭ eble en JVM-bajtkodon. Mi alvenis al la stadio de verkado de prototipa kompililo, sed forlasis ĉi tiun ideon ĉar tipkontrolado mem aspektis sufiĉe utila.

Mi finis prezenti mian projekton ĉe PyCon 2013 en Santa Clara. Pri tio mi ankaŭ parolis kun Guido van Rossum, la bonvola diktatoro de Python dumvive. Li konvinkis min forlasi mian kutiman sintakson kaj resti kun la norma sintakso de Python 3. Python 3 subtenas funkciokotadojn, do mia ekzemplo povus esti reverkita kiel sube, rezultigante normalan Python-programon:

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

Mi devis fari kelkajn kompromisojn (antaŭ ĉio, mi volas atentigi, ke tial mi elpensis mian propran sintakson). Aparte, Python 3.3, la plej lastatempa versio de la lingvo tiutempe, ne subtenis variajn komentadojn. Mi diskutis diversajn sintaksajn eblojn por tiaj komentarioj kun Guido per retpoŝto. Ni decidis uzi tipkomentojn por variabloj. Ĉi tio atingis la celon, sed aspektis iom ĝena (Python 3.6 donis al ni pli belan sintakson):

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

Tapaj komentoj ankaŭ estas utilaj por subteni Python 2, kiu ne havas enkonstruitan subtenon por tipkomentoj:

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

Montriĝis, ke tiuj (kaj aliaj) kompromisoj ne vere gravis - la avantaĝoj de senmova tajpado signifis, ke uzantoj baldaŭ forgesis pri la malpli-ol-perfekta sintakso. Ĉar Python-kodo, kiu kontrolis tipojn, ne plu uzis specialan sintakson, ekzistantaj Python-iloj kaj kodprocezoj daŭre funkciis normale, igante ĝin multe pli facila por programistoj lerni la novan ilon.

Guido ankaŭ konvinkis min aliĝi al Dropbox post kiam mi kompletigis mian altrangan tezon. Ĉi tie komenciĝas la plej interesa afero en la historio de mypy.

Daŭrigota…

Karaj legantoj! Se vi uzas Python, bonvolu rakonti al ni pri la skalo de projektoj, kiujn vi disvolvas en ĉi tiu lingvo.

La vojo al tipokontrolado de 4 milionoj da linioj de Python-kodo. Parto 1
La vojo al tipokontrolado de 4 milionoj da linioj de Python-kodo. Parto 1

fonto: www.habr.com

Aldoni komenton