Y llwybr i deipio 4 miliwn o linellau o god Python. Rhan 1

Heddiw rydyn ni'n tynnu eich sylw at ran gyntaf y cyfieithiad o'r deunydd ar sut mae Dropbox yn delio â rheoli math o god Python.

Y llwybr i deipio 4 miliwn o linellau o god Python. Rhan 1

Mae Dropbox yn ysgrifennu llawer yn Python. Mae'n iaith rydyn ni'n ei defnyddio'n eang iawn, ar gyfer gwasanaethau pen ôl a chymwysiadau cleientiaid bwrdd gwaith. Rydym hefyd yn defnyddio Go, TypeScript a Rust yn aml, ond Python yw ein prif iaith. O ystyried ein graddfa, ac rydym yn sôn am filiynau o linellau o god Python, daeth i'r amlwg bod teipio cod o'r fath yn ddeinamig yn cymhlethu ei ddealltwriaeth yn ddiangen a dechreuodd effeithio'n ddifrifol ar gynhyrchiant llafur. I liniaru'r broblem hon, rydym wedi dechrau trosglwyddo ein cod yn raddol i wirio math statig gan ddefnyddio mypy. Mae'n debyg mai dyma'r system wirio math annibynnol fwyaf poblogaidd ar gyfer Python. Mae Mypy yn brosiect ffynhonnell agored, mae ei brif ddatblygwyr yn gweithio yn Dropbox.

Dropbox oedd un o'r cwmnïau cyntaf i weithredu gwirio math statig mewn cod Python ar y raddfa hon. Mae Mypy yn cael ei ddefnyddio mewn miloedd o brosiectau y dyddiau hyn. Mae'r offeryn hwn amseroedd di-rif, fel y maent yn ei ddweud, "profi mewn brwydr." Rydyn ni wedi dod yn bell i gyrraedd lle rydyn ni nawr. Ar hyd y ffordd, bu llawer o ymgymeriadau aflwyddiannus ac arbrofion aflwyddiannus. Mae'r swydd hon yn ymdrin â hanes gwirio teip statig yn Python, o'i ddechreuadau creigiog fel rhan o fy mhrosiect ymchwil, hyd heddiw, pan mae gwirio teip ac awgrym teipio wedi dod yn gyffredin i ddatblygwyr di-ri sy'n ysgrifennu yn Python. Mae'r mecanweithiau hyn bellach yn cael eu cefnogi gan lawer o offer fel IDEs a dadansoddwyr cod.

Darllenwch yr ail ran

Pam fod angen gwirio math?

Os ydych chi erioed wedi defnyddio Python wedi'i deipio'n ddeinamig, efallai y bydd gennych chi rywfaint o ddryswch ynghylch pam y bu cymaint o ffwdan ynglŷn â theipio statig a mypy yn ddiweddar. Neu efallai eich bod chi'n hoffi Python yn union oherwydd ei deipio deinamig, ac mae'r hyn sy'n digwydd yn syml yn eich cynhyrfu. Yr allwedd i werth teipio statig yw maint yr atebion: po fwyaf yw eich prosiect, y mwyaf y byddwch chi'n pwyso tuag at deipio statig, ac yn y diwedd, y mwyaf y mae ei angen arnoch chi mewn gwirionedd.

Tybiwch fod prosiect penodol wedi cyrraedd maint degau o filoedd o linellau, ac mae'n troi allan bod nifer o raglenwyr yn gweithio arno. Wrth edrych ar brosiect tebyg, yn seiliedig ar ein profiad, gallwn ddweud mai deall ei god fydd yr allwedd i gadw datblygwyr yn gynhyrchiol. Heb anodiadau teip, gall fod yn anodd darganfod, er enghraifft, pa ddadleuon i'w trosglwyddo i ffwythiant, neu pa fathau y gall ffwythiant eu dychwelyd. Dyma gwestiynau nodweddiadol sy'n aml yn anodd eu hateb heb ddefnyddio anodiadau teip:

  • A all y swyddogaeth hon ddychwelyd None?
  • Beth ddylai'r ddadl hon fod? items?
  • Beth yw'r math o briodwedd id: int ydy e, str, neu efallai rhyw fath arferiad?
  • A ddylai'r ddadl hon fod yn rhestr? A yw'n bosibl trosglwyddo tuple iddo?

