對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分

我們向您展示有關 Dropbox 在為 Python 程式碼實作類型檢查系統時所採取的路徑的材料翻譯的第三部分。

對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分

→ 上一部分: 第一 и 第二

達到 4 萬行鍵入程式碼

另一個主要挑戰(也是內部調查中第二個最常見的問題)是增加 Dropbox 中類型檢查所涵蓋的程式碼量。 我們嘗試了多種方法來解決這個問題,從自然增加類型程式碼庫的大小到將 mypy 團隊的工作重點放在靜態和動態自動類型推斷上。 最終,似乎沒有簡單的獲勝策略,但我們透過結合多種方法實現了註釋程式碼量的快速成長。

因此,我們最大的 Python 儲存庫(包含後端程式碼)擁有近 4 萬行帶有註解的程式碼。 靜態程式碼類型的工作大約在三年內完成。 Mypy 現在支援各種類型的程式碼覆蓋率報告,可以更輕鬆地監控輸入進度。 特別是,我們可以產生有關類型不明確的程式碼的報告,例如明確使用類型 Any 無法驗證的註釋,或匯入沒有類型註釋的第三方庫之類的內容。 作為提高 Dropbox 類型檢查準確性的專案的一部分,我們致力於改進集中式 Python 儲存庫中一些流行開源庫的類型定義(所謂的存根文件) 排版.

我們實現了(並在後續 PEP 中標準化)類型系統的新功能,允許針對某些特定的 Python 模式提供更精確的類型。 一個值得注意的例子是 TypeDict,它為類似 JSON 的字典提供類型,這些字典具有一組固定的字串鍵,每個鍵都有自己類型的值。 我們將繼續擴展類型系統。 我們的下一步可能是改進對 Python 數值功能的支援。

對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分
註解程式碼行數:server

對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分
註解程式碼行數:client

對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分
附註解的程式碼總行數

以下概述了我們為增加 Dropbox 中已註解的程式碼數量所做的事情的主要功能:

註釋嚴謹。 我們逐漸提高了對新程式碼註釋嚴謹性的要求。 我們從 linter 提示開始,建議在已經有一些註解的文件中加入註解。 現在,我們需要在新的 Python 檔案和大多數現有檔案中使用類型註解。

打字報告。 我們每周向團隊發送有關代碼輸入級別的報告,並就應首先註釋的內容提供建議。

mypy 的普及。 我們在活動中談論 mypy 並與團隊交談以幫助他們開始使用類型註釋。

民意調查。 我們定期進行用戶調查以發現主要問題。 我們已經準備好在解決這些問題方面走得更遠(甚至創建一種新語言來加速 mypy!)。

表現。 我們透過使用守護進程和 mypyc 大大提高了 mypy 的效能。 這樣做是為了消除註釋過程中出現的不便,並且能夠處理大量程式碼。

與編輯器整合。 我們建立了工具來支援在 Dropbox 上流行的編輯器中執行 mypy。 這包括 PyCharm、Vim 和 VS Code。 這極大地簡化了註解程式碼和檢查其功能的過程。 在註釋現有程式碼時,這些類型的操作很常見。

靜態分析。 我們創建了一個使用靜態分析工具推斷函數簽名的工具。 這個工具只能在相對簡單的情況下工作,但它幫助我們不費吹灰之力地增加了程式碼類型覆蓋率。

支援第三方函式庫。 我們的許多專案都使用 SQLAlchemy 工具包。 它利用了 PEP 484 類型無法直接建模的 Python 動態功能。 我們按照PEP 561創建了對應的存根檔案並為mypy編寫了一個插件(開源),這改進了 SQLAlchemy 支援。

我們遇到的困難

對我們來說,通往 4 萬行鍵入程式碼的道路並不總是那麼容易。 在這條路上,我們遇到了很多坎坷,也犯過一些錯誤。 這些是我們遇到的一些問題。 我們希望講述這些問題能幫助其他人避免類似的問題。

