Роҳи санҷиши чопи 4 миллион хати рамзи Python. Қисми 1

Имрӯз мо қисми аввали тарҷумаи маводро ба диққати шумо мерасонем, ки чӣ гуна Dropbox бо назорати навъи рамзи Python сару кор дорад.

Роҳи санҷиши чопи 4 миллион хати рамзи Python. Қисми 1

Dropbox дар Python бисёр менависад. Ин забонест, ки мо ҳам барои хидматҳои пас-энд ва ҳам барномаҳои муштарии мизи корӣ хеле васеъ истифода мебарем. Мо инчунин Go, TypeScript ва Rust-ро зиёд истифода мебарем, аммо Python забони асосии мост. Бо назардошти миқёси мо ва мо дар бораи миллионҳо сатрҳои коди Python гап мезанем, маълум шуд, ки чопкунии динамикии чунин код фаҳмиши онро беасос мушкил карда, ба ҳосилнокии меҳнат таъсири ҷиддӣ расонидааст. Барои сабук кардани ин мушкилот, мо тадриҷан гузариши коди худро ба санҷиши навъи статикӣ бо истифода аз mypy оғоз кардем. Ин эҳтимолан маъмултарин системаи санҷиши навъи мустақил барои Python аст. Mypy лоиҳаи кушодаасос буда, таҳиягарони асосии он дар Dropbox кор мекунанд.

Dropbox яке аз аввалин ширкатҳое буд, ки дар ин миқёс санҷиши навъи статикиро дар коди Python амалӣ кардааст. Mypy имрӯзҳо дар ҳазорҳо лоиҳа истифода мешавад. Ин асбоб борҳои бешумор, чунон ки мегӯянд, "дар ҷанг озмуда шудааст". Мо рохи дуру дарозеро тай кардаем, то ба он чое, ки хозир дорем. Дар ин рох бисьёр кор-хои нобарор ва тачрибахои но-барор буданд. Ин паём таърихи санҷиши типи статикӣ дар Python, аз оғози сангини он ҳамчун як қисми лоиҳаи тадқиқотии ман, то имрӯз, вақте ки санҷиши намуд ва ишораи намуд барои таҳиягарони бешуморе, ки дар Python менависанд, маъмулӣ шудааст, фаро мегирад. Ин механизмҳо ҳоло аз ҷониби бисёр асбобҳо, аз қабили IDE ва код анализаторҳо дастгирӣ карда мешаванд.

Қисми дуюмро хонед

Чаро санҷиши намуд зарур аст?

Агар шумо ягон бор Python-и ба таври динамикӣ чопшударо истифода карда бошед, шумо шояд каме нофаҳмиҳо дошта бошед, ки чаро вақтҳои охир дар атрофи чопкунии статикӣ ва mypy ин қадар шӯриш ба вуҷуд омадааст. Ё шояд ба шумо Python маҳз аз сабаби чопи динамикии он маъқул аст ва он чизе, ки рӯй медиҳад, шуморо хафа мекунад. Калиди арзиши чопкунии статикӣ миқёси қарорҳост: ҳар қадаре ки лоиҳаи шумо калонтар бошад, ҳамон қадар шумо ба чопкунии статикӣ майл мекунед ва дар ниҳоят ба он бештар ниёз доред.

Фарз мекунем, ки як лоиҳаи муайян ба андозаи даҳҳо ҳазор сатр расидааст ва маълум шуд, ки якчанд барномасозон дар болои он кор мекунанд. Ба як лоиҳаи шабеҳ дар асоси таҷрибаи мо назар карда, мо гуфта метавонем, ки фаҳмидани рамзи он калиди самаранок нигоҳ доштани таҳиягарон хоҳад буд. Бе эзоҳҳои навъи, фаҳмидан душвор буда метавонад, масалан, кадом аргументҳоро ба функсия интиқол додан ё кадом намуди функсия метавонад баргардонида шавад. Дар ин ҷо саволҳои маъмулӣ ҳастанд, ки аксар вақт бе истифодаи эзоҳҳои навъи ҷавоб додан душвор аст:

  • Оё ин функсия бармегардад None?
  • Ин баҳс бояд чӣ гуна бошад? items?
  • Навъи атрибут чист id: int он аст, str, ё шояд ягон намуди фармоишӣ?
  • Оё ин далел бояд рӯйхат бошад? Оё мумкин аст, ки ба он як tuple гузаред?