Os edrychwch ar y pyt cod canlynol gyda nodiadau teipiedig a cheisio ateb cwestiynau tebyg, mae'n ymddangos mai dyma'r dasg symlaf:

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

  • read_metadata ddim yn dychwelyd None, gan nad yw'r math dychwelyd Optional[…].
  • ddadl items yn ddilyniant o linellau. Ni ellir ei ailadrodd ar hap.
  • Priodoledd id yn llinyn o beit.

Mewn byd delfrydol, byddai rhywun yn disgwyl i bob cynnil o'r fath gael ei ddisgrifio yn y ddogfennaeth fewnol (docstring). Ond mae profiad yn rhoi llawer o enghreifftiau o'r ffaith nad yw dogfennaeth o'r fath yn aml yn cael ei arsylwi yn y cod y mae'n rhaid i chi weithio ag ef. Hyd yn oed os yw dogfennaeth o'r fath yn bresennol yn y cod, ni all rhywun ddibynnu ar ei gywirdeb absoliwt. Gall y ddogfennaeth hon fod yn amwys, yn anghywir, ac yn agored i gamddealltwriaeth. Mewn timau mawr neu brosiectau mawr, gall y broblem hon ddod yn hynod o ddifrifol.

Tra bod Python yn rhagori yng nghamau cynnar neu ganolradd prosiectau, ar ryw adeg gall prosiectau a chwmnïau llwyddiannus sy’n defnyddio Python wynebu’r cwestiwn hollbwysig: “A ddylem ni ailysgrifennu popeth mewn iaith sydd wedi’i theipio’n statig?”.

Mae systemau gwirio math fel mypy yn datrys y broblem uchod trwy ddarparu iaith ffurfiol i'r datblygwr ar gyfer disgrifio mathau, a thrwy wirio bod y datganiadau math yn cyd-fynd â gweithrediad y rhaglen (ac, yn ddewisol, gwirio am eu bodolaeth). Yn gyffredinol, gallwn ddweud bod y systemau hyn yn rhoi rhywbeth i ni fel dogfennaeth wedi'i gwirio'n ofalus.

Mae manteision eraill i ddefnyddio systemau o'r fath, ac maent eisoes yn gwbl ddibwys:

  • Gall y system gwirio math ganfod rhai gwallau bach (ac nid mor fach). Enghraifft nodweddiadol yw pan fyddant yn anghofio prosesu gwerth None neu ryw gyflwr arbennig arall.
  • Mae ailffactorio cod wedi'i symleiddio'n fawr oherwydd bod y system gwirio math yn aml yn gywir iawn ynghylch pa god sydd angen ei newid. Ar yr un pryd, nid oes angen i ni obeithio am sylw cod 100% gyda phrofion, nad yw, beth bynnag, fel arfer yn ymarferol. Nid oes angen i ni dreiddio i ddyfnderoedd olion y pentwr i ddarganfod achos y broblem.
  • Hyd yn oed ar brosiectau mawr, yn aml gall mypy wneud gwiriad math llawn mewn ffracsiwn o eiliad. Ac mae cynnal profion fel arfer yn cymryd degau o eiliadau neu hyd yn oed funudau. Mae'r system gwirio math yn rhoi adborth ar unwaith i'r rhaglennydd ac yn caniatáu iddo wneud ei waith yn gyflymach. Nid oes angen iddo bellach ysgrifennu profion uned bregus ac anodd eu cynnal sy'n disodli endidau go iawn â ffug a chlytiau dim ond i gael canlyniadau profion cod yn gyflymach.

