當前位置: 首頁 ? 深入 Python 3 ?

難度級別: ?????

打包 Python 類庫

? You’ll find the shame is like the pain; you only feel it once. ?
— Marquise de Merteuil, Dangerous Liaisons

 

深入

讀到這里,你可能是想要發布一個 Python 腳本,庫,框架,或者應用程序。太棒了!世界需要更多的Python代碼。

Python 3 自帶一個名為 Distutils 的打包框架。Distutils 包含許多功能:構建工具(為你所準備),安裝工具(為用戶所準備),數據包格式(為搜索引擎所準備)等。它集成了 Python 安裝包索引(“PyPI”),一個開源 Python 類庫的中央資料庫。

這些 Distutils 的不同功能以setup script為中心,一般被命名為 setup.py。事實上,你已經在本書中見過一些 Distutils 安裝腳本。在 《HTTP Web Services》 一章中,我們使用 Distutils 來安裝 httplib2 ,而在《案例研究:將 chardet 移植到 Python 3》一章中,我們用它安裝 chardet

在本章中,你將學習 chardethttplib2 的安裝腳本如何工作,并將逐步(學會)發布自己的 Python 軟件。

# chardet's setup.py
from distutils.core import setup
setup(
    name = "chardet",
    packages = ["chardet"],
    version = "1.0.2",
    description = "Universal encoding detector",
    author = "Mark Pilgrim",
    author_email = "mark@diveintomark.org",
    url = "http://chardet.feedparser.org/",
    download_url = "http://chardet.feedparser.org/download/python3-chardet-1.0.1.tgz",
    keywords = ["encoding", "i18n", "xml"],
    classifiers = [
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Development Status :: 4 - Beta",
        "Environment :: Other Environment",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
        "Operating System :: OS Independent",
        "Topic :: Software Development :: Libraries :: Python Modules",
        "Topic :: Text Processing :: Linguistic",
        ],
    long_description = """\
Universal character encoding detector
-------------------------------------

Detects
 - ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants)
 - Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese)
 - EUC-JP, SHIFT_JIS, ISO-2022-JP (Japanese)
 - EUC-KR, ISO-2022-KR (Korean)
 - KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic)
 - ISO-8859-2, windows-1250 (Hungarian)
 - ISO-8859-5, windows-1251 (Bulgarian)
 - windows-1252 (English)
 - ISO-8859-7, windows-1253 (Greek)
 - ISO-8859-8, windows-1255 (Visual and Logical Hebrew)
 - TIS-620 (Thai)

This version requires Python 3 or later; a Python 2 version is available separately.
"""
)

?chardethttplib2 都是開源的,但這并沒有要求你在特定的許可下發布你自己的 Python 庫。本章所描述的過程對任何 Python軟件都適用,無論它使用什么許可證

?

Distutils 無法為你完成的工作

發布第一個 Python 包是一項艱巨的過程。(發布第二個相對容易一些。)Distutils 試圖盡可能多的自動完成一些工作,但是仍然有一些事情你必須自己做。

?

目錄結構

要開始打包 Python 軟件,必須先將文件和目錄安排好。 httplib2 的目錄樹如下:

httplib2/                 
|
+--README.txt             
|
+--setup.py               
|
+--httplib2/              
   |
   +--__init__.py
   |
   +--iri2uri.py
  1. 創建根目錄來保存所有的目錄和文件。將其以 Python 模塊的名字命名。
  2. 為了適應 Windows 用戶,"自述"文件應包含 .txt 擴展名,而且它應該使用 Windows 風格回車符。不能僅僅因為使用了一個優秀的文本編輯器,它從命令行運行并包括它自己的宏語言,而需要讓你的用戶為難。(你的用戶使用記事本。雖然可悲,但卻是事實。)即使你工作在 Linux 或 Mac OS X 環境下,優秀的文本編輯器毫無疑問地會有一個選項,允許將文件以 Windows 風格回車符來保存。
  3. Distutils 安裝腳本應命名為 setup.py,除非你有一個很好的理由不這樣做。但你并沒有一個很好的理由不這樣做。
  4. 如果你的Python軟件只包含一個單一的 .py 文件,你應該把它和"自述"文件以及安裝腳本放到根目錄下。但 httplib2 并不是單一的 .py 文件,它是一個多文件模塊 。但是沒關系!只需在根目錄下放置 httplib2 目錄,這樣在 httplib2/ 根目錄下就會有一個包含 __init__.py 文件的 httplib2/ 目錄。這并不是一個難題,事實上,它可以簡化打包過程。