Агар шумо ба порчаи коди зерини навъи эзоҳ назар кунед ва кӯшиш кунед, ки ба саволҳои шабеҳ ҷавоб диҳед, маълум мешавад, ки ин соддатарин вазифа аст:

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

  • read_metadata барнамегардад None, зеро навъи бозгашт нест Optional[…].
  • Баҳс items пайдарпаии сатрҳо мебошад. Онро ба таври тасодуфӣ такрор кардан мумкин нест.
  • Хусусият id як сатри байт аст.

Дар ҷаҳони идеалӣ интизор шудан мумкин аст, ки ҳамаи ин нозукиҳо дар ҳуҷҷатҳои дарунсохт (docstring) тавсиф карда мешаванд. Аммо таҷриба мисолҳои зиёде медиҳад, ки чунин ҳуҷҷатгузорӣ аксар вақт дар кодексе, ки шумо бояд кор кунед, риоя карда намешавад. Ҳатто агар ин гуна ҳуҷҷатҳо дар кодекс мавҷуд бошанд ҳам, ба дурустии мутлақи он бовар кардан мумкин нест. Ин ҳуҷҷат метавонад норавшан, нодуруст ва барои нофаҳмиҳо кушода бошад. Дар дастаҳои калон ё лоиҳаҳои калон ин мушкилот метавонад хеле шадид гардад.

Гарчанде ки Python дар марҳилаҳои ибтидоӣ ё мобайнии лоиҳаҳо бартарӣ дорад, дар баъзе лаҳзаҳо лоиҳаҳо ва ширкатҳои муваффақе, ки Python-ро истифода мебаранд, метавонанд бо саволи муҳим дучор шаванд: "Оё мо бояд ҳама чизро бо забони статикӣ чопшуда аз нав нависем?".

Системаҳои санҷиши типҳо, ба монанди mypy, мушкилоти дар боло зикршударо тавассути додани забони расмӣ барои тавсифи намудҳо ба таҳиякунанда ҳал мекунанд ва тавассути тафтиш кардани он, ки эъломияҳои навъи ба татбиқи барнома мувофиқат мекунанд (ва ихтиёрӣ, тавассути тафтиши мавҷудияти онҳо). Умуман, мо метавонем бигӯем, ки ин системаҳо чизе ба монанди ҳуҷҷатҳои бодиққат тафтишшударо ба ихтиёри мо медиҳанд.

Истифодаи чунин системаҳо бартариҳои дигар дорад ва онҳо аллакай комилан ғайриоддӣ мебошанд:

  • Системаи санҷиши намуд метавонад хатогиҳои хурдро (ва на он қадар хурд) ошкор кунад. Намунаи маъмулӣ он аст, ки онҳо коркарди арзишро фаромӯш мекунанд None ё ягон шарти дигари махсус.
  • Рефакторинги код хеле содда карда шудааст, зеро системаи санҷиши намуд одатан дар бораи кадом код бояд тағир дода шавад, хеле дақиқ аст. Дар айни замон, ба мо лозим нест, ки 100% фарогирии код бо санҷишҳо, ки дар ҳар сурат, одатан ғайриимкон аст, умедвор бошем. Барои фаҳмидани сабаби мушкилот ба мо лозим нест, ки ба умқи пайи стек омӯзем.
  • Ҳатто дар лоиҳаҳои калон, mypy аксар вақт метавонад дар як сония санҷиши пурраи навъиро анҷом диҳад. Ва иҷрои санҷишҳо одатан даҳҳо сония ва ҳатто дақиқаҳоро мегирад. Системаи санҷиши намуд ба барномасоз зуд ҷавоб медиҳад ва ба ӯ имкон медиҳад, ки кори худро зудтар анҷом диҳад. Ба ӯ дигар лозим нест, ки санҷишҳои нозук ва душвор барои нигоҳ доштани воҳидҳо нависад, ки объектҳои воқеиро бо масхара ва часпакҳо иваз мекунанд, танҳо барои зудтар ба даст овардани натиҷаҳои санҷиши код.

