Published on

Python包管理工作流

从pip到virtualenv到pipenv

Reading Time

6 min read

凡是一个成熟的软件生态都有它的软件源和对应的包管理工具,Python 也不例外,pip 就是它的(官方推荐的)包管理工具。可能很多小伙伴都对 pip 比较熟悉了,那么使用 pip 会有什么问题呢?

使用 requirements.txt 管理依赖

pip 最普通的使用方法就是pip install <package_name>,如果要指定版本,可以用pip install <package_name>==<version>。如果你的应用中包含很多条依赖,可以把这些依赖都写在一个requirements.txt文件中,就像这样:

Django==1.11.2
requests>=2.11.0
simplejson
ordereddict

其中既有指定版本号的,也有忽略版本号的。然后你就可以用:pip install -r requirements.txt来安装所有依赖。requirements.txt本质上是一个纯文本文件,但它还支持其他特性,比如包含另一个requirements.txt,这就使得模块化成为可能:

-r web.txt
-r secure.txt
simplejson
ordereddict

有了内部 PyPI 镜像,安装依赖将会变得非常简单。那么问题来了:如果我想要升级依赖的版本呢?

对于忽略版本号的依赖,当然没问题,全新安装时,自动会选择当前最新版本,但对于指定了版本号的,则需要手动更新这些版本号,然后重新安装。

使用虚拟环境

现在升级好了,一运行,你发现其他服务挂了,这是因为其他服务可能不兼容新版的依赖。这非常有可能发生,A 应用依赖 v1.0,B 应用依赖 v2.0,你一升级,A 就用不了了。如果把 A 应用和 B 应用环境独立,装两份不同版本的依赖,不就没问题了?没错,要达成这一目的,你可以装两份 Python,然后分别使用 Python 下的 pip 安装,就会安装到不同路径,运行应用时,指定不同的 Python 路径就可以了。但这样未免太过烦琐,于是 virtualenv 大展身手的时机来了。

Virtulenv 会使用当前的 Python 解释器创建出一个虚拟环境,并把 Python 解释器拷贝一份到环境中,这个拷贝,比起编译安装一个新的会省不少资源。使用时,需要事先激活这个虚拟环境,把当前的 Python 指到这个环境中的 Python:

创建虚拟环境

$ virtualenv venv
...
$ cd venv

激活环境

$ source venv/bin/activate
(venv)$

后续的 pip 安装、启动应用,只要在这个虚拟环境中运行即可。也可以不激活,通过绝对路径使用它:

$ /home/frostming/myproject/venv/bin/python server.py

Pipenv: pip + virtualenv

有了虚拟环境,依赖冲突的问题解决了,但还有一个问题仍未解决:更新版本号,如果你想更新依赖包,对于那些在requirements.txt中指定了版本号的依赖,你得逐个检查是否有新版,然后更新。既然如此麻烦,那是不是全都忽略版本号就好了?非也,这会产生新的问题。你在开发机上验证完毕了,部署到生产机上,或者别的小伙伴喜欢这个应用,想在自己的机器上跑。这时使用无版本号的requirements.txt安装依赖,很可能安装的版本和你开发时不一样,结果导致应用不可用。

但仔细分析,requirements.txt中是否指定版本号,解决的是两个维度的问题:

  • 无版本号是为了方便你更新依赖时自动拉取最新版本。(A 型)
  • 有版本号是为了部署和开发时的环境完全一致。(B 型)

requirements.txt只有一份,手动维护两份requirements成本又过高。于是 Pipenv 就应运而生,它可以从 A 型的requirements.txt(Pipenv 使用了一种新的格式 Pipfile)生成 B 型的文件,称为 Pipfile.lock,锁定当前所有依赖的版本。部署时,从 Pipfile.lock 安装,这些理念,是从其他语言的包管理工具借鉴过来的。

除此之外,Pipenv 还会帮你管理虚拟环境,不用自己创建。

Pipenv 的一些主要的使用方法:

  1. pipenv --two/--three:使用 Python 2 或 Python 3 创建一个虚拟环境并新建 Pipfile,它会探测系统中安装的所有 Python 并自动选择对应的 Python 版本。
  2. pipenv install:从当前的 Pipfile 安装所有依赖
  3. pipenv install --deploy:从 Pipfile.lock 安装所有依赖,部署用
  4. pipenv lock:从当前的 Pipfile 生成 Pipfile.lock
  5. pipenv install <package_name>:安装新的依赖包、添加到 Pipfile 中,并 lock
  6. pipenv update:使用最新可用版本更新 Pipfile.lock 并安装
  7. pipenv shell:激活虚拟环境的 shell
  8. pipenv run <command>:在不激活虚拟环境时运行虚拟环境中的命令

其他用法参考文档:https://docs.pipenv.org/

Share: