本文介绍了 setuptools 框架的内容,它是 PEAK 的一个副项目,它提供了比 distutils 更加简单的包管理和发行功能。
开始
setuptools 模块很会 "规避"。例如,如果我们下载一个使用 setuptools 而不是使用 distutils 构建的包,那么安装就应该可以像我们期望的一样工作:通常使用 python setup.py install 就可以。为了实现这种功能,使用 setuptools 绑定在一起的包就会在归档文件中包含一个很小的引导模块 ez_setup.py。此处惟一需要注意的是 ez_setup.py 试图在后台下载并安装所需要的 setuptools ―― 当然,这需要有一个连接网络的机器。如果 setuptools 早已在本地机器上安装了,那么这个后台步骤就不再需要执行;但是如果它需要手工进行安装,那么很多透明性就都丢失了。不过,大部分系统现在都有一个 Internet 连接了;为没有连接网络的机器多执行几个特殊步骤也并非特别麻烦。
setuptools 的真正优点并不在于实现 distutils 所能实现的功能 ―― 尽管它 的确 增强了 distutils 的功能并简化了 setup.py 脚本中的内容。setuptools 最大的优势是它在包管理能力方面的增强。它可以使用一种更加透明的方法来查找、下载并安装依赖包;并可以在一个包的多个版本中自由进行切换,这些版本都安装在同一个系统上;也可以声明对某个包的特定版本的需求;还可以只使用一个简单的命令就能更新到某个包的最新版本。给人印象最为深刻的是,即使有些包的开发人员可能还从未考虑过任何 setuptools 兼容性问题,我们依然可以使用这些包。
下面让我们详细探讨一下。
引导
工具 ez_setup.py 是一个简单的脚本,它可以引导 setuptools 中其余部分。有点让人困惑的是,完整 setuptools 包中所提供的 easy_install 脚本与 ez_setup.py 所实现的功能是相同的。不过前者假设 setuptools 早已安装了,因此它会跳过幕后的安装过程。这两个版本都可以接受相同的参数和开关。
这个过程中的第一个步骤是下载一个小脚本 ez_setup.py:
清单 1. 下载引导脚本
% wget -q http://peak.telecommunity.com/dist/ez_setup.py
然后,就可以不带任何参数运行脚本来安装 setuptools 中其余部分了(如果不作为一个单独的步骤来执行这个步骤,在首次安装其他包时,它还是会被完成)。会看到类似于下面的内容(当然,这要取决于所使用的版本):
清单 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
完毕。这就是我们需要确保在系统上安装 setuptools 而需要做的工作。
安装包
对于很多 Python 包来说,要安装这些包,需要做的就是将这些包的名字作为一个参数传递给 ez_setup.py 或 easy_install。既然目前已经使用引导脚本加载了 setuptools,那就可以使用内部更加简化的 easy_install(实际上它与我们选择的版本的区别很小)了。
例如,假设希望安装 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/ 上查找有关这个包的信息,然后查找真正可以下载它的地方(此处 egg 包就在 cheeseshop.python.org 上;后面将介绍有关 egg 的更多内容)。
现在不仅仅可以安装某个包的最新版本(这是默认操作)。如果愿意,还可以为 easy_install 提供一个特定的版本需求。现在让我们尝试安装 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。不过,现在让我们来尝试安装一下这个包,已知的只有它所在的 HTTP(或 FTP、SVN、CVS)位置(setuptools 可以理解所有这些协议)。我的下载 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 现在可以在一个脚本中运行。但是假设现在需要对 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
现在通常已经安装了两个版本的 Gnosis Utilities,当前活动版本是 1.2.0。将活动版本切换回 1.2.1 也非常简单:
清单 7. 在系统范围修改 "活动" 版本
% 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
当然,这一次只能使一个版本是活动的。不过通过在各个脚本上面放上这样两行类似内容,就可以让脚本选择自己希望使用的版本:
清单 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。具体地说,这个归档文件是根据类似于 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 创建为一个发布后版本。发行版归档文件本身并没有什么变化,不过是在 Cheeseshop 里增加了一点元数据。只需要少量努力,就可以使用更加简单的安装程序(注意,出于测试目的,我运行了一个 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 [...]
我把这个过程剩余的部分忽略掉了,因为这与您前面看到的内容没什么两样。惟一的区别在于 easy_install 要在 Cheeseshop(换言之 www.python.org/pypi/)上寻找可以匹配指定名字的元数据,并使用这些信息来查找真正的下载位置。在这种情况中,所列出的 .ANNOUNCE 文件没有包含任何有帮助的内容,不过 easy_install 还会继续查看另一个所列的 URL,这会证明它是一个下载目录。
关于 egg
egg 是一个包含所有包数据的文件包。在理想情况中,egg 是一个使用 zip 压缩的文件,其中包括了所有需要的包文件。但是在某些情况下,setuptools 会决定(或被开关告知)包不应该是 zip 压缩的。在这些情况下,egg 只是一个简单的未曾压缩的子目录,但是里面的内容是相同的。使用单一的版本可以方便地进行转换,并可以节省一点磁盘空间,但是 egg 目录从功能和组织结构上来说都是相同的。一直使用 JAR 文件的 Java? 技术的用户会发现 egg 非常熟悉。
由于最新的 Python 版本中(需要 2.3.5+ 或 2.4)导入挂钩的更改,可以简单地通过设置 PYTHONPATH 或 sys.path 并像往常一样导入相应的包来使用 egg。如果希望采用这种方法,就不需要使用 setuptools 或 ez_setup.py 了。例如,在本文使用的工作目录中,我就为 PyYAML 包放入了一个 egg。现在我就可以使用这个包了,方法如下:
清单 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 会话内的 sys.path的)这种操作有些脆弱。egg 的发现最好是在新一点的 .pth 文件中进行。在 site-packages/ 或 PYTHONPATH 中的任何 .pth 文件都会进行解析来执行其他导入操作,其方法类似于检查可能包含包的那些目录位置一样。如果使用 setuptools 来处理包的管理功能,那么在安装、更新、删除包时,就需要修改一个名为 easy-install.pth 的文件。而且可以按照自己喜欢的方式对这个 .pth 进行命名(只要其扩展名是 .pth 即可)。例如,下面是我的 easy-install.pth 文件的内容:
清单 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 会在运行时实现这种功能。也可以在 site-packages/ 下创建任意多个 .pth 文件;每个都可以列出有哪些 egg 是可用的。
增强安装脚本
上面所述的这种安装 setuptools naive 包的能力(请参阅 清单 6)只部分有效。也就是说,包 Gnosis_Utils 的确安装上了,但是并不完整。所有常见的功能都可以工作,但是在自动生成 egg 时却忽略了很多支持文件 ―― 大部分是扩展名为 .txt 的文档和扩展名为 .xml 的测试文件(还有一些其他的 README、.rnc、.rng、.xsl 和围绕子包的文件)。在安装时,所有这些支持文件都 "最好要有",而没有严格要求一定要有。不过,我们仍然希望能够包含所有的支持文件。
Gnosis_Utils 使用的 setup.py 脚本实际上非常复杂。除了列出基本的元数据之外,在第 467 行代码中,它还对 Python 版本的功能和 bug 进行完整测试;解决旧版本的 distutils 中的一些故障;回溯跳过对不支持部分的安装(例如,如果 pyexpat 在 Python 发行版中并没有包括);处理 OS 行结束符的转换;创建多个归档/安装程序类型;根据测试结果重新构建 MANIFEST 文件。能够实现处理这些工作的能力要感谢此包的另外一个维护人员 Frank McIngvale;这些能力可以让 Gnosis_Utils 能成功安装回 Python 1.5.1 的版本,当然前提是需要这么做(早期版本中的功能没有这么丰富)。不过此处我要向大家展示的脚本并没有像 distutils 脚本一样做这么复杂的事情:它只是简单地假设系统中已经安装了一个 "普通的" 最新版本的 Python。即使这么讲,setuptools 能让安装脚本变得如此简单还是非常吸引人。
在第一次尝试时,让我们来创建一个 setup.py 脚本,它是从 setuptools 手册中借用的,并试图使用它来创建一个 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__
这点努力就已经可以起作用;至少可以部分地起作用。使用这几行内容的确可以创建一个 egg,不过这个 egg 与使用 easy_install 创建的 egg 有一些相似的缺点:缺乏对不使用 .py 命名的文件的支持。因此让我们再试一次,只是需要稍微再努力一点:
清单 13. 添加缺少的 package_data
from setuptools import setup, find_packages
setup(
name = "Gnosis_Utils",
version = "1.2.2",
package_data = {'':['*.*']},
packages = find_packages(),
)
这就是需要做的所有操作。当然,根据实际情况,通常希望对它进行一些调整。例如,它可能会列出下面的内容:
清单 14. 打包特定类型文件类型
package_data = {'doc':['*.txt'], 'xml':['*.xml', 'relax/*.rnc']}
这段内容翻译一下就是:将 .txt 文件包括在 doc/ 子包中,将 .xml 文件包括在 xml/ 子包中,将所有 .rnc 文件包括在 xml/relax/ 子包中。
结束语
本文实际上只介绍了用支持 setuptools 的发行版可以执行的定制操作的表层的知识。例如,假设您现在有一个发行版(可以是首选的 egg 格式或另外一种归档类型),您就可以使用一个命令将这个归档文件和元数据上载到 Cheeseshop 上。显然,完整的 setup.py 脚本应该包含旧版本 distutils 脚本中所包含的同样详细的元数据;为了简单起见,本文跳过了这些内容,但是其参数名与 distutils 是兼容的。
尽管要完全适应 setuptools 所提供的巨大功能需要一些时间,但是实际上它确实可以让维护您自己的包和安装外来包都要比 distutils 更加简单。如果您所关心的内容仅仅是安装包,那么您所需要了解的内容在本文的介绍中已经全部包括了;只是您在描述您自己的包时可能会发现一些复杂性,不过仍然没有使用 distutils 那么复杂。
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。