您当前位置:首页 ‣ 深入 Python 3 ‣
难度级别:♦♦♦♦♢
❝ 您会发现,羞愧就像痛苦一样,您只会感受到一次。 ❞
— 梅特伊侯爵夫人,危险的关系
真正的艺术家会发布作品。或者正如史蒂夫·乔布斯所说。您是否想发布 Python 脚本、库、框架或应用程序?太好了。世界需要更多 Python 代码。Python 3 附带了一个名为 Distutils 的打包框架。Distutils 是很多东西:构建工具(为您)、安装工具(为您的用户)、包元数据格式(用于搜索引擎)等等。它与 Python 包索引(“PyPI”)集成,后者是开源 Python 库的中央存储库。
所有这些 Distutils 的方面都围绕着安装脚本,传统上称为 setup.py
。事实上,您已经在本书中看到了几个 Distutils 安装脚本。您使用 Distutils 在 HTTP Web 服务 中安装了 httplib2
,并在 案例研究:将 chardet
移植到 Python 3 中再次安装了 chardet
。
在本章中,您将学习 chardet
和 httplib2
的安装脚本如何工作,并且您将逐步完成发布自己的 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 = "[email protected]",
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.
"""
)
☞
chardet
和httplib2
是开源的,但您没有义务在任何特定许可下发布您自己的 Python 库。本章中描述的过程适用于任何 Python 软件,无论其许可证如何。
⁂
发布第一个 Python 包是一个艰巨的过程。(发布第二个会更容易一些。)Distutils 试图尽可能地自动化它,但有一些事情您必须亲自完成。
⁂
要开始打包您的 Python 软件,您需要整理好文件和目录。httplib2
目录如下所示
httplib2/ ① | +--README.txt ② | +--setup.py ③ | +--httplib2/ ④ | +--__init__.py | +--iri2uri.py
.txt
扩展名,并且应使用 Windows 风格的回车符。仅仅因为您使用了一个从命令行运行并包含其自身宏语言的精美文本编辑器,并不意味着您需要给您的用户制造麻烦。(您的用户使用记事本。很悲哀,但这是事实。)即使您使用的是 Linux 或 Mac OS X,您的精美文本编辑器无疑也具有保存具有 Windows 风格回车符的文件的选项。setup.py
,除非您有充分的理由不这样做。您没有充分的理由不这样做。.py
文件,您应该将其与您的“自述文件”和安装脚本一起放在根目录中。但是 httplib2
不是单个 .py
文件;它是 一个多文件模块。但没关系!只需将 httplib2
目录放在根目录中,这样您就在 httplib2/
根目录中的 httplib2/
目录中有一个 __init__.py
文件。这不是问题;事实上,它将简化您的打包过程。chardet
目录看起来略有不同。与 httplib2
一样,它也是 一个多文件模块,因此在 chardet/
根目录中有一个 chardet/
目录。除了 README.txt
文件之外,chardet
在 docs/
目录中还有 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 安装脚本做的不仅仅是调用 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()
函数。classifiers
参数是一个字符串列表。这些字符串不是自由格式的。所有分类器字符串都应该来自 PyPI 上的这个列表。
分类器是可选的。您可以在没有任何分类器的情况下编写 Distutils 安装脚本。不要那样做。您应该始终至少包含以下分类器
"Programming Language :: Python"
和 "Programming Language :: Python :: 3"
。如果您不包含这些,您的包将不会出现在此 Python 3 兼容库列表中,该列表链接自 pypi.python.org
的每个页面的侧边栏。"Operating System :: OS Independent"
。只有当您的软件需要对每个平台进行特定支持时,才需要多个 Operating System
分类器。(这并不常见。)我还建议您包含以下分类器
Developers
、End Users/Desktop
、Science/Research
和 System Administrators
。Framework
分类器。如果不是,请省略它。例如,以下是 Django 的分类器,Django 是一个生产就绪的、跨平台的、BSD 许可的 Web 应用程序框架,它运行在您的 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
是 案例研究:将 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
的分类器,httplib2
是 HTTP Web 服务 章节中介绍的库。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 将在您的发行包中包含以下文件
README.txt
setup.py
packages
参数中列出的多文件模块所需的 .py
文件py_modules
参数中列出的单个 .py
文件这将涵盖httplib2
项目中的所有文件。但是对于chardet
项目,我们还希望包含COPYING.txt
许可证文件以及包含图像和HTML文件的整个docs/
目录。要告诉Distutils在构建chardet
发行包时包含这些附加文件和目录,你需要一个清单文件。
清单文件是一个名为MANIFEST.in
的文本文件。将其放在项目的根目录中,与README.txt
和setup.py
并排。清单文件不是Python脚本;它们是包含一系列以Distutils定义的格式编写的“命令”的文本文件。清单命令允许你包含或排除特定的文件和目录。
这是chardet
项目的完整清单文件
include COPYING.txt ①
recursive-include docs *.html *.css *.png *.gif ②
COPYING.txt
文件。recursive-include
命令接受一个目录名和一个或多个文件名。文件名不限于特定文件;它们可以包含通配符。这行意味着“看到项目根目录中的docs/
目录了吗?在其中(递归地)查找.html
、.css
、.png
和.gif
文件。我想要在我的发行包中包含所有这些文件。”所有清单命令都保留你在项目目录中设置的目录结构。该recursive-include
命令不会将一堆.html
和.png
文件放到发行包的根目录中。它将保留现有的docs/
目录结构,但只包含该目录中与给定通配符匹配的文件。(我之前没有提到,但chardet
文档实际上是用XML编写的,并由单独的脚本转换为HTML。我不想在发行包中包含XML文件,只想要HTML和图像。)
☞清单文件有自己的独特格式。有关详细信息,请参阅指定要分发的文件和清单模板命令。
重申一下:只有当你想要包含Distutils默认情况下不包含的文件时,你才需要创建一个清单文件。如果你确实需要一个清单文件,它应该只包含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安装脚本、你的“自述”文件以及你想包含的任何其他文件。要构建源代码发行版,请将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)
这里要注意几点
MANIFEST.in
)。COPYING.txt
以及docs/
目录中的HTML和图像文件。dist/
目录。在dist/
目录中,你可以找到可以分发的.zip
文件。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用户也会对此表示感谢。
Distutils可以为你创建图形Windows安装程序,方法是将bdist_wininst
命令传递给你的Distutils安装脚本。
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包索引是一个三步过程。
setup.py sdist
和setup.py bdist_*
创建的包要注册你自己,请访问PyPI用户注册页面。输入你想要的用户名和密码,提供一个有效的电子邮件地址,然后点击Register
按钮。(如果你有PGP或GPG密钥,你也可以提供它。如果你没有密钥或不知道这是什么意思,不要担心。)检查你的电子邮件;几分钟内,你应该收到来自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 ⑧
setup.py
参数中所做的任何更改来更新项目元数据。接下来,它会构建一个源代码发行版(sdist
)和一个Windows安装程序(bdist_wininst
),然后将它们上传到PyPI(upload
)。恭喜你,你现在在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
⁂
Distutils并不是Python打包的全部,但截至本文撰写之时(2009年8月),它是唯一在Python 3中有效的打包框架。Python 2中还有其他一些框架;有些专注于安装,有些专注于测试和部署。这些框架中的一些或全部可能会在将来被移植到Python 3中。
这些框架专注于安装
这些框架专注于测试和部署
virtualenv
zc.buildout
py2exe
⁂
关于Distutils
setup()
函数的所有可能参数site-packages
目录关于其他打包框架
© 2001–11 Mark Pilgrim