IDE ва муҳаррирон ба монанди PyCharm ё Visual Studio Code қудрати эзоҳҳои навъиро истифода мебаранд, то ба таҳиягарон бо анҷоми код, равшансозии хатогӣ ва дастгирии конструксияҳои маъмули забон истифода шаванд. Ва инҳо танҳо баъзе аз бартариҳои чопкунӣ мебошанд. Барои баъзе барномасозон, ҳамаи ин далели асосӣ ба манфиати чоп кардан аст. Ин чизест, ки фавран пас аз татбиқ фоида меорад. Ин ҳолати истифода барои намудҳо системаи алоҳидаи тафтиши навъи монанди mypy талаб намекунад, гарчанде ки бояд қайд кард, ки mypy кӯмак мекунад, ки эзоҳҳои навъи мувофиқ бо код нигоҳ дошта шаванд.

Заминаи mypy

Таърихи mypy дар Британияи Кабир, дар Кембриҷ, чанд сол пеш аз он ки ман ба Dropbox ҳамроҳ шудам, оғоз ёфт. Ман ҳамчун як қисми тадқиқоти доктории худ дар муттаҳидсозии забонҳои статикӣ ва динамикӣ иштирок доштам. Ман аз мақолае дар бораи чопкунии афзояндаи Ҷереми Сиек ва Валид Таҳа ва лоиҳаи Typed Racket илҳом гирифтам. Ман кӯшиш кардам, ки роҳҳои истифодаи як забони барномасозиро барои лоиҳаҳои гуногун пайдо кунам - аз скриптҳои хурд то пойгоҳҳои рамзӣ, ки аз миллионҳо сатр иборат аст. Дар баробари ин, ман мехостам кафолат диҳам, ки дар лоиҳаи ҳама гуна миқёс ба созишҳои аз ҳад калон лозим наояд. Қисми муҳими ҳамаи ин идеяи оҳиста-оҳиста гузаштан аз лоиҳаи прототипи номатлуб ба маҳсулоти тайёри ҳамаҷониба санҷидашудаи статикӣ буд. Дар ин рӯзҳо, ин ғояҳо асосан ҳамчун як чизи муқаррарӣ қабул карда мешаванд, аммо дар соли 2010 он мушкилоте буд, ки ҳоло ҳам фаъолона омӯхта мешуд.

Кори аслии ман дар санҷиши намуд ба Python нигаронида нашудааст. Ба ҷои ин, ман забони хурди "худсохти" -ро истифода кардам Alore. Ин аст мисоле, ки ба шумо имкон медиҳад фаҳмед, ки мо дар бораи чӣ гап мезанем (дар ин ҷо эзоҳҳои навъи ихтиёрӣ мебошанд):

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

Истифодаи забони модарии соддакардашуда як равиши маъмулест, ки дар таҳқиқоти илмӣ истифода мешавад. Ин на камтар аз он аст, ки он ба шумо имкон медиҳад, ки таҷрибаҳоро зуд гузаронед ва инчунин аз он чизе, ки ба омӯзиш ҳеҷ иртиботе надорад, ба осонӣ нодида гирифтан мумкин аст. Забонҳои барномасозии воқеии ҷаҳонӣ одатан падидаҳои калонҳаҷм бо татбиқи мураккаб мебошанд ва ин таҷрибаро суст мекунад. Бо вуҷуди ин, ҳама гуна натиҷаҳое, ки дар асоси забони соддашуда асос ёфтаанд, каме шубҳанок ба назар мерасанд, зеро ҳангоми ба даст овардани ин натиҷаҳо муҳаққиқ метавонад мулоҳизаҳои муҳимро барои истифодаи амалии забонҳо қурбонӣ кунад.

