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