Python打包庫
譯者:徐宏富
Sun-2014-05-25 blog.ionelmc.ro
我認為應該重新審視打包最佳做法,現如今,有許多的好的工具沒有被使用過,或被充分使用。重新評估最佳的做法,通常來說一直都是件好事情。
例如,現在你的python代碼包要在多個python版本中測試,這些版本有着不同的依賴庫和設置等。
在打包時,我喜歡遵循一些基本的原則:
.如果你可以使用py.test或者nose這樣的工具幫助測試,就不要浪費時間去建一個專有的運行測 試器。因為這些龐大的插件生態系統能夠改進你的測試。
.如有可能,盡早阻止問題發生。這主要是要設計嚴格而詳細的測試,去阻止一些常見的錯誤。
.收集覆蓋數據,記錄下來,甄別原因。
.測試所有可能的配置。
結構
這是相當重要的,每一件事都圍繞着它。我比較喜歡如下類型的布局:
這種src目錄是一種很好的途徑,是因為:
.你得到了導入入口。這個當前目錄隱含着系統路徑(sys.path)。假如你用site-pakages來進行安裝或導入,就不是這樣了。用戶就不會和你一樣,有着相同的當前工作目錄。
.這樣的約束,使得測試和打包均受益:
你將不得不檢測安裝代碼(例如:安裝某種虛擬環境),這能確保代碼開發工作--打包代碼是正確的,否則你的測試會失敗。早早的,在你公開發布這樣的有問題的軟件包前,就能發現問題。
.你將不得不安裝這些軟件包。如果你曾經在pypi發布過軟件包,由於沒測試過安裝配置,當遇到這些軟件包缺少模板或者使用了破損的依賴庫時,就不會上傳成功。只有成功的建立源分發(sdist)模式,才能確保安裝成功。
.這能預防你從setup.py腳本里導入代碼。從主包或者模型里導入這些不可用的依賴庫時,是糟糕的令人氣炸的體驗。首先就得避免這種可能發生。
.更簡單的打包代碼和表現形式。這種表現形式簡單易寫。例如你打包一個有許多模塊和文件的Django APP時。同樣的,你在面對有多個包的大型庫時也不必擔心。這樣寫,被打包的代碼和打包用的代碼一目了然。
沒有src而去寫manifest.in 文檔是很困難的。如果你的manifest.in文檔是破損的,你的測試也會失敗。有src目錄就會容易很多:你只須把src移植放入manifest.in文檔里。
把破損的軟件發布到pypi上不好處理。
沒有src,在你編輯安裝命令時就會很混亂(是用”setup.py develop” 還是用 “pip install -e”?)。沒有代碼分離src目錄,就會迫使setuptools工具把你的項目根目錄安裝在系統路徑(sys.path)上。這樣就會充滿大量的沒用的東西,造成setup.py文檔,還有其他的測試或配置腳本不知不覺的不能導入。
這里有個更好的工具--tox,你不需要處理安裝包,只需要進行測試。你不必擔心,它可以自動自如的幫你安裝好軟件包。
更少的用戶錯誤出現。甚至幾乎沒有。
各種工具也不會混淆代碼和非代碼。
有一種說法是:扁平比嵌套好。但對數據來說並非如此。文件系統畢竟也是數據,緊湊、標准化的數據結構是可取的。
您會注意到我沒有在已安裝的軟件包中包含測試。這是因為:
模塊發現工具將會和你的測試模塊沖突。奇怪的事情通常出現在測試模塊中。內置help幫助就是模塊發現。例如:
測試通常要額外的對象運行,它們自身沒什么用,你不能直接空空的運行它們。
測試與開發有關,而不是用途。
庫的用戶不可能代替庫的開發者去測試庫,例如:當你測試app時,你沒有去測試Django---因為Django已經測試過了。
備選方案
你可以這樣用src--更少的布局,很少的例子。
這兩種布局變得流行是因為,幾年前,打包時有很多問題,只為測試而安裝打包是不可行的。人們還在推薦它們,僅僅是舊的和過時的假設。
很多項目不當的使用它們,是因為除了Twisted trial外,很多的測試運行器,都有不正確的當前工作目錄默認值。如果你沒有檢測已經安裝好的代碼,就會錯誤的去檢測的別的代碼。Trial通過暫時改變當前工作目錄去做正確的事情,而很多項目沒有使用trial。
Setup 腳本
很不幸,當前的打包工具有很多的陷阱。Setup.py腳本應該盡量簡單。
上面的腳本的特點:
.沒有exec 或者 import 權略
.所有東西來自src--包或者根級別模塊
.明確的編碼
運行測試
再一次,人們傾向於用python setup.py test 去運行軟件包測試。我認為並不值得這么去做,setup.py test 是個失敗的試驗,它嘗試着去復制CPAN的測試系統。Python沒有個通用的測試結果協議,所以它也沒有個通用的測試命令,去達成目的。就目前來說,我們需要有人去建立規格和服務,以便值得我們這樣去做,並支持他們。認識到失敗在哪里,並在必要的時候能重新回到圖板,我認為這是很重要的。毫無疑問,沒相應的服務和工具,是在用setup.py test命令,提供這種增值服務的。有些事情錯的很明顯。
我認為,pypi現在去做這件事情已經為時過晚,因為現在有一個牢靠的、自由的、非常有彈性的候選者---Travis。它已經和Github緊緊的整合在一起,能自動獲得Pull-Rquest支持。
Tox是一款很好的當地測試工具,它能運行所有可能的配置(每一種配置就一個tox環境)。我喜歡把這些測試用這些環境條件組成一個矩陣:
.檢測 檢測包裹的元數據。比如,重組文本在你的詳細描述中是否有效。
.清理 清理覆蓋數據
.報告 用所有累積的數據生成報告
.文檔 生成sphnix文檔
無論有沒有測量覆蓋率,我一直都喜歡用環境去測試。如果你總是用覆蓋率去測量,你就不大可能捕獲到,各種競爭條件下敏感的表現變化。
測試矩陣
根據依賴關系,你最終會得到各種python版本、依賴庫版本、不同的設置的巨大組合。通常的,人們在tox.ini或者僅僅在.travis.yml中對所有的東西硬編碼。在沒有完成本地測試,或者在travis中進行連續測試完成前,這些測試就結束了。我那樣做過,不喜歡這樣。我嘗試過在tox.ini和.travis.yml中復制所有環境,還不是喜歡它。
由於沒有現成可用的選擇去生成配置,我用模塊生成器腳本去生成tox.ini和.travis.yml。這種方法更好,很干凈,你很容易跳過特定的環境測試(例如,跳過Django 1.4 在Python 3中的測試),只需要做少量的改動工作。
要點(全部代碼)
Ci/bootstrap.py
這是生成器腳本。無論什么時候,你想生成配置就運行它。
Ci/templates/.travis.yml
有好東西在里面--非常有用的libSegFault.so.trick.
它基本上只運行tox.
如果你已經足夠耐心通讀了全文,你就會注意到:
Travis使用的tox的每一項都在在矩陣里,這使得測試Travis連續性同時,也進行了當地測試。
Tox的環境命令是:clean,check,2.6-1.3,2.6-1.4,...,report.
覆蓋測量+環境配置的運行代碼是沒有安裝的(usedevelop=true),最后覆蓋能同各種量結合在一起。
沒有覆蓋+環境配置的代碼將進行源分發(sdist),裝進虛擬環境中(tox的默認行為),以便及早的發現有關打包的問題。
各種環境中運行的結果最終生成單一報告。
擁有一份在tox.ini中各種環境的完整測試報告優勢明顯:
你是在本地有干擾的條下並行測試的(除非你的測試需要非常嚴格的隔離),你還可以用drone.io代替Travis並行運行一切。
你可以在本地為所有東西測量累積覆蓋(把所有的環境下的覆蓋合並到一起)。
測量覆蓋
這種機制-- 一種跟蹤時間和多個構建的覆蓋范圍的好方法,能夠自動的把覆蓋變化的信息添加到Github Pull Request上。
簡單總結
把代碼放進src
用tox或detox
有無覆蓋兩種情況均要測試
為tox.ini和.travis.ini使用生成器腳本
在Travis中用tox運行測試,確保和本地測試條件一致
太復雜?請使用python package template.
還不夠清楚?請閱讀:Hyneky’s post about the src layout.
也可以查閱:Short list of packaging pitfalls.