Mae IDEs a golygyddion fel PyCharm neu Visual Studio Code yn defnyddio pŵer anodiadau teip i ddarparu i ddatblygwyr gwblhau cod, amlygu gwallau, a chefnogaeth ar gyfer lluniadau iaith a ddefnyddir yn gyffredin. A dyma rai o fanteision teipio. I rai rhaglenwyr, dyma'r brif ddadl o blaid teipio. Mae hyn yn rhywbeth sydd o fudd yn syth ar ôl gweithredu. Nid yw'r achos defnydd hwn ar gyfer mathau yn gofyn am system gwirio math ar wahân fel mypy, er y dylid nodi bod mypy yn helpu i gadw anodiadau math yn gyson â chod.

Cefndir mypy

Dechreuodd hanes mypy yn y DU, yng Nghaergrawnt, ychydig flynyddoedd cyn i mi ymuno â Dropbox. Rwyf wedi bod yn ymwneud, fel rhan o fy ymchwil doethurol, ag uno ieithoedd deinamig wedi'u teipio'n statig. Cefais fy ysbrydoli gan erthygl ar deipio cynyddrannol gan Jeremy Siek a Walid Taha, a chan y prosiect Typed Racket. Ceisiais ddod o hyd i ffyrdd o ddefnyddio'r un iaith raglennu ar gyfer prosiectau amrywiol - o sgriptiau bach i seiliau cod sy'n cynnwys miliynau lawer o linellau. Ar yr un pryd, roeddwn i eisiau sicrhau, mewn prosiect o unrhyw raddfa, na fyddai'n rhaid i un wneud cyfaddawdau rhy fawr. Rhan bwysig o hyn oll oedd y syniad o symud yn raddol o brosiect prototeip heb ei deipio i gynnyrch gorffenedig wedi'i deipio'n statig wedi'i brofi'n gynhwysfawr. Y dyddiau hyn, mae'r syniadau hyn yn cael eu cymryd yn ganiataol i raddau helaeth, ond yn 2010 roedd yn broblem a oedd yn dal i gael ei harchwilio'n weithredol.

Nid oedd fy ngwaith gwreiddiol mewn gwirio teip wedi'i anelu at Python. Yn lle hynny, defnyddiais iaith "cartref" fach Alor. Dyma enghraifft a fydd yn gadael i chi ddeall yr hyn yr ydym yn sôn amdano (mae anodiadau teip yn ddewisol yma):

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

Mae defnyddio iaith frodorol symlach yn ddull cyffredin a ddefnyddir mewn ymchwil wyddonol. Mae hyn yn wir, nid yn lleiaf oherwydd ei fod yn caniatáu ichi gynnal arbrofion yn gyflym, a hefyd oherwydd y ffaith y gellir yn hawdd anwybyddu'r hyn sydd ddim i'w wneud ag ymchwil. Mae ieithoedd rhaglennu’r byd go iawn yn tueddu i fod yn ffenomenau ar raddfa fawr gyda gweithrediadau cymhleth, ac mae hyn yn arafu arbrofi. Fodd bynnag, mae unrhyw ganlyniadau sy'n seiliedig ar iaith symlach yn edrych ychydig yn amheus, oherwydd wrth gael y canlyniadau hyn efallai y bydd yr ymchwilydd wedi aberthu ystyriaethau sy'n bwysig ar gyfer defnydd ymarferol o ieithoedd.

Roedd fy gwiriwr math ar gyfer Alore yn edrych yn addawol iawn, ond roeddwn i eisiau ei brofi trwy arbrofi gyda chod go iawn, nad oedd, efallai y byddwch chi'n dweud, wedi'i ysgrifennu yn Alore. Yn ffodus i mi, roedd iaith Alore yn seiliedig i raddau helaeth ar yr un syniadau â Python. Roedd yn ddigon hawdd newid y typechecker fel y gallai weithio gyda chystrawen a semanteg Python. Roedd hyn yn caniatáu i ni geisio teipio gwirio cod Python ffynhonnell agored. Yn ogystal, ysgrifennais drawsbilydd i drosi cod a ysgrifennwyd yn Alore i god Python a'i ddefnyddio i gyfieithu fy nghod typechecker. Nawr roedd gen i system wirio teip wedi'i ysgrifennu yn Python a oedd yn cefnogi is-set o Python, rhyw fath o'r iaith honno! (Roedd rhai penderfyniadau pensaernïol a oedd yn gwneud synnwyr i Alore yn addas iawn ar gyfer Python, ac mae hyn yn amlwg o hyd mewn rhannau o sylfaen cod mypy.)