chardet 目錄看起來有些不同。像 httplib2 一樣,它是一個多文件模塊 ,所以在 chardet/ 根目錄下有一個 chardet/ 目錄。除了 README.txt 文件,在 docs/ 目錄下, chardet 還有 HTML ——格式化文檔。該 docs/ 目錄包含多個 .html.css 文件和 images/ 子目錄,其中包含幾個 .png.gif 文件。(稍后你會發現,這將是很重要的。)此外,對于 (L)GPL 許可的軟件,它包含一個單獨的 COPYING.txt 文件,其中包含 LGPL 許可證的完整內容。


chardet/
|
+--COPYING.txt
|
+--setup.py
|
+--README.txt
|
+--docs/
|  |
|  +--index.html
|  |
|  +--usage.html
|  |
|  +--images/ ...
|
+--chardet/
   |
   +--__init__.py
   |
   +--big5freq.py
   |
   +--...

?

編寫安裝腳本

Distutils 安裝腳本是一份 Python 腳本。從理論上講,它可以做任何 Python 可以做的事情。在實踐中,安裝腳本應該做盡可能少的事情并盡可能按標準的方式做。安裝腳本應該簡單。安裝過程越奇異,錯誤報告也會更奇特。

每個 Distutils 安裝腳本的第一行總是相同的:

from distutils.core import setup

該行導入 setup() 函數,這是 Distutils 的主入口點。95% 的 Distutils 安裝腳本僅由一個對 setup() 方法的調用組成。(這完全是我臆造的統計,但如果你的 Distutils 安裝腳本所做的比僅僅調用 setup() 方法更多,你會有一個好的理由。你有一個好的理由嗎?我并不這么認為。)

setup() 方法可以有幾十個參數 。為了使每個參與者都能清楚,你必須對每個參數使用命名變量 。這不只是一項約定,還是一項硬性要求。如果嘗試以非命名變量調用 setup() 方法,安裝腳本會崩潰。

下面的命名變量是必需的:

雖然以下內容不是必須的,但我也建議你把他們包括在你的安裝腳本里:

?安裝腳本中用到的元數據具體定義在 PEP 314 中。

現在讓我們看看 chardet 的安裝腳本。它包含所有這些要求的和建議的參數,還有一個我沒有提到: packages

from distutils.core import setup
setup(
    name = 'chardet',
    packages = ['chardet'],
    version = '1.0.2',
    description = 'Universal encoding detector',
    author='Mark Pilgrim',
    ...
)

在分發過程中,這個 packages 參數凸顯出一個不幸的詞匯表重疊。我們一直在談論正在構建的“安裝包”(并將潛在地出現在Python包索引中)。但是,這并不是 packages 參數所指代的。它指代的是 chardet 模塊是一個多文件模塊這一事實 ,有時也被稱為...“包”。packages 參數告訴 Distutils 去包含chardet/ 目錄,它的 __init__.py 文件,以及所有其他構成 chardet 模塊的 .py 文件。這還算比較重要;如果你忘記了包含實際的代碼,那么所有這些關于文件和元數據的愉快交談都將是無關緊要的。

?

將包分類

Python 包索引(“PyPI”)包含成千上萬的 Python 庫。正確的分類數據將讓人們更容易找到你的包。PyPI 讓你以類別的形式瀏覽包 。你甚至可以選擇多個類別來縮小搜索范圍。分類不是你可以忽略的不可見的元數據!

你可以通過傳遞 classifiers 參數給 Distutils 的 setup() 方法來給你的軟件分類。classifers 參數是一個字符串列表。這些字符串不是任意形式的。所有的分類字符串應該來自 PyPI 上的列表

分類是可選的。你可以寫一個不包含任何分類的 Distutils 安裝腳本。不要這樣做。 你應該總是至少包括以下分類:

我還建議你包括以下分類:

包分類的優秀范例

作為例子,下面是 Django 的分類。它是一個運行在 Web 服務器上的,可用于生產環境的,跨平臺的,使用 BSD 授權的 Web 應用程序框架。(Django還沒有與Python 3兼容,因此, 并沒有列出 Programming Language :: Python :: 3 分類。)

Programming Language :: Python
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Framework :: Django
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Internet :: WWW/HTTP :: WSGI
Topic :: Software Development :: Libraries :: Python Modules

下面是 chardet 的分類。它就是在《案例研究:將 chardet 移植到 Python 3》一章提到的字符編碼檢測庫。chardet 是高質量的,跨平臺的,與 Python 3 兼容的, LGPL 許可的庫。它旨在讓開發者將其集成進自己的產品。

Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Other Environment
Intended Audience :: Developers
Topic :: Text Processing :: Linguistic
Topic :: Software Development :: Libraries :: Python Modules

以下是在本章開頭我提到的 httplib2 模塊——HTTP 的分類。httplib2 是一個測試品質的,跨平臺的,MIT 許可證授權的,為 Python 開發者準備的模塊。

Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Web Environment
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Software Development :: Libraries :: Python Modules

通過清單指定附加文件

默認情況下,Distutils 將把下列文件包含在你的發布包中:

這將覆蓋httplib2 項目的所有文件。但對于 chardet 項目,我們還希望包含 COPYING.txt 許可文件和含有圖像與 HTML 文件的整個 docs/ 目錄。要讓 Distutils 在構建 chardet 發布包時包含這些額外的文件和目錄,你需要創建一個 manifest file

清單文件是一個名為 MANIFEST.in 的文本文件。將它放置在項目的根目錄下,同 README.txtsetup.py 一起。清單文件并不是 Python 腳本,它是文本文件,其中包含一系列 Distutils 定義格式的命令。清單命令允許你包含或排除特定的文件和目錄。

以下是 chardet 項目的全部清單文件:

include COPYING.txt                                
recursive-include docs *.html *.css *.png *.gif    
  1. 第一行是不言自明的:包含項目根目錄的 COPYING.txt 文件。
  2. 第二行有些復雜。recursive-include 命令需要一個目錄名和至少一個文件名。文件名并不限于特定的文件,可以包含通配符。這行的意思是“看到在項目根目錄下的 docs/ 目錄了嗎?在該目錄下(遞歸地)查找 .html.css.png.gif 文件。我希望將他們都包含在我的發布包中。”

所有的清單命令都將保持你在項目目錄中所設置的目錄結構。recursive-include 命令不會將一組 .html.png 文件放置在你的發布包的根目錄下。它將保持現有的 docs/ 目錄結構,但只包含該目錄內匹配給定的通配符的文件。(之前我并沒有提到, chardet 的文檔實際上由 XML 語言寫成,并由一個單獨的腳本轉換為 HTML 。我不想在發布包中包含XML 文件,只包含 HTML 文件和圖像。)

?清單文件有自己獨特的格式。詳見 分發指定文件 清單文件命令

重申:僅僅在你需要包含一些 Distutils 不會默認包含的文件時才創建清單文件。I如果你確實需要一個清單文件,它應該只包含那些Distutils不會自動包含的文件和目錄。

檢查安裝腳本的錯誤

有許多事情需要留意。Distutils帶有一個內置的驗證命令,它檢查是否所有必須的元數據都體現在你的安裝腳本中。例如,如果你忘記包含 version 參數,Distutils 會提醒你。

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py check
running check
warning: check: missing required meta-data: version

當你包含了 version 參數(和所有其他所需的元數據)時, check 命令將如下所示:

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py check
running check

?

創建發布源

Distutils 支持構建多種類型的發布包。至少,你應該建立一個“源代碼分發”,其中包含源代碼,你的Distutils 安裝腳本,“read me ”文件和你想要包含其他文件 。為了建立一個源代碼分發,傳遞 sdist 命令給你的 Distutils 安裝腳本。

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py sdist
running sdist
running check
reading manifest template 'MANIFEST.in'
writing manifest file 'MANIFEST'
creating chardet-1.0.2
creating chardet-1.0.2\chardet
creating chardet-1.0.2\docs
creating chardet-1.0.2\docs\images
copying files to chardet-1.0.2...
copying COPYING -> chardet-1.0.2
copying README.txt -> chardet-1.0.2
copying setup.py -> chardet-1.0.2
copying chardet\__init__.py -> chardet-1.0.2\chardet
copying chardet\big5freq.py -> chardet-1.0.2\chardet
...
copying chardet\universaldetector.py -> chardet-1.0.2\chardet
copying chardet\utf8prober.py -> chardet-1.0.2\chardet
copying docs\faq.html -> chardet-1.0.2\docs
copying docs\history.html -> chardet-1.0.2\docs
copying docs\how-it-works.html -> chardet-1.0.2\docs
copying docs\index.html -> chardet-1.0.2\docs
copying docs\license.html -> chardet-1.0.2\docs
copying docs\supported-encodings.html -> chardet-1.0.2\docs
copying docs\usage.html -> chardet-1.0.2\docs
copying docs\images\caution.png -> chardet-1.0.2\docs\images
copying docs\images\important.png -> chardet-1.0.2\docs\images
copying docs\images\note.png -> chardet-1.0.2\docs\images
copying docs\images\permalink.gif -> chardet-1.0.2\docs\images
copying docs\images\tip.png -> chardet-1.0.2\docs\images
copying docs\images\warning.png -> chardet-1.0.2\docs\images
creating dist
creating 'dist\chardet-1.0.2.zip' and adding 'chardet-1.0.2' to it
adding 'chardet-1.0.2\COPYING'
adding 'chardet-1.0.2\PKG-INFO'
adding 'chardet-1.0.2\README.txt'
adding 'chardet-1.0.2\setup.py'
adding 'chardet-1.0.2\chardet\big5freq.py'
adding 'chardet-1.0.2\chardet\big5prober.py'
...
adding 'chardet-1.0.2\chardet\universaldetector.py'
adding 'chardet-1.0.2\chardet\utf8prober.py'
adding 'chardet-1.0.2\chardet\__init__.py'
adding 'chardet-1.0.2\docs\faq.html'
adding 'chardet-1.0.2\docs\history.html'
adding 'chardet-1.0.2\docs\how-it-works.html'
adding 'chardet-1.0.2\docs\index.html'
adding 'chardet-1.0.2\docs\license.html'
adding 'chardet-1.0.2\docs\supported-encodings.html'
adding 'chardet-1.0.2\docs\usage.html'
adding 'chardet-1.0.2\docs\images\caution.png'
adding 'chardet-1.0.2\docs\images\important.png'
adding 'chardet-1.0.2\docs\images\note.png'
adding 'chardet-1.0.2\docs\images\permalink.gif'
adding 'chardet-1.0.2\docs\images\tip.png'
adding 'chardet-1.0.2\docs\images\warning.png'
removing 'chardet-1.0.2' (and everything under it)

有幾件事情需要注意:

c:\Users\pilgrim\chardet> dir dist
 Volume in drive C has no label.
 Volume Serial Number is DED5-B4F8

 Directory of c:\Users\pilgrim\chardet\dist

07/30/2009  06:29 PM    <DIR>          .
07/30/2009  06:29 PM    <DIR>          ..
07/30/2009  06:29 PM           206,440 chardet-1.0.2.zip
               1 File(s)        206,440 bytes
               2 Dir(s)  61,424,635,904 bytes free

?

創建圖形化安裝程序

在我看來,每一個 Python 庫都應該為 Windows 用戶提供圖形安裝程序。這很容易做(即使你并沒有運行 Windows ),而且 Windows 用戶會對此表示感激。

通過傳遞 bdist_wininst 命令到你的 Distutils 安裝腳本,它可以為你創建一個圖形化的 Windows 安裝程序

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py bdist_wininst
running bdist_wininst
running build
running build_py
creating build
creating build\lib
creating build\lib\chardet
copying chardet\big5freq.py -> build\lib\chardet
copying chardet\big5prober.py -> build\lib\chardet
...
copying chardet\universaldetector.py -> build\lib\chardet
copying chardet\utf8prober.py -> build\lib\chardet
copying chardet\__init__.py -> build\lib\chardet
installing to build\bdist.win32\wininst
running install_lib
creating build\bdist.win32
creating build\bdist.win32\wininst
creating build\bdist.win32\wininst\PURELIB
creating build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5freq.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5prober.py -> build\bdist.win32\wininst\PURELIB\chardet
...
copying build\lib\chardet\universaldetector.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\utf8prober.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\__init__.py -> build\bdist.win32\wininst\PURELIB\chardet
running install_egg_info
Writing build\bdist.win32\wininst\PURELIB\chardet-1.0.2-py3.1.egg-info
creating 'c:\users\pilgrim\appdata\local\temp\tmp2f4h7e.zip' and adding '.' to it
adding 'PURELIB\chardet-1.0.2-py3.1.egg-info'
adding 'PURELIB\chardet\big5freq.py'
adding 'PURELIB\chardet\big5prober.py'
...
adding 'PURELIB\chardet\universaldetector.py'
adding 'PURELIB\chardet\utf8prober.py'
adding 'PURELIB\chardet\__init__.py'
removing 'build\bdist.win32\wininst' (and everything under it)
c:\Users\pilgrim\chardet> dir dist
c:\Users\pilgrim\chardet>dir dist
 Volume in drive C has no label.
 Volume Serial Number is AADE-E29F

 Directory of c:\Users\pilgrim\chardet\dist

07/30/2009  10:14 PM    <DIR>          .
07/30/2009  10:14 PM    <DIR>          ..
07/30/2009  10:14 PM           371,236 chardet-1.0.2.win32.exe
07/30/2009  06:29 PM           206,440 chardet-1.0.2.zip
               2 File(s)        577,676 bytes
               2 Dir(s)  61,424,070,656 bytes free

為其它操作系統編譯安裝包

