在Python的setuptools框架下生成egg的教程
本文介紹了 setuptools 框架的內(nèi)容,它是 PEAK 的一個副項目,它提供了比 distutils 更加簡單的包管理和發(fā)行功能。
開始
setuptools 模塊很會 “規(guī)避”。例如,如果我們下載一個使用 setuptools 而不是使用 distutils 構(gòu)建的包,那么安裝就應(yīng)該可以像我們期望的一樣工作:通常使用 python setup.py install 就可以。為了實現(xiàn)這種功能,使用 setuptools 綁定在一起的包就會在歸檔文件中包含一個很小的引導模塊 ez_setup.py。此處惟一需要注意的是 ez_setup.py 試圖在后臺下載并安裝所需要的 setuptools —— 當然,這需要有一個連接網(wǎng)絡(luò)的機器。如果 setuptools 早已在本地機器上安裝了,那么這個后臺步驟就不再需要執(zhí)行;但是如果它需要手工進行安裝,那么很多透明性就都丟失了。不過,大部分系統(tǒng)現(xiàn)在都有一個 Internet 連接了;為沒有連接網(wǎng)絡(luò)的機器多執(zhí)行幾個特殊步驟也并非特別麻煩。
setuptools 的真正優(yōu)點并不在于實現(xiàn) distutils 所能實現(xiàn)的功能 —— 盡管它 的確 增強了 distutils 的功能并簡化了 setup.py 腳本中的內(nèi)容。setuptools 最大的優(yōu)勢是它在包管理能力方面的增強。它可以使用一種更加透明的方法來查找、下載并安裝依賴包;并可以在一個包的多個版本中自由進行切換,這些版本都安裝在同一個系統(tǒng)上;也可以聲明對某個包的特定版本的需求;還可以只使用一個簡單的命令就能更新到某個包的最新版本。給人印象最為深刻的是,即使有些包的開發(fā)人員可能還從未考慮過任何 setuptools 兼容性問題,我們依然可以使用這些包。
下面讓我們詳細探討一下。
引導
工具 ez_setup.py 是一個簡單的腳本,它可以引導 setuptools 中其余部分。有點讓人困惑的是,完整 setuptools 包中所提供的 easy_install 腳本與 ez_setup.py 所實現(xiàn)的功能是相同的。不過前者假設(shè) setuptools 早已安裝了,因此它會跳過幕后的安裝過程。這兩個版本都可以接受相同的參數(shù)和開關(guān)。
這個過程中的第一個步驟是下載一個小腳本 ez_setup.py:
清單 1. 下載引導腳本
% wget -q http://peak.telecommunity.com/dist/ez_setup.py
然后,就可以不帶任何參數(shù)運行腳本來安裝 setuptools 中其余部分了(如果不作為一個單獨的步驟來執(zhí)行這個步驟,在首次安裝其他包時,它還是會被完成)。會看到類似于下面的內(nèi)容(當然,這要取決于所使用的版本):
清單 2. 引導 setuptools
% python ez_setup.py Downloading http://cheeseshop.python.org/packages/2.4/s/ setuptools/setuptools-0.6b1-py2.4.egg#md5=b79a8a403e4502fbb85ee3f1941735cb Processing setuptools-0.6b1-py2.4.egg creating /sw/lib/python2.4/site-packages/setuptools-0.6b1-py2.4.egg Extracting setuptools-0.6b1-py2.4.egg to /sw/lib/python2.4/site-packages Removing setuptools 0.6a11 from easy-install.pth file Adding setuptools 0.6b1 to easy-install.pth file Installing easy_install script to /sw/bin Installing easy_install-2.4 script to /sw/bin Installed /sw/lib/python2.4/site-packages/setuptools-0.6b1-py2.4.egg Processing dependencies for setuptools
完畢。這就是我們需要確保在系統(tǒng)上安裝 setuptools 而需要做的工作。
安裝包
對于很多 Python 包來說,要安裝這些包,需要做的就是將這些包的名字作為一個參數(shù)傳遞給 ez_setup.py 或 easy_install。既然目前已經(jīng)使用引導腳本加載了 setuptools,那就可以使用內(nèi)部更加簡化的 easy_install(實際上它與我們選擇的版本的區(qū)別很?。┝?。
例如,假設(shè)希望安裝 SQLObject 包。過程非常簡單,如清單 3 所示。注意消息中說 SQLObject 依賴于一個名為 FormEncode 的包;所幸的是,這會被很好地解決:
清單 3. 安裝一個典型的包
% easy_install SQLObject Searching for SQLObject Reading http://www.python.org/pypi/SQLObject/ Reading http://sqlobject.org Best match: SQLObject 0.7.0 Downloading http://cheeseshop.python.org/packages/2.4/S/ SQLObject/SQLObject-0.7.0-py2.4.egg#md5=71830b26083afc6ea7c53b99478e1b6a Processing SQLObject-0.7.0-py2.4.egg creating /sw/lib/python2.4/site-packages/SQLObject-0.7.0-py2.4.egg Extracting SQLObject-0.7.0-py2.4.egg to /sw/lib/python2.4/site-packages Adding SQLObject 0.7.0 to easy-install.pth file Installing sqlobject-admin script to /sw/bin Installed /sw/lib/python2.4/site-packages/SQLObject-0.7.0-py2.4.egg Processing dependencies for SQLObject Searching for FormEncode>=0.2.2 Reading http://www.python.org/pypi/FormEncode/ Reading http://formencode.org Best match: FormEncode 0.5.1 Downloading http://cheeseshop.python.org/packages/2.4/F/ FormEncode/FormEncode-0.5.1-py2.4.egg#md5=f8a19cbe95d0ed1b9d1759b033b7760d Processing FormEncode-0.5.1-py2.4.egg creating /sw/lib/python2.4/site-packages/FormEncode-0.5.1-py2.4.egg Extracting FormEncode-0.5.1-py2.4.egg to /sw/lib/python2.4/site-packages Adding FormEncode 0.5.1 to easy-install.pth file Installed /sw/lib/python2.4/site-packages/FormEncode-0.5.1-py2.4.egg
正如可以從這些消息中看到的一樣,easy_install 要在 www.python.org/pypi/ 上查找有關(guān)這個包的信息,然后查找真正可以下載它的地方(此處 egg 包就在 cheeseshop.python.org 上;后面將介紹有關(guān) egg 的更多內(nèi)容)。
現(xiàn)在不僅僅可以安裝某個包的最新版本(這是默認操作)。如果愿意,還可以為 easy_install 提供一個特定的版本需求?,F(xiàn)在讓我們嘗試安裝 SQLObject 的一個 post-beta 版本。
清單 4. 安裝某個包的最小版本
% easy_install 'SQLObject>=1.0' Searching for SQLObject>=1.0 Reading http://www.python.org/pypi/SQLObject/ Reading http://sqlobject.org No local packages or download links found for SQLObject>=1.0 error: Could not find suitable distribution for Requirement.parse('SQLObject>=1.0')
如果(在本文編寫時情況就是如此)SQLObject 的最新版本小于 1.0,那么這會什么也不安裝。
安裝 “naive” 包
SQLObject 是可以識別 setuptools 的;但是如果要安裝一個尚未兼容 setuptools 的包又該如何呢?例如,在本文之前,我從沒有對自己的 “Gnosis Utilities” 使用過 setuptools。不過,現(xiàn)在讓我們來嘗試安裝一下這個包,已知的只有它所在的 HTTP(或 FTP、SVN、CVS)位置(setuptools 可以理解所有這些協(xié)議)。我的下載 Web 站點上有各個 Gnosis Utilities 的版本,它們的命名采用了常見的版本風格:
清單 5. 安裝不識別 setuptools 的包
% easy_install -f http://gnosis.cx/download/Gnosis_Utils.More/ Gnosis_Utils Searching for Gnosis-Utils Reading http://gnosis.cx/download/Gnosis_Utils.More/ Best match: Gnosis-Utils 1.2.1 Downloading http://gnosis.cx/download/Gnosis_Utils.More/ Gnosis_Utils-1.2.1.zip Processing Gnosis_Utils-1.2.1.zip Running Gnosis_Utils-1.2.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-CCrXEs/Gnosis_Utils-1.2.1/egg-dist-tmp-Sh4DW1 zip_safe flag not set; analyzing archive contents... gnosis.__init__: module references __file__ gnosis.magic.__init__: module references __file__ gnosis.xml.objectify.doc.__init__: module references __file__ gnosis.xml.pickle.doc.__init__: module references __file__ gnosis.xml.pickle.test.test_zdump: module references __file__ Adding Gnosis-Utils 1.2.1 to easy-install.pth file Installed /sw/lib/python2.4/site-packages/Gnosis_Utils-1.2.1-py2.4.egg Processing dependencies for Gnosis-Utils
所幸的是 easy_install 可以把這一切都完成得很好。它會查看給定的下載目錄,識別出可用的最高版本,展開這個包,然后將其重新打包為 “egg” 格式,后者就可以用來進行安裝了。導入 gnosis 現(xiàn)在可以在一個腳本中運行。但是假設(shè)現(xiàn)在需要對 Gnosis Utilities 之前的某個特定版本來測試一個腳本又該怎么做呢?這也非常簡單:
清單 6. 安裝一個 “naive” 包的特定版本
% easy_install -f http://gnosis.cx/download/Gnosis_Utils.More/ "Gnosis_Utils==1.2.0" Searching for Gnosis-Utils==1.2.0 Reading http://gnosis.cx/download/Gnosis_Utils.More/ Best match: Gnosis-Utils 1.2.0 Downloading http://gnosis.cx/download/Gnosis_Utils.More/ Gnosis_Utils-1.2.0.zip [...] Removing Gnosis-Utils 1.2.1 from easy-install.pth file Adding Gnosis-Utils 1.2.0 to easy-install.pth file Installed /sw/lib/python2.4/site-packages/Gnosis_Utils-1.2.0-py2.4.egg Processing dependencies for Gnosis-Utils==1.2.0
現(xiàn)在通常已經(jīng)安裝了兩個版本的 Gnosis Utilities,當前活動版本是 1.2.0。將活動版本切換回 1.2.1 也非常簡單:
清單 7. 在系統(tǒng)范圍修改 “活動” 版本
% easy_install "Gnosis_Utils==1.2.1" Searching for Gnosis-Utils==1.2.1 Best match: Gnosis-Utils 1.2.1 Processing Gnosis_Utils-1.2.1-py2.4.egg Removing Gnosis-Utils 1.2.0 from easy-install.pth file Adding Gnosis-Utils 1.2.1 to easy-install.pth file Using /sw/lib/python2.4/site-packages/Gnosis_Utils-1.2.1-py2.4.egg Processing dependencies for Gnosis-Utils==1.2.1
當然,這一次只能使一個版本是活動的。不過通過在各個腳本上面放上這樣兩行類似內(nèi)容,就可以讓腳本選擇自己希望使用的版本:
清單 8. 在腳本中使用某個版本的包
from pkg_resources import require require("Gnosis_Utils==1.2.0")
通過使用上述要求,setuptools 就可以在運行 import 語句時添加一個特定的版本(如果指定了大于比較,就是最新的可用版本)。
讓包可以識別 setuptools
我會更希望讓用戶不需要知道 Gnosis Utilities 的下載目錄就可以安裝它。這 通常都可以 工作,因為 Gnosis Utilities 在 Python Cheeseshop 上有一個信息清單。不幸的是,因為沒有考慮 setuptools ,所以我在 python.org 上為我的 Gnosis Utilities 建立了一個 “不匹配” 的入口 http://www.python.org/pypi/Gnosis%20Utilities/1.2.1。具體地說,這個歸檔文件是根據(jù)類似于 Gnosis_Utils-N.N.N.tar.gz 的模式進行命名的(這些工具也打包成了 .zip 和 .tar.bz2 文件,最新的幾個版本還打包成了 win32.exe 的安裝程序,所有這些文件 setuptools 都可以很好地處理)。不過 Cheeseshop 上的項目名的拼寫與 “Gnosis Utilities” 稍微有點不同。實際上,在 Cheeseshop 的一個很小的管理版本的更改就會將 http://www.python.org/pypi/Gnosis_Utils/1.2.1-a 創(chuàng)建為一個發(fā)布后版本。發(fā)行版歸檔文件本身并沒有什么變化,不過是在 Cheeseshop 里增加了一點元數(shù)據(jù)。只需要少量努力,就可以使用更加簡單的安裝程序(注意,出于測試目的,我運行了一個 easy_install -m 來刪除所安裝的包)。
清單 9. 簡單增加對 setuptools 的識別
% easy_install Gnosis_Utils Searching for Gnosis-Utils Reading http://www.python.org/pypi/Gnosis_Utils/ Reading http://www.gnosis.cx/download/Gnosis_Utils.ANNOUNCE Reading http://gnosis.cx/download/Gnosis_Utils.More/ Best match: Gnosis-Utils 1.2.1 Downloading [...]
我把這個過程剩余的部分忽略掉了,因為這與您前面看到的內(nèi)容沒什么兩樣。惟一的區(qū)別在于 easy_install 要在 Cheeseshop(換言之 www.python.org/pypi/)上尋找可以匹配指定名字的元數(shù)據(jù),并使用這些信息來查找真正的下載位置。在這種情況中,所列出的 .ANNOUNCE 文件沒有包含任何有幫助的內(nèi)容,不過 easy_install 還會繼續(xù)查看另一個所列的 URL,這會證明它是一個下載目錄。
關(guān)于 egg
egg 是一個包含所有包數(shù)據(jù)的文件包。在理想情況中,egg 是一個使用 zip 壓縮的文件,其中包括了所有需要的包文件。但是在某些情況下,setuptools 會決定(或被開關(guān)告知)包不應(yīng)該是 zip 壓縮的。在這些情況下,egg 只是一個簡單的未曾壓縮的子目錄,但是里面的內(nèi)容是相同的。使用單一的版本可以方便地進行轉(zhuǎn)換,并可以節(jié)省一點磁盤空間,但是 egg 目錄從功能和組織結(jié)構(gòu)上來說都是相同的。一直使用 JAR 文件的 Java? 技術(shù)的用戶會發(fā)現(xiàn) egg 非常熟悉。
由于最新的 Python 版本中(需要 2.3.5+ 或 2.4)導入掛鉤的更改,可以簡單地通過設(shè)置 PYTHONPATH 或 sys.path 并像往常一樣導入相應(yīng)的包來使用 egg。如果希望采用這種方法,就不需要使用 setuptools 或 ez_setup.py 了。例如,在本文使用的工作目錄中,我就為 PyYAML 包放入了一個 egg?,F(xiàn)在我就可以使用這個包了,方法如下:
清單 10. PYTHONPATH 上的 egg
% export PYTHONPATH=~/work/dW/PyYAML-3.01-py2.4.egg % python -c 'import yaml; print yaml.dump({"foo":"bar",1:[2,3]})' 1: [2, 3] foo: bar
不過,PYTHONPATH 的(或者腳本或 Python shell 會話內(nèi)的 sys.path的)這種操作有些脆弱。egg 的發(fā)現(xiàn)最好是在新一點的 .pth 文件中進行。在 site-packages/ 或 PYTHONPATH 中的任何 .pth 文件都會進行解析來執(zhí)行其他導入操作,其方法類似于檢查可能包含包的那些目錄位置一樣。如果使用 setuptools 來處理包的管理功能,那么在安裝、更新、刪除包時,就需要修改一個名為 easy-install.pth 的文件。而且可以按照自己喜歡的方式對這個 .pth 進行命名(只要其擴展名是 .pth 即可)。例如,下面是我的 easy-install.pth 文件的內(nèi)容:
清單 11. 用作 egg 位置配置的 .pth 文件
% cat /sw/lib/python2.4/site-packages/easy-install.pth import sys; sys.__plen = len(sys.path) setuptools-0.6b1-py2.4.egg SQLObject-0.7.0-py2.4.egg FormEncode-0.5.1-py2.4.egg Gnosis_Utils-1.2.1-py2.4.egg import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)
這種格式有點特殊:它近似于一個 Python 腳本,但卻不完全是。需要說明的是,可以在那里添加額外列出的 egg;更好的情況是,easy_install 會在運行時實現(xiàn)這種功能。也可以在 site-packages/ 下創(chuàng)建任意多個 .pth 文件;每個都可以列出有哪些 egg 是可用的。
增強安裝腳本
上面所述的這種安裝 setuptools naive 包的能力(請參閱 清單 6)只部分有效。也就是說,包 Gnosis_Utils 的確安裝上了,但是并不完整。所有常見的功能都可以工作,但是在自動生成 egg 時卻忽略了很多支持文件 —— 大部分是擴展名為 .txt 的文檔和擴展名為 .xml 的測試文件(還有一些其他的 README、.rnc、.rng、.xsl 和圍繞子包的文件)。在安裝時,所有這些支持文件都 “最好要有”,而沒有嚴格要求一定要有。不過,我們?nèi)匀幌M軌虬械闹С治募?/p>
Gnosis_Utils 使用的 setup.py 腳本實際上非常復(fù)雜。除了列出基本的元數(shù)據(jù)之外,在第 467 行代碼中,它還對 Python 版本的功能和 bug 進行完整測試;解決舊版本的 distutils 中的一些故障;回溯跳過對不支持部分的安裝(例如,如果 pyexpat 在 Python 發(fā)行版中并沒有包括);處理 OS 行結(jié)束符的轉(zhuǎn)換;創(chuàng)建多個歸檔/安裝程序類型;根據(jù)測試結(jié)果重新構(gòu)建 MANIFEST 文件。能夠?qū)崿F(xiàn)處理這些工作的能力要感謝此包的另外一個維護人員 Frank McIngvale;這些能力可以讓 Gnosis_Utils 能成功安裝回 Python 1.5.1 的版本,當然前提是需要這么做(早期版本中的功能沒有這么豐富)。不過此處我要向大家展示的腳本并沒有像 distutils 腳本一樣做這么復(fù)雜的事情:它只是簡單地假設(shè)系統(tǒng)中已經(jīng)安裝了一個 “普通的” 最新版本的 Python。即使這么講,setuptools 能讓安裝腳本變得如此簡單還是非常吸引人。
在第一次嘗試時,讓我們來創(chuàng)建一個 setup.py 腳本,它是從 setuptools 手冊中借用的,并試圖使用它來創(chuàng)建一個 egg:
清單 12. setuptools setup.py 腳本
% cat setup.py from setuptools import setup, find_packages setup( name = "Gnosis_Utils", version = "1.2.2", packages = find_packages(), ) % python setup.py -q bdist_egg zip_safe flag not set; analyzing archive contents... gnosis.__init__: module references __file__ gnosis.doc.__init__: module references __file__ gnosis.magic.__init__: module references __file__ gnosis.xml.objectify.doc.__init__: module references __file__ gnosis.xml.pickle.doc.__init__: module references __file__ gnosis.xml.pickle.test.test_zdump: module references __file__
這點努力就已經(jīng)可以起作用;至少可以部分地起作用。使用這幾行內(nèi)容的確可以創(chuàng)建一個 egg,不過這個 egg 與使用 easy_install 創(chuàng)建的 egg 有一些相似的缺點:缺乏對不使用 .py 命名的文件的支持。因此讓我們再試一次,只是需要稍微再努力一點:
清單 13. 添加缺少的 package_data
from setuptools import setup, find_packages setup( name = "Gnosis_Utils", version = "1.2.2", package_data = {'':['*.*']}, packages = find_packages(), )
這就是需要做的所有操作。當然,根據(jù)實際情況,通常希望對它進行一些調(diào)整。例如,它可能會列出下面的內(nèi)容:
清單 14. 打包特定類型文件類型
package_data = {'doc':['*.txt'], 'xml':['*.xml', 'relax/*.rnc']}
這段內(nèi)容翻譯一下就是:將 .txt 文件包括在 doc/ 子包中,將 .xml 文件包括在 xml/ 子包中,將所有 .rnc 文件包括在 xml/relax/ 子包中。
結(jié)束語
本文實際上只介紹了用支持 setuptools 的發(fā)行版可以執(zhí)行的定制操作的表層的知識。例如,假設(shè)您現(xiàn)在有一個發(fā)行版(可以是首選的 egg 格式或另外一種歸檔類型),您就可以使用一個命令將這個歸檔文件和元數(shù)據(jù)上載到 Cheeseshop 上。顯然,完整的 setup.py 腳本應(yīng)該包含舊版本 distutils 腳本中所包含的同樣詳細的元數(shù)據(jù);為了簡單起見,本文跳過了這些內(nèi)容,但是其參數(shù)名與 distutils 是兼容的。
盡管要完全適應(yīng) setuptools 所提供的巨大功能需要一些時間,但是實際上它確實可以讓維護您自己的包和安裝外來包都要比 distutils 更加簡單。如果您所關(guān)心的內(nèi)容僅僅是安裝包,那么您所需要了解的內(nèi)容在本文的介紹中已經(jīng)全部包括了;只是您在描述您自己的包時可能會發(fā)現(xiàn)一些復(fù)雜性,不過仍然沒有使用 distutils 那么復(fù)雜。
相關(guān)文章
pytorch使用resnet快速加載官方提供的預(yù)訓練模型
這篇文章主要介紹了pytorch使用resnet快速加載官方提供的預(yù)訓練模型方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09Python 讀取千萬級數(shù)據(jù)自動寫入 MySQL 數(shù)據(jù)庫
這篇文章主要介紹了Python 讀取千萬級數(shù)據(jù)自動寫入 MySQL 數(shù)據(jù)庫,本篇文章會給大家系統(tǒng)的分享千萬級數(shù)據(jù)如何寫入到 mysql,分為兩個場景,兩種方式2022-06-06Python tornado隊列示例-一個并發(fā)web爬蟲代碼分享
這篇文章主要介紹了Python tornado隊列示例-一個并發(fā)web爬蟲代碼分享,具有一定借鑒價值,需要的朋友可以參考下2018-01-01進行數(shù)據(jù)處理的6個?Python?代碼塊分享
這篇文章主要介紹了進行數(shù)據(jù)處理6個Python代碼塊的分享,分享內(nèi)容有選取有空值的行、快速替換列值、對列進行分區(qū)、將一列分為多列等內(nèi)容,需要的朋友可以參考一下2022-04-04Python爬取數(shù)據(jù)并實現(xiàn)可視化代碼解析
這篇文章主要介紹了Python爬取數(shù)據(jù)并實現(xiàn)可視化代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08Django?logging日志模塊實例詳解(日志記錄模板配置)
Django使用python自帶的logging作為日志打印工具,下面這篇文章主要給大家介紹了Django?logging日志模塊(日志記錄模板配置)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2022-08-08pycharm創(chuàng)建一個python包方法圖解
在本篇文章中小編給大家分享了關(guān)于pycharm怎么創(chuàng)建一個python包的相關(guān)知識點,需要的朋友們學習下。2019-04-04