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 нь хэдхэн секундын дотор бүрэн төрлийн шалгалтыг хийж чаддаг. Туршилтын гүйцэтгэл нь ихэвчлэн хэдэн арван секунд, бүр минут болдог. Төрөл шалгах систем нь програмистад шуурхай хариу өгч, ажлаа хурдан гүйцэтгэх боломжийг олгодог. Кодын тестийн үр дүнг илүү хурдан авахын тулд бодит объектуудыг элэглэл, нөхөөсөөр сольдог хэврэг, засвар үйлчилгээ хийхэд хэцүү нэгж тестүүдийг бичих шаардлагагүй болсон.

PyCharm эсвэл Visual Studio Code зэрэг IDE болон редакторууд нь хөгжүүлэгчдэд код бөглөх, алдааг тодруулах, түгээмэл хэрэглэгддэг хэлний бүтцийг дэмжих зорилгоор төрлийн тэмдэглэгээний хүчийг ашигладаг. Мөн эдгээр нь бичихийн зарим ашиг тус юм. Зарим програмистуудын хувьд энэ бүхэн нь бичихийг дэмжсэн гол аргумент юм. Энэ нь хэрэгжүүлсний дараа шууд ашиг тусаа өгөх зүйл юм. Төрөлүүдэд зориулсан энэхүү хэрэглэгдэхүүн нь 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 код руу хөрвүүлэхийн тулд transpiler бичиж, түүгээрээ typechecker кодыг орчуулсан. Одоо би Python хэл дээр бичигдсэн төрөл шалгах системтэй байсан бөгөөд энэ нь Python-ийн дэд багцыг дэмждэг байсан. (Alore-д утга учиртай байсан зарим архитектурын шийдвэрүүд Python-д тохиромжгүй байсан бөгөөд энэ нь mypy кодын зарим хэсэгт мэдэгдэхүйц хэвээр байна.)

Үнэн хэрэгтээ, миний төрлийн системээр дэмжигдсэн хэлийг одоогоор Python гэж нэрлэх боломжгүй байсан: Python 3 төрлийн тэмдэглэгээний синтаксийн зарим хязгаарлалтын улмаас энэ нь Python-ийн хувилбар байсан юм.

Энэ нь 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-р хэсэг

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх