你可能会想要运行你自己的Python 包索引,主要有以下3 个原因。
● 官方的Python 包索引没有任何可用性保证。它由Python 软件基金会运行,这要感
谢大量的捐款。因此,它往往意味着网站可能会倒闭。你不希望由于PyPI 的故障
而中途停止部署或打包过程。
● 即使是不会公开发布的闭源代码,将Python 编写的可复用组件正确打包也很有用。
它简化了代码库,因为公司内用于不同项目的包不需要供应(vendored)。你可以
从仓库直接安装这些包。这简化了对这些共享代码的维护,如果许多团队在不同项
目上工作,这还可能会降低整个公司的开发成本。
● 使用setuptools 将整个项目打包是非常好的做法。然后,新应用版本的部署非常简单,只需运行pip install --update my-application。
代码供应
代码供应(code vendoring)是将外部包的源代码包含在其他项目的
源代码(仓库)中的一种做法。如果项目代码依赖于某个外部包的
特定版本,且其他包可能也需要这个外部包(但是完全不同的版本),
那么通常会采用这种做法。例如,流行的requests 包在源代码树
中包含了(vendor)某个版本的urllib3,因为它与这个库紧密耦
合,并且很可能和其他版本的urllib3 不兼容。特别经常被包含
在其他模块中的一个模块的例子是six。它可以在许多流行项目的
源代码中找到,例如Django(django.utils.six)、Boto(boto.
vendored.six)或Matplotlib(matplotlib.externals.six)。
虽然一些成功的大型开源项目甚至也在使用代码供应,但如果
可能的话应避免使用。这只在某些情况下可以正当使用,不应
该被用于替代包依赖管理。
PyPI 镜像
如果允许安装工具从PyPI 的一个镜像下载包,那么就可以缓解PyPI 故障带来的问题。
事实上,官方的Python 包索引已经通过内容分发网络(Content Delivery Network,CDN)
提供服务,因此它是自带镜像的。但这无法改变下列事实:似乎不时会有一些糟糕的日子,
下载一个包的任何尝试都会失败。对于这种情况,使用非官方的镜像也不是解决办法,因
为可能会引起一些安全问题。
最好的解决方案是使用你自己的PyPI 镜像,里面包含所有你需要的包。只有你会使
用这个镜像,才更容易确保其可用性。另一个优点是,每当这项服务停机时,你不需要
依靠其他人来启动它。由PyPA 维护并推荐的镜像工具是bandersnatch。它允许你制作
Python 包索引全部内容的镜像,你可以在.pypirc 文件中repository 区段的index-url
选项中进行设置(正如上一章所述)。这个镜像不接受上传,并且没有PyPI 的web 部分。
无论怎样一定要小心!完整的镜像可能需要数百GB 的存储,其大小将随着时间的推移而
持续增长。
但如果我们有更好的选择,为什么要止步于简单的镜像呢?你几乎不可能需要整个包索引的镜像。即使对于一个有上百个依赖的项目,也只是所有可用包的一小部分。此外,
无法上传自己的私有包,也是这种简单镜像的一大限制。使用bandersnatch 的代价这么大,
而附加的价值似乎却非常小。而且对大多数情况来说都是这样。如果只需要为几个项目中
的一个维护包镜像,那么更好的方法是使用devpi。它是与PyPI 兼容的包索引实现,可以
提供:
● 上传非公开包的私有索引;
● 索引镜像。
与bandersnatch 相比,devpi 的主要优点在于它处理镜像的方式。它当然可以对其他索
引制作一般完整的镜像,就像bandersnatch 所做的那样,但这并不是它的默认做法。它维
护客户端已经请求的包组成的镜像,而不是对整个仓库进行代价高昂的备份。因此,每当
安装工具(pip、setuptools 和easyinstall)请求一个包时,如果它不在本地镜像
中,那么devpi 服务器将会尝试从镜像索引(通常是PyPI)中下载并提供。一旦包下载完
成之后,devpi 将定期检查其更新,以保持镜像的最新状态。
如果你请求一个尚未制作镜像的新包,且上游包索引出现了故障,那么镜像方法有很
小的可能性会失败。不管怎样,由于在大多数部署中,你将会仅依赖在索引中已经存在镜
像的包,因此这一可能性很小。已经请求过的包的镜像状态与PyPI 保持完全一致,新版本
将会自动下载。这似乎是一个非常合理的权衡。