- 引言
- 本书涉及的内容
- 第 1 部分 Python 开发入门
- 第 1 章 Python 入门
- 第 2 章 开发 Web 应用
- 第 3 章 Python 项目的结构与包的创建
- 第 4 章 面向团队开发的工具
- 第 5 章 项目管理与审查
- 第 6 章 用 Mercurial 管理源码
- 第 7 章 完备文档的基础
- 第 8 章 模块分割设计与单元测试
- 第 9 章 Python 封装及其运用
- 第 10 章 用 Jenkins 持续集成
- 第 11 章 环境搭建与部署的自动化
- 第 12 章 应用的性能改善
- 第 13 章 让测试为我们服务
- 第 14 章 轻松使用 Django
- 第 15 章 方便好用的 Python 模块
- 附录 A VirtualBox 的设置
- 附录 B OS(Ubuntu)的设置
9.1 使用程序包
本部分将介绍一些能应用于部署及自动测试的 pip 功能。
9.1.1 程序包的版本指定
有些时候,我们需要安装一些特定版本的程序包,比如想查看某版本下的运行情况,或者需要用到某个版本之前的最后一版。
指定程序包版本的方法有几个。以 pip install colander 为例,colander 是用于模式定义和校验的 Python 程序包。我们用几种不同方式来安装指定版本(LIST 9.1)。
LIST 9.1 指定版本的方法
$ pip install -U colander # 1.0 : 最新的稳定版 $ pip install -U colander==1.0" # 1.0 : 指定版本 $ pip install -U "colander<1.0" # 0.9.9 : 1.0 版以前的最后一个稳定版 $ pip install -U "colander<1.0" --pre # 1.0b1 : 1.0 版以前的最后一个版本 $ pip install -U colander==1.0b1 # 1.0b1 : 指定版本 $ pip install -U "colander<=1.0" # 1.0 : 1.0 版以前的最后一个稳定版(包括 1.0 版) $ pip install -U "colander>=0.9,<0.9.9" # 0.9.8 : 0.9 版(含)以后 0.9.9 版以前的最后一个稳定版
可见,指定版本的方法并不唯一。某些指定方法需要用双引号“" ”括起来。这是防止不等号“< ”“> ”被 shell 解释为重定向。
为了便于理解,我们这里指定了 -U(--upgrade)选项,因此即便指定程序包已存在于计算机中,系统仍会再安装一遍。
另外,pip 不会安装 alpha 版、beta 版等预发布版本的程序包。至于某版本是否为预发布版本,可以看版本号是否像 1.3a1、1.3b1 这样后面跟着 a1、b1(在 PEP 4401 中有相关定义)。要安装上述预发布版本时,需要明确指定版本号,或者附加 --pre 选项。
1 https://www.python.org/dev/peps/pep-0440
专栏 版本命名规范与大小关系
版本的命名规范(结构)与大小关系在 PEP 440 中有相关规定。
版本号要遵循 [N!]N(.N)*[{a|b|c}N][.postN][.devN] 的结构。N 可以为正整数或0。(.N)* 可以重复出现任意次。被 [] 括起来的部分可以省略。a、b、c 分别代表 alpha、beta、rc。
各版本号的先后顺序自然不能根据字符串排序来定,其编号方式及排序大致如 LIST 9.2 所示。
LIST 9.2 版本的编号方式及排序
0.9.1 0.10 1.0a1.dev1 1.0a1 1.0b1 1.0b2 1.0c1 1.0 1.0-post1 1.0.1
实际上,版本号这东西我们很少会在命令行指定。大多是在 setup.py 中指定版本时才会用到它。
举个例子,假设我们为 Python 2.7 开发的 myapp 程序依赖于名叫 securelib 的外部库。然而 securelib 从 1.0 版之后才开始提供我们所需的功能,所以必须指定安装 1.0 以后的版本(LIST 9.3)。现阶段的情况是,myapp 的 setup.py 中并没有给 install_requires 指定版本号。这种情况下,如果计算机中已经装有 securelib,那么其版本将不会被更新。一旦使用者环境中安装的是不提供所需功能的旧版本,程序将无法正常运行。
LIST 9.3 在 setup.py 中指定依赖库为 1.0 以后的版本
setup( name='myapp', ... install_requires=[ 'securelib>=1.0', ], )
这里如果指定 securelib==1.0 又会带来别的问题。因为这样一来,只要我们使用 myapp,securelib 的版本就会被限制在 1.0。就算 securelib 因为安全问题发布了 1.0.1,myapp 的使用者也只能继续用 securelib-1.0。为避免这类问题,要尽量避免在 setup.py 中指定固定版本。
接下来了解一下另一种情况,就是指定某版本之前的版本。比如 securelib-3.0 发布,其使用的 API 发生了重大变更。此时如果安装 myapp 时安装了 3.0,那么 myapp 将无法正常工作。为防止这个情况发生,可以给 myapp 追加一条指定,指定其使用 securelib-3.0 之前的版本(LIST 9.4)。
LIST 9.4 在 setup.py 中指定依赖库为 1.0 以后、3.0 以前的版本
setup( name='myapp', ... install_requires=[ 'securelib>=1.0,<3.0', ], )
做过上述修改后,就能让使用者安装正确版本的程序包,保证 myapp 正常运行了。
NOTE
本例中,myapp 选择使用旧版本的securelib。然而,如果程序一直依赖于旧版本的 securelib,将无法使用今后推出的安全更新。所以根据 API 的变更重新修改、测试 myapp,让其支持 securelib-3.0 才是比较好的选择。
9.1.2 从非 PyPI 服务器安装程序包
pip 搜索程序包时默认引用 PyPI 的 URL2 。需要引用 PyPI 以外的服务器时,有两个选项可供使用。
2 https://pypi.python.org/simple
-i(--index-url)选项可以指定其他兼容服务器来代替 PyPI。这些服务器被称为 index 服务器,PEP 3013 、PEP 4384 中对它们的规格有着严格规定。举个例子,当我们想从 PyPI 的测试服务器 5 安装程序包时,可以像下面这样执行。
3 https://www.python.org/dev/peps/pep-0301
4 https://www.python.org/dev/peps/pep-0438
5 https://testpypi.python.org/
$ pip install bpbook -i https://testpypi.python.org/simple
同样地,我们可以在公司内搭建替代 PyPI 的 index 服务器,用 pip 从该服务器上进行安装。PyPI 上公开了多种搭建 PyPI 替代服务器的方法,其中 devpi6 可以给每个用户设置多个索引,同时具备 PyPI 镜像功能,非常好用。
6 https://pypi.python.org/pypi/devpi
另一个用起来更方便的选项是 find-links。-f(--find-links)选项用来指定包含目标程序包链接的页面。该选项指定的页面所链接的程序包会优先于 index 服务器。如果在指定页面没有找到目标程序包,计算机则会引用 index 服务器内的资源进行安装。
$ pip install -f https://bitbucket.org/shimizukawa/logfilter/downloads logfilter Downloading/unpacking logfilter Downloading logfilter-0.9.2.zip Running setup.py egg_info for package logfilter
像上面例子中这样,指定 -f 选项后,计算机便会检查该页面内的链接,识别程序包名称,自动获取并安装最新版本。不过,pip install 只能识别符合规定的正确包名。规定大致如下。
{ 包名}-{ 版本号}(-{ 平台})?.{ 扩展名}
指定平台的部分可以省略。如果一个程序包指定了平台(如 win32),那证明它在其他平台上是无法运行的。因此,pip 只会寻找并安装适合当前平台的程序包。
专栏 安装未存放于PyPI 的程序包
有些程序包虽然被添加到了PyPI,但实际的程序包文件却存放在其他地方。这种程序包无法用 pip install 程序包名的方式进行安装,所以必须指定 --allow-external 选项。
$ pip install --allow-external 程序包名
另外,如果程序包被存放在 http 环境而非 https 环境下,pip 会认为程序包有可能在通讯中被篡改,因此会中断安装。在迫不得已一定要使用这类程序包的情况下,请指定 --allowunverified 选项。
$ pip install --allow-unverified 程序包名
专栏 已删除的 PyPI 镜像规格
PEP 3817 对 PyPI 镜像服务器规格作了规定。但随着 CDN 等技术的应用,以及 PyPI 服务器逐渐稳定,镜像服务器规格的删除在 PEP 4648 中被提出,并最终于 2014 年春完成删除。因此,如今已不需要用 --index-url 选项引用镜像服务器。另外,曾经的 --use-mirror 选项也于pip-1.5起被删除。
曾提供服务的 a.pypi.python.org、b.……之类的镜像服务器的域名早已废止。现在使用旧版本 pip,通过环境变量或设置将镜像引用设为有效后,pip 有可能不正常工作。因此请各位避免使用那些会引用镜像服务器的选项。
7 https://www.python.org/dev/peps/pep-0381
8 https://www.python.org/dev/peps/pep-0464
9.1.3 程序包的发布格式
如今上传到 PyPI 的程序包大多采用 sdist 格式。sdist 是包含程序包元数据及构建方法的存档格式。它会在每次安装时读取各环境的设置,存在 C 扩展时进行构建,然后检查所需的 Python 程序包并复制到 site-packages。另外,sdist 中其实有许多文件并不会被安装。这些工作在每个平台上实施一次就足够了。相对地,二进制包的特点是仅包含已构建完毕的 C 扩展和 Python 程序包,仅解压程序包的存档即可完成安装。
Python 的二进制包长期以来使用着 setuptools 实现的 egg 格式。但是 pip 并不支持 egg 格式的程序包,这导致该格式在普及的道路上一直停滞不前。随着 Python 封装的规范化,PEP 4279 提出了一个能克服 egg 缺点的 wheel 格式。加之 pip 也开始支持 wheel 格式,所以其很快在部署等方面得到应用。
9 https://www.python.org/dev/peps/pep-0427
已上传到 PyPI 的 wheel 格式程序包可以通过 pip 直接安装。接下来,我们以安装 Django 为例来学习一下。
$ pip install django Downloading/unpacking django Downloading Django-1.7.1-py2.py3-none-any.whl (7.4MB): 7.4MB downloaded Storing download in cache at /var/pip-chache/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fpy2.py3%2FD%2FDjango%2FDjango-1.7.1-py2.py3-none-any.whl Installing collected packages: django Successfully installed django Cleaning up...
可以看到计算机从 PyPI 下载了 wheel 格式的程序包。pip 是从 1.5 版以后开始正式支持 wheel 格式的。这里我们将 pip 的版本降回 1.4,然后再安装 Django 试试。
$ pip install django Downloading/unpacking django Downloading Django-1.7.1.tar.gz (7.5MB): 7.5MB downloaded Storing download in cache at /home/aodag/.pip/eggs/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2FD%2FDjango%2FDjango-1.7.1.tar.gz Running setup.py egg_info for package django warning: no previously-included files matching '__pycache__' found under directory '*' warning: no previously-included files matching '*.py[co]' found under directory '*' Installing collected packages: django Running setup.py install for django warning: no previously-included files matching '__pycache__' found under directory '*' warning: no previously-included files matching '*.py[co]' found under directory '*' changing mode of build/scripts-2.7/django-admin.py from 644 to 755 changing mode of /home/aodag/works/bpbook2015/djangoenv/bin/django-admin.py to 755 Installing django-admin script to /home/aodag/works/bpbook2015/djangoenv/bin Successfully installed django Cleaning up...
可以看到,这种情况下的安装是通过 tar.gz 存档的 sdist 进行的。另外,相较于通过 wheel 进行安装,通过 sdist 进行安装时,下载后的处理要多出很多。
对于 Django 这种不包含 C 扩展的程序包而言,上面的差距还算可以接受。可是一旦换成 Pillow 这种包含 C 扩展,且 C 扩展需要大量依赖库的程序包时,差距就无法忽视了。现在 PyPI 上提供了支持各种 Python 版本及 CPU 的 wheel 格式 Windows 版 Pillow 程序包,即便是在难以准备编译器的 Windows 环境下,也只需要通过 pip 进行安装即可开始使用 Pillow 了。
专栏 wheel 程序包的文件名
PEP 42710 中规定了 wheel 程序包的命名规则。自此,wheel 程序包依赖于哪个版本的 Python 以及哪个OS,仅凭包名即可一目了然。
其命名与解释规则如下。
{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl
LIST 9.5 是一些已上传到PyPI 的程序包的文件名。
LIST 9.5 wheel 程序包文件名示例
bpmappers-0.7-py2-none-any.whl Django-1.7.1-py2.py3-none-any.whl MarkupSafe-0.23-cp27-none-linux_x86_64.whl Pillow-2.6.1-cp32-cp32m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.whl
10 https://www.python.org/dev/peps/pep-0427
9.1.4 生成wheelhouse 的方法
wheel 格式的程序包方便好用,但我们前面也说了,PyPI 上大部分程序包都是 sdist 格式。为方便今后在 CI 工具上和部署时能有 wheel 可用,我们来学习一下如何根据 sdist 生成 wheel。pip 可以通过 wheel 命令将 PyPI 上只提供了 sdist 的程序包转换成 wheel 格式并保存在本地计算机中。本地的 wheel 格式程序包默认保存在 wheelhouse 目录下,也正因为如此,我们习惯将所有保存 wheel 格式程序包的目录都称为 wheelhouse(与实际目录名称无关)。使用 wheel 命令前,需要先安装 wheel 包。
$ pip install wheel
接下来,我们以生成 Pillow 的 wheel 为例实际生成一个 wheelhouse。
$ pip wheel pillow Downloading/unpacking pillow Downloading Pillow-2.6.1.tar.gz (7.3MB): 7.3MB downloaded Running setup.py egg_info for package pillow ... Building wheels for collected packages: pillow Running setup.py bdist_wheel for pillow Destination directory: /home/aodag/works/bpbook2015/wheelhouse Successfully built pillow Cleaning up...
$ ls wheelhouse/ -l -rw-r--r-- 1 bp bp 714085 11 月 10 19:31 Pillow-2.6.1-cp27-none-linux_x86_64.whl
可见,pip 和 wheel 组合使用之后,可以将只提供了 sdist 的程序包转换为 wheel 格式保存。
NOTE
生成并保存在 wheelhouse 里的 wheel 依赖于执行 wheel 命令的平台。库中包含 C 扩展的 wheel 不具备跨平台通用性。比如正式服务器使用 Linux 但开发者使用 OS X,就需要准备一个与正式服务器同平台的 CI 服务器,在上面执行生成 wheelhouse 的任务。
9.1.5 从 wheelhouse 安装
现在我们来学习如何安装 wheelhouse 内的程序包。最简单的方法就是用 pip install 直接指定 wheelhouse 目录下的文件。
$ pip install wheelhouse/Pillow-2.6.1-cp27-none-linux_x86_64.whl Unpacking ./wheelhouse/Pillow-2.6.1-cp27-none-linux_x86_64.whl Installing collected packages: Pillow Successfully installed Pillow Cleaning up...
由于 C 扩展的编译等处理在这个阶段早已完成,所以安装会十分迅速。不过,wheel 格式程序包的文件名通常比较复杂,每次安装都手动输入实在麻烦。
其实,只要用 -f(--find-links)指定 wheelhouse 目录,程序包名部分就可以只写 Pillow 了。
$ pip install -f wheelhouse pillow Downloading/unpacking pillow Installing collected packages: pillow Successfully installed pillow Cleaning up...
保险起见,我们添上 --no-index 选项以防 wheelhouse 以外的目录被引用。
$ pip install --no-index -f wheelhouse pillow Ignoring indexes: https://pypi.python.org/simple/ Downloading/unpacking pillow Installing collected packages: pillow Successfully installed pillow Cleaning up...
事先在 wheelhouse 目录下生成的 wheel 格式程序包都可以通过上述步骤进行安装。只要将所有依赖库全都放到 wheelhouse 下,我们就可以脱机完成环境搭建。另外,由于安装时所需的处理极少,所以非常适合需要多次重复搭建相同环境的情况。为部署和 CI 工具搭建环境时,请务必记得运用 wheelhouse 目录。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论