Mozilla的Python3使用情况
Mozilla使用了很多Python。我們的大多數(shù)構(gòu)建系統(tǒng)、CI配置、測試工具、命令行工具和無數(shù)其他腳本、工具或Github項(xiàng)目都是由Python處理的。在mozilla-central中有3500多個(gè)Python文件(不包括第三方文件),大約包含230萬行代碼。此外,在Github上的Mozilla org中有462個(gè)帶有Python標(biāo)簽的存儲庫(盡管其中許多并不活躍)。這些存儲庫中有很多Python項(xiàng)目,其中大部分是Python2。
隨著Python 2停止支持的日期的臨近,這是一個(gè)很好的時(shí)間來評估當(dāng)前的情況并提出一些問題。在Python3的遷移中,Mozilla已經(jīng)走了多遠(yuǎn)?哪些大型工作項(xiàng)位于關(guān)鍵路徑上?我們是否有一個(gè)計(jì)劃能在2020年1月1日Python 2停止支持之前達(dá)到一個(gè)好的狀態(tài)?
種樹(進(jìn)行遷移)的第二個(gè)最佳的時(shí)間
但是在處理這些問題之前,我想先解決另一個(gè)經(jīng)常出現(xiàn)的問題: 隨著Python2停止支持,我們是否需要實(shí)現(xiàn)100%的遷移?
從技術(shù)上講,不需要。我們?nèi)匀豢梢园惭bPython 2,并且仍然可以從PyPi上安裝包。但繼續(xù)堅(jiān)守Python2將是一個(gè)巨大的錯(cuò)誤,原因如下:
那么,2020年1月1日是一個(gè)硬性的最后期限嗎?不。但這不會阻止我們以最快的速度進(jìn)行遷移。不是因?yàn)檫@個(gè)最后期限,而是因?yàn)檫@將給Mozilla帶來最好的成功機(jī)會。
認(rèn)真考慮遷移到Python3的最佳時(shí)機(jī)是五年前。第二次最佳的時(shí)間是現(xiàn)在。
當(dāng)前狀況
現(xiàn)在,既然我們已經(jīng)確定了遷移到python3是重要且有價(jià)值的,讓我們進(jìn)入細(xì)節(jié)。本文的其余部分只關(guān)注mozilla-central。不是因?yàn)槲覀兺獠縭epos中的Python不重要,而是因?yàn)槲矣匈Y格討論mozilla-central。以下是我們目前取得的一些進(jìn)展:
- 我們增加了在CI中使用Python3運(yùn)行python測試的能力。這為我們提供了一個(gè)保障,一旦模塊的單元測試在Python3下通過,我們就可以相對自信地認(rèn)為,該模塊將來在Python3中依然會運(yùn)行良好(假設(shè)有足夠的測試覆蓋率)。
- 我們設(shè)置了一些linter。一個(gè)linter可以確保Python文件至少可以在Python3中導(dǎo)入而不會失敗,另一個(gè)linter可以確保Python2文件使用合適的的__future__語句,使將來遷移該文件稍微容易一些。盡管這些linter還沒有在所有應(yīng)該啟用它的文件上啟用。
- 最后,我們開始移植mozbase。它是在我們的構(gòu)建、測試和CI基礎(chǔ)設(shè)施中到處使用的一組包。對這些模塊進(jìn)行完全遷移是進(jìn)行幾乎其他所有事情的先決條件。
雖然到目前為止所取得的進(jìn)展并不明顯,但這只是完成全部工作的一小部分。那么接下來會發(fā)生什么呢?
下一個(gè)主要障礙
最初的重點(diǎn)是添加使用Python 3運(yùn)行測試的能力,這已經(jīng)完成了(盡管我們對用于此目的的機(jī)制并不完全滿意,稍后將詳細(xì)介紹)。但是,即使我們正在運(yùn)行測試和linter來捕獲潛在的與Python3相關(guān)的問題,我們實(shí)際上并沒有在任何地方默認(rèn)使用Python3。因此,下一個(gè)主要障礙是,用Python 3運(yùn)行一個(gè)簡單的mach命令(比如mach google)。從表面上看,這聽起來很容易實(shí)現(xiàn),畢竟所有的mach google命令只有四行代碼。但實(shí)際上,這是一個(gè)非常大的項(xiàng)目,我將把本文余下的大部分時(shí)間用于這個(gè)項(xiàng)目。
使用Python3運(yùn)行mach命令意味著不僅命令本身需要與Python3兼容,而且所有依賴項(xiàng)也需要兼容。幾乎每個(gè)命令(包括mach google)都依賴于兩個(gè)主要庫:python/mach和python/mozbuild。讓這些模塊(或者至少是大多數(shù)mach命令使用的那些模塊)與Python3一起工作是這里的第一個(gè)主要困難。但是,盡管為Python3準(zhǔn)備mach和mozbuild是一項(xiàng)繁重的任務(wù),但同時(shí)又是一項(xiàng)簡單的任務(wù)。道路是明確的,我們只需要有人卷起袖子把工作做好。我估計(jì)這將不會超過一個(gè)星期的工作價(jià)值(這只是完成mach google所需的部分的時(shí)間)。
另一個(gè)阻礙是bootstrapping(引導(dǎo))。我們驗(yàn)證了開發(fā)人員在運(yùn)行mach bootstrap時(shí)安裝了受支持的Python版本。我們需要就一個(gè)最低可行的版本達(dá)成一致(3.5似乎是可能的),然后修改我們的引導(dǎo)腳本,以確保開發(fā)人員同時(shí)擁有Python 2和Python 3的兼容版本。但這也不是一項(xiàng)非常困難的任務(wù)。
這個(gè)里程碑的第三個(gè)也是最后一個(gè)部分是實(shí)際實(shí)現(xiàn)mach中的管道。要增強(qiáng)對命令進(jìn)行內(nèi)省的能力,并確定是否需要使用Python2或Python3運(yùn)行命令。這就是復(fù)雜性所在。
讓我們深入研究一下,把需要解決的問題分解一下。這里的基本假設(shè)是,這個(gè)tree(樹)太大了,不能一次全部轉(zhuǎn)換為Python3。代碼太多了,bitrot的潛力太大了,在不經(jīng)意的情況下破壞其他東西的風(fēng)險(xiǎn)太大了。我們必須一次一個(gè)來慢慢地轉(zhuǎn)換命令。
調(diào)用問題
記住這一點(diǎn),我們遇到的第一個(gè)問題是調(diào)用問題。在mach中,命令是通過裝飾器注冊在實(shí)際的Python類上的。如果你以前看過mach_commands.py文件,你可能已經(jīng)注意到了@CommandProvider、@Command和@CommandArgument裝飾器。這為工具作者注冊他們的命令和使用的參數(shù)提供了一種非常方便的方法。但它也有一個(gè)很大的缺點(diǎn): 每次調(diào)用mach時(shí)都會導(dǎo)入每一個(gè)mach_commands.py。這是mach獲得必需的命令元數(shù)據(jù)來確定它要執(zhí)行什么的唯一方法。
簡而言之,我們使用Python解析所有可用的命令,然后發(fā)送用戶指定的命令。但是現(xiàn)在我們要發(fā)送的命令可能需要一個(gè)不同于當(dāng)前運(yùn)行的Python。
選項(xiàng)1
如果我們不改變注冊的工作方式,這就意味著兩件事:
盡管我們需要實(shí)現(xiàn)調(diào)用第二個(gè)Python進(jìn)程的實(shí)際機(jī)制,但這是最簡單的解決方案。
選項(xiàng)2
或者,我們可以更改命令注冊的工作方式。代替(或者除此之外)使用裝飾器,我們可以將發(fā)送命令所必須的命令元數(shù)據(jù)(例如,名稱和模塊路徑)注冊在一些主要文件中。也許我們可以使用一個(gè)頂層的mach_commands.json文件,它看起來是這樣的:
mach二進(jìn)制文件的內(nèi)部有一些巧妙的修改,它既是有效的Python,也是有效的shell。當(dāng)你執(zhí)行./mach時(shí),它首先會作為shell腳本運(yùn)行,找到合適的Python可執(zhí)行文件,然后將自身作為一個(gè)Python腳本來重新執(zhí)行。有了這個(gè)提議,mach 驅(qū)動程序的shell部分可替換為:
這比選項(xiàng)1復(fù)雜得多,但它避免了這兩個(gè)警告。也就是說,我們不需要擔(dān)心所有的Python 3都是可導(dǎo)入的,也不需要運(yùn)行兩個(gè)單獨(dú)的Python進(jìn)程。
通常情況下,我認(rèn)為這種方法的復(fù)雜性遠(yuǎn)不及這兩個(gè)微小的好處。但這個(gè)選項(xiàng)更有吸引力,因?yàn)檫@是我們一直在討論要做的事情。這個(gè)選項(xiàng)還有第三個(gè)更大的好處,盡管它與Python 3遷移完全無關(guān)。我們不需要在每次mach調(diào)用時(shí)都加載每一個(gè)mach_commands.py??梢栽诓粚?dǎo)入所有文件的情況下獲得發(fā)送和運(yùn)行mach help所需的所有信息。這將大大地加快mach調(diào)用。
最終結(jié)果是,這兩種選項(xiàng)都是可行的。如果我們想把精力集中在Python3遷移上,我會選擇選項(xiàng)1。但是選項(xiàng)2仍然很有吸引力,因?yàn)樗赡軙o我們一個(gè)同時(shí)解決兩個(gè)實(shí)質(zhì)性問題的理由。在寫這篇文章的時(shí)候,我不確定我應(yīng)該選擇哪一個(gè)選項(xiàng)。
依賴項(xiàng)問題
當(dāng)你運(yùn)行并執(zhí)行mach命令時(shí),系統(tǒng)會創(chuàng)建一個(gè)virtualenv,其中包含一組分散在mozilla-central上的“基礎(chǔ)" 包。我們稱之為“initial(初始)”virtualenv。一些帶有更復(fù)雜需求的命令確實(shí)會在這個(gè)基礎(chǔ)層上創(chuàng)建它們自己的virtualenv,除此之外,這個(gè)“init”virtualenv在默認(rèn)情況下會被激活。當(dāng)然,我們在這個(gè)virtualenv中安裝的包集合是不同的,這取決于我們使用Python2還是Python3來運(yùn)行命令。我們不能在Python3 virtualenv中安裝只適用于Python2的包(反之亦然)。
這里的解決方案非常簡單。我們可以維護(hù)兩個(gè)單獨(dú)的清單來關(guān)聯(lián)這兩個(gè)必需的virtualenv。一個(gè)用于Python2,一個(gè)用于Python3。有些模塊(甚至大多數(shù)模塊)可能會同時(shí)出現(xiàn)在這兩種清單中。但這里還有其他需要考慮的事情。在mozilla-central中,我們需要解決一個(gè)類似但無關(guān)緊要的問題: 依賴項(xiàng)鎖定。
依賴項(xiàng)鎖定會確保一個(gè)工具的所有使用者使用的版本與其他人完全相同。這使得事情可以保持重現(xiàn)性和明確性,通過驗(yàn)證哈希值可以阻止mitm(中間人攻擊)攻擊,并且這樣做被廣泛認(rèn)為是任何包生態(tài)系統(tǒng)中的最佳實(shí)踐。依賴項(xiàng)鎖定值得考慮的原因是,處理依賴項(xiàng)鎖定的工具也傾向于處理virtualenv管理。事實(shí)上,我們使用了一個(gè)這樣的工具(Pipenv)來處理當(dāng)前的依賴項(xiàng)鎖定需求。由于無論如何我們都在使用這些類型的工具,因此花一些時(shí)間研究它們是否能夠幫助我們處理Python 3依賴關(guān)系是值得的,那我們就來看一看。
Pipenv
近幾年來,Pipenv一直是Python社區(qū)的寵兒,我們在很多地方都使用它:
當(dāng)我和Dave Hunt在實(shí)現(xiàn)這些事情的時(shí)候,這是城里唯一的一款游戲。我們把一切都完成好了,但道路比我們希望的要曲折一些。我們最終實(shí)現(xiàn)了一些“足夠好”的東西,但是沒有達(dá)到我們想要的水平。我個(gè)人之所以不在Pipenv上出售這些東西以便它們可以在像我們這樣的大型monorepo上使用,主要有幾個(gè)原因:
我不得不說,我們上一次研究這些系統(tǒng)已經(jīng)有一年了,我們使用的Pipenv版本也同樣老舊。從那時(shí)起,情況有可能有所改善。盡管如此,我不建議使用Pipenv來幫助我們。然而…
Poetry
Poetry的創(chuàng)建是為了彌補(bǔ)前面提到的Pipenv的一些缺點(diǎn)。我個(gè)人在我自己的幾個(gè)項(xiàng)目中使用它,并且我認(rèn)為它是一個(gè)非常棒的工具。它看起來更敏捷、更輕量級,至少維護(hù)者對提議的新特性是開放討論的,而且我在使用它時(shí)從未遇到過bug或向后不兼容的更改(盡管版本還沒有達(dá)到1.0,所以向后不兼容的更改還是會遇到的)。
Poetry包含了我在Pipenv中所希望的一切功能,但是它仍然有一個(gè)很大的缺點(diǎn): 它也假設(shè)你使用的是單個(gè)Python包。它甚至比Pipenv更進(jìn)一步,迫使你提供諸如包名稱和版本之類的元數(shù)據(jù)。這使得它無法作為大型monorepo的工具后端。那么,我又何必費(fèi)心提及它呢?
Jetty
Jetty是我一直在構(gòu)建的一個(gè)小實(shí)驗(yàn)品。它是一個(gè)圍繞著Poetry本身的非常微小的包裝器,并試圖使它在像mozilla-central這樣的monorepo中使用時(shí)更有用。它做了一些事情:
它似乎運(yùn)行得相當(dāng)好。我的下一步計(jì)劃是試驗(yàn)用Jetty替換我們的代碼樹中的 Pipenv用法。如果一切順利,這可能是處理我們的Python 3依賴關(guān)系的一種可行方法。
所有這些關(guān)于Pipenv/Poetry/Jetty的討論,都與當(dāng)前的問題無關(guān)。我們可以在沒有它們的情況下解決我們需要的所有問題,這可能是目前最明智的做法。我只是想提到它們,因?yàn)樗鼈兇_實(shí)試圖解決我們面臨的許多相同的問題。它們至少是值得考慮的。
結(jié)論及具體步驟
總而言之,下一個(gè)主要障礙是開始用Python 3運(yùn)行特定的mach命令(除了用Python 2運(yùn)行其他命令之外)。這里有一些具體的步驟可以幫助我們解決這個(gè)障礙:
用Python 3運(yùn)行python/mach和python/mozbuild單元測試。
盡可能多地啟用py3 linter(最好是所有內(nèi)容)。
臨時(shí)破解mach二進(jìn)制文件,使其指向Python3,并嘗試運(yùn)行一個(gè)非?;镜拿?例如 mach google)。
將Python3添加到我們的引導(dǎo)過程中。
與此同時(shí),還有一些更大的問題需要解決。即調(diào)用和依賴項(xiàng)問題。在這兩種情況下,都有一種快速而又隨性的解決方案,以及一種更長但可能更好的解決方案。這兩種情況都需要一定程度的規(guī)劃和協(xié)調(diào)。
最后,我想回答我在開始時(shí)問的一個(gè)問題。我們是否計(jì)劃在2020年1月1日Python 2的EOL之前達(dá)到一個(gè)好的狀態(tài)?我的回答是不。這篇文章可能是一個(gè)非常粗略的計(jì)劃大綱,但它只討論了下一步的主要步驟。在這一步之后,我們?nèi)匀挥修D(zhuǎn)換所有東西的實(shí)際工作要做。另外,本文甚至沒有涉及Github中的Python?;卮稹安弧钡牧硪粋€(gè)原因是,盡管一些工程師和團(tuán)隊(duì)確實(shí)認(rèn)識到這項(xiàng)工作的重要性,但這并不是高層管理人員關(guān)注的事情。我們只是沒有必要的資源分配來修復(fù)它,我不知道它有沒有在其他人的正式計(jì)劃表上。
話雖如此,我樂觀地認(rèn)為,如果我們按優(yōu)先順序來做,這些工作是可以及時(shí)完成的。如果我們不這樣做,我仍然樂觀地認(rèn)為最終也會完成。只是可能趕不上2020年1月1日。
總結(jié)
以上是生活随笔為你收集整理的Mozilla的Python3使用情况的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 白话 Python 的函数式编程
- 下一篇: Python中随机森林的实现与解释