Санҷиши навъи ман барои Alore хеле умедбахш ба назар мерасид, аммо ман мехостам онро бо таҷриба бо коди воқеӣ санҷам, ки шумо гуфта метавонед, ки дар Alore навишта нашудааст. Хушбахтона, барои ман, забони Alore асосан ба ҳамон ғояҳои Python асос ёфтааст. Аз нав сохтани чопкунак ба қадри кофӣ осон буд, то он бо синтаксис ва семантикаи Python кор кунад. Ин ба мо имкон дод, ки санҷиши навъи коди кушодаи Python-ро санҷем. Илова бар ин, ман барои табдил додани коди дар Alore ба рамзи Python навишташуда як транспилер навиштам ва онро барои тарҷумаи рамзи чопкунаки худ истифода кардам. Ҳоло ман системаи санҷиши намуд доштам, ки бо Python навишта шуда буд, ки як зермаҷмӯи Python, як навъ ин забонро дастгирӣ мекард! (Баъзе қарорҳои меъморӣ, ки барои Alore маъно доштанд, барои Python чандон мувофиқ набуданд ва ин ҳоло ҳам дар қисматҳои пойгоҳи коди mypy мушоҳида мешавад.)

Дар асл, забонеро, ки системаи навъи ман дастгирӣ мекунад, дар ин лаҳза комилан Python номидан мумкин нест: он як варианти Python аз сабаби баъзе маҳдудиятҳои синтаксиси аннотацияи навъи Python 3 буд.

Он ба омехтаи Java ва Python монанд буд:

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

Яке аз ғояҳои ман дар он вақт ин буд, ки эзоҳҳои типро барои беҳтар кардани кор тавассути тартиб додани ин намуди Python ба C ё шояд JVM байт код истифода баранд. Ман ба марҳилаи навиштани прототипи компилятор расидам, аммо ман аз ин идея даст кашидам, зеро санҷиши навъи худ хеле муфид менамуд.

Ман лоиҳаи худро дар PyCon 2013 дар Санта Клара муаррифӣ кардам. Ман инчунин дар ин бора бо Гвидо ван Россум, диктатори хайрхоҳи Python барои як умр сӯҳбат кардам. Вай маро бовар кунонд, ки синтаксиси шахсии худро тарк кунам ва бо синтаксиси стандартии Python 3 пайваст шавам. Python 3 тавзеҳоти функсияҳоро дастгирӣ мекунад, аз ин рӯ мисоли ман метавонист аз нав навишта шавад, тавре ки дар зер нишон дода шудааст, дар натиҷа як барномаи муқаррарии Python:

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

Ман маҷбур будам, ки баъзе созишҳо кунам (пеш аз ҳама, ман мехоҳам қайд кунам, ки ман синтаксиси худро барои ҳамин ихтироъ кардам). Аз ҷумла, Python 3.3, ки версияи охирини забон дар он вақт буд, шарҳҳои тағирёбандаро дастгирӣ намекард. Ман бо Гуидо тавассути почтаи электронӣ имкониятҳои гуногуни тарҳрезии синтаксиси чунин эзоҳҳоро муҳокима кардам. Мо тасмим гирифтем, ки шарҳҳои навъиро барои тағирёбандаҳо истифода барем. Ин ба мақсади пешбинишуда хизмат мекард, аммо то андозае душвор буд (Python 3.6 ба мо синтаксиси хубтар дод):

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

Шарҳҳои навъи низ барои дастгирии Python 2, ки дастгирии дарунсохти эзоҳҳои навъиро надорад, муфиданд:

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

Маълум шуд, ки ин (ва дигар) муомилаҳо аслан муҳим набуданд - бартариҳои чопкунии статикӣ маънои онро дошт, ки корбарон ба зудӣ дар бораи синтаксиси камтар аз мукаммал фаромӯш карданд. Азбаски дар коди навъи Python ягон сохтори махсуси синтаксиси истифода нашуда буд, абзорҳои мавҷудаи Python ва равандҳои коркарди код ба таври мӯътадил кор мекарданд ва барои таҳиягарон омӯхтани асбоби навро хеле осон карданд.

Гуидо инчунин маро бовар кунонд, ки пас аз хатми рисолаи баъдидипломӣ ба Dropbox ҳамроҳ шавам. Аз ин чо чолиби диккати достони mypy сар мешавад.

Давом дорад…

Хонандагони азиз! Агар шумо Python-ро истифода баред, лутфан ба мо дар бораи миқёси лоиҳаҳое, ки шумо бо ин забон таҳия мекунед, бигӯед.

Роҳи санҷиши чопи 4 миллион хати рамзи Python. Қисми 1
Роҳи санҷиши чопи 4 миллион хати рамзи Python. Қисми 1

Манбаъ: will.com

Илова Эзоҳ