Distutils 可以幫助你為 Linux 用戶構建可安裝包 。我認為,這可能不值得你浪費時間。如果你希望在 Linux 中分發你的軟件,你最好將時間花在與那些社區成員進行交流上,他們專門為主流 Linux 發行版打包軟件。

例如,我的 chardet 庫包含在 Debian GNU/Linux 軟件倉庫中(因而也包含在 Ubuntu 的軟件倉庫中)。我不曾做任何事情,我只在那里將安裝包展示了一天。Debian 社區擁有他們自己的關于打包 Python 庫的政策,并且Debian 的 python-chardet 包被設計為遵循這些公約。由于這個包存在在 Debian 的軟件倉庫中,依賴于 Debian 用戶所選擇的管理自己計算機的系統設置,他們會收到該包的安全更新和(或)新版本。

Distutils構建的包不具有Linux包所提供的任何優勢。你的時間最好花在其他地方。

?

將軟件添加到 Python 安裝包列表

上傳軟件到 Python 包索引需要三個步驟。

  1. 注冊你自己
  2. 注冊你的軟件
  3. 上傳你通過 setup.py sdistsetup.py bdist_* 創建的包。

要注冊自己,訪問 PyPI用戶注冊頁面。輸入你想要的用戶名和密碼,提供一個有效的電子郵件地址,然后點擊 Register 按鈕。(如果你有一個 PGPGPG 密鑰,你也可以提供。如果你沒有或者不知道這是什么意思,不用擔心。)檢查你的電子郵件,在幾分鐘之內,你應該會收到一封來自 PyPI 的包含驗證鏈接的郵件。點擊鏈接以完成注冊過程。

現在,你需要在PyPI注冊你的軟件并上傳它。你可以用一步完成。

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py register sdist bdist_wininst upload  
running register
We need to know who you are, so please choose either:
 1. use your existing login,
 2. register as a new user,
 3. have the server generate a new password for you (and email it to you), or
 4. quit
Your selection [default 1]:  1                                                                 
Username: MarkPilgrim                                                                          
Password:
Registering chardet to http://pypi.python.org/pypi                                             
Server response (200): OK
running sdist                                                                                  
... output trimmed for brevity ...
running bdist_wininst                                                                          
... output trimmed for brevity ...
running upload                                                                                 
Submitting dist\chardet-1.0.2.zip to http://pypi.python.org/pypi
Server response (200): OK
Submitting dist\chardet-1.0.2.win32.exe to http://pypi.python.org/pypi
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in c:\home\.pypirc)
Save your login (y/N)?n                                                                        
  1. 當你第一次發布你的項目時,Distutils 會將你的軟件加入到Python包索引中并給出它的 URL。在這之后,它只會用你在 setup.py 參數所做的任何改變來更新項目的元數據。之后,它構建一個源代碼發布 (sdist) 和一個 Windows 安裝程序 (bdist_wininst) 并把他們上傳到PyPI (upload)。
  2. 鍵入 1ENTER 選擇“ 使用已有的賬戶登錄【use your existing login.】”。
  3. 輸入你在 PyPI 用戶注冊頁面所選擇的用戶名和密碼。Distuils不會回顯你的密碼,它甚至不會在相應的位置顯示星號。只需輸入你的密碼,然后按 回車鍵
  4. Distutils 在 Python 包索引注冊你的包……
  5. ……構建源代碼分發……
  6. ……構建Windows安裝程序……
  7. ……并把它們上傳至 Python 包索引。
  8. 如果你想自動完成發布新版本的過程,你需要將你的PyPI憑據保存在一個本地文件中。這完全是不安全的而且是完全可選的。

恭喜你,現在,在Python包索引中有你自己的頁面了!地址是 http://pypi.python.org/pypi/NAME,其中 NAME 是你在 setup.py 文件中 name 參數所傳遞的字符串。

如果你想發布一個新版本,只需以新的版本號更新 setup.py 文件,然后再一次運行相同的上傳命令:

c:\Users\pilgrim\chardet> c:\python31\python.exe setup.py register sdist bdist_wininst upload

?

Python 打包工具的一些可能的將來

Distutils并非是一個代替所有并終結所有的 Python 打包,但在寫本書時(2009年8月),它是唯一可以工作在 Python 3 下的打包框架。對于Python 2,還有許多其他的框架,有的重在安裝,有的重在測試,還有的重在部署。在未來,它們中的一部分或全體都將移植到Python 3。

以下框架重在安裝:

以下框架重在測試和部署:

?

深入閱讀

關于 Distutils:

其它打包框架:

? 2001–9 Mark Pilgrim

<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            亚洲欧美在线