遺失文件。 我們從僅檢查少量文件開始我們的工作。 未檢查這些文件中未包含的任何內容。 當第一個註釋出現在文件中時,文件就會被加入掃描清單。 如果某些內容是從位於驗證範圍之外的模組導入的,那麼我們正在討論使用諸如 Any,根本沒有經過測試。 這導致打字準確性顯著下降,尤其是在遷移的早期階段。 到目前為止,這種方法的效果出乎意料地好,儘管典型的情況是將文件添加到審查範圍會揭示程式碼庫其他部分的問題。 在最壞的情況下,當合併兩個獨立的程式碼區域時,其中彼此獨立地檢查了類型,結果發現這些區域的類型彼此不相容。 這導致需要對註釋進行許多更改。 現在回想起來,我們意識到我們應該早點將核心庫模組新增到 mypy 的類型檢查區域。 這將使我們的工作更加可預測。

註解舊代碼。 當我們開始時,我們有大約 4 萬行現有的 Python 程式碼。 很明顯,註解所有這些程式碼並不是一件容易的事。 我們創建了一個名為 PyAnnotate 的工具,它可以在測試運行時收集類型信息,並可以根據收集的信息向程式碼添加類型註釋。 然而,我們還沒有註意到該工具得到特別廣泛的採用。 收集類型資訊的速度很慢,自動產生的註釋通常需要許多手動編輯。 我們考慮過在每次檢查代碼時自動運行此工具,或者根據對少量實際網絡請求的分析來收集類型信息,但最終決定不這樣做,因為這兩種方法都風險太大。

因此,可以注意到大部分的程式碼都是由其所有者手動註釋。 為了引導這個過程朝著正確的方向發展,我們準備了關於需要註釋的特別重要的模組和功能的報告。 例如,為在數百個地方使用的庫模組提供類型註釋非常重要。 但是,被新服務取代的舊服務註釋不再那麼重要。 我們也嘗試使用靜態分析來為遺留程式碼產生類型註解。

循環導入。 上面,我談到了循環導入(「依賴纏結」),它的存在使得 mypy 的加速變得困難。 我們還必須努力讓 mypy 支援由這些循環導入引起的各種習慣用法。 我們最近完成了一個主要的系統重新設計項目,解決了 mypy 的大部分有關循環導入的問題。 這些問題其實源自於計畫的早期階段,來自 Alore,mypy 計畫最初關注的教育語言。 Alore 語法可以輕鬆解決循環導入指令的問題。 現代 mypy 繼承了早期簡單實作的一些限制(這非常適合 Alore)。 Python 讓循環導入變得困難,主要是因為表達式不明確。 例如,賦值操作實際上可以定義類型別名。 在處理大部分導入循環之前,Mypy 並不總是能夠檢測到這樣的事情。 阿洛爾沒有這樣的含糊之處。 系統開發早期階段做出的錯誤決策可能會在多年後給程式設計師帶來不愉快的意外。

結果:通往 5 萬行程式碼和新視野的道路

mypy 專案已經走過了漫長的道路——從早期的原型到控制 4 萬行生產程式碼類型的系統。 隨著 mypy 的發展,Python 的類型提示被標準化。 如今,圍繞輸入 Python 程式碼已經形成了一個強大的生態系統。 它有一個庫支援的地方,它包含 IDE 和編輯器的輔助工具,它有幾個類型控制系統,每個系統都有自己的優點和缺點。

儘管 Dropbox 已經提供了類型檢查,但我相信我們仍處於輸入 Python 程式碼的早期階段。 我認為類型檢查技術將繼續發展和改進。

如果您尚未在大型 Python 專案中使用類型檢查,現在是開始轉向靜態類型的好時機。 我曾與那些經歷過類似轉變的人交談過。 他們都沒有後悔。 類型檢查使 Python 成為一種比「常規 Python」更適合開發大型專案的語言。

親愛的讀者! 你在 Python 專案中使用類型檢查嗎?

對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分
對 4 萬行 Python 代碼進行類型檢查的路徑。 第3部分

來源: www.habr.com

添加評論