Mewn gwirionedd, ni ellid galw'r iaith a gefnogir gan fy system fath yn Python ar hyn o bryd: roedd yn amrywiad ar Python oherwydd rhai cyfyngiadau ar gystrawen anodi math Python 3.

Roedd yn edrych fel cymysgedd o Java a Python:

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

Un o fy syniadau ar y pryd oedd defnyddio anodiadau teip i wella perfformiad drwy lunio’r math yma o Python i C, neu efallai JVM bytecode. Cyrhaeddais y cam o ysgrifennu prototeip casglwr, ond rhoddais y gorau i'r syniad hwn, gan fod gwirio teip ei hun yn edrych yn eithaf defnyddiol.

Yn y diwedd, fe wnes i gyflwyno fy mhrosiect yn PyCon 2013 yn Santa Clara. Siaradais am hyn hefyd gyda Guido van Rossum, yr unben Python caredig am oes. Fe wnaeth fy argyhoeddi i ollwng fy nghystrawen fy hun a glynu gyda chystrawen safonol Python 3. Mae Python 3 yn cefnogi anodiadau swyddogaeth, felly gellid ailysgrifennu fy enghraifft fel y dangosir isod, gan arwain at raglen Python arferol:

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

Roedd yn rhaid i mi wneud rhai cyfaddawdau (yn gyntaf oll, rwyf am nodi i mi ddyfeisio fy nghystrawen fy hun am yr union reswm hwn). Yn benodol, nid oedd Python 3.3, y fersiwn ddiweddaraf o'r iaith ar y pryd, yn cefnogi anodiadau amrywiol. Trafodais gyda Guido trwy e-bost amrywiol bosibiliadau ar gyfer dylunio anodiadau o'r fath yn gystrawen. Penderfynasom ddefnyddio sylwadau yn nodi mathau ar gyfer newidynnau. Roedd hyn yn ateb y diben a fwriadwyd, ond roedd braidd yn feichus (rhoddodd Python 3.6 gystrawen brafiach inni):

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

Daeth sylwadau math hefyd yn ddefnyddiol i gefnogi Python 2, nad oes ganddo gefnogaeth fewnol ar gyfer anodiadau math:

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

Daeth i'r amlwg nad oedd y cyfaddawdau hyn (ac eraill) o bwys mewn gwirionedd - roedd manteision teipio statig yn golygu bod defnyddwyr yn anghofio'n fuan am gystrawen lai na pherffaith. Gan na ddefnyddiwyd unrhyw luniadau cystrawenol arbennig yn y cod Python a wiriwyd o'r math, parhaodd yr offer Python presennol a'r prosesau prosesu cod i weithio'n normal, gan ei gwneud yn llawer haws i ddatblygwyr ddysgu'r offeryn newydd.

Fe wnaeth Guido hefyd fy argyhoeddi i ymuno â Dropbox ar ôl i mi gwblhau fy nhraethawd ymchwil graddedig. Dyma lle mae rhan fwyaf diddorol y stori mypy yn dechrau.

I'w barhau…

Annwyl ddarllenwyr! Os ydych chi'n defnyddio Python, dywedwch wrthym am raddfa'r prosiectau rydych chi'n eu datblygu yn yr iaith hon.

Y llwybr i deipio 4 miliwn o linellau o god Python. Rhan 1
Y llwybr i deipio 4 miliwn o linellau o god Python. Rhan 1

Ffynhonnell: hab.com

Ychwanegu sylw