前言
我们在运行 Python 项目的时候经常会遇到一些版本问题,例如 A 项目依赖于 Django 1.5,而 B 项目又依赖 Django 2.0,而我们的系统却只有一个 Python 解释器,我们所有的包都被装在了 Python 安装目录的 site-packages 目录下,所以 Django 只能是某个特定的版本,所以这样就会导致运行的时候导致 A 或 B 项目出现兼容问题。为了解决这个问题,我们可能会使用 virtualenv 来为项目创建一套独立的 Python 运行环境,或者我们可能会使用 Docker 容器来实现不同项目的隔离运行,但总的来说,它们使用起来其实并没有那么方便。另外在进行 Python 包管理时,requirements.txt 这样的包依赖标识文件也显得很鸡肋,在某些情况下可能会带来一些麻烦。为了解决这些问题,一个更加使用方便的包管理工具诞生了,叫做 Pipenv,接下来就让我们一起来了解一下它的用法。
简介
Pipenv,它的项目简介为 Python Development Workflow for Humans,是 Python 著名的 requests 库作者 kennethreitz 写的一个包管理工具,它可以为我们的项目自动创建和管理虚拟环境并非常方便地管理 Python 包,现在它也已经是 Python 官方推荐的包管理工具。 Pipenv 我们可以简单理解为 pip 和 virtualenv 的集合体,它可以为我们的项目自动创建和管理一个虚拟环境。virtualenv 在使用时我们需要手动创建一个虚拟环境然后激活,Pipenv 会自动创建。另外我们之前可能使用 requirements.txt 文件来标识项目所需要的依赖,但是这样会带来一些问题,如有的 requirements.txt 中只是将库名列出来了,没有严格指定版本号,这样就可能会导致不同时间安装的库版本是不同的,如 requirements.txt 文件中对 Django 的依赖只写了一个 django,可能在 2016 年的时候运行安装会安装 Django 的 1.x 版本,到了 2017 年就会安装 Django 的 2.x 版本,所以可能导致一些麻烦。为了解决这个问题,Pipenv 直接弃用了 requirements.txt,会同时它会使用一个叫做 Pipfile 和 Pipfile.lock 的文件来管理项目所需的依赖包,而不再是简单地使用 requirements.txt 文件来记录项目所需要的依赖。 总的来说,Pipenv 可以解决如下问题:
- 我们不需要再手动创建虚拟环境,Pipenv 会自动为我们创建,它会在某个特定的位置创建一个 virtualenv 环境,然后调用 pipenv shell 命令切换到虚拟环境。
- 使用 requirements.txt 可能会导致一些问题,所以 Pipenv 使用 Pipfile 和 Pipfile.lock 来替代之,而且 Pipfile 如果不存在的话会自动创建,而且在安装、升级、移除依赖包的时候会自动更新 Pipfile 和 Pipfile.lock 文件。
- 广泛使用 Hash 校验,保证安全性。
- 可以更清晰地查看 Python 包及其关系,调用 pipenv graph 即可呈现,结果简单明了。
- 可通过自动加载 .env 读取环境变量,简化开发流程。
安装
本文内容基于 Python 3.6 说明,默认的 Python 解释器命令为 python3,包管理工具命令为 pip3。 Pipenv 是基于 Python 开发的包,所以可以直接用 pip 来安装,命令如下:
1 |
pip3 install pipenv |
另外还有多种安装方式,如 Pipsi、Nix、Homebrew,安装方式可以参考:http://pipenv.readthedocs.io/en/latest/#install-pipenv-today。
基本使用
首先我们可以新建一个项目,例如叫做 PipenvTest,然后新建一个 Python 脚本,例如叫 main.py,内容为:
1 |
import django |
直接用系统的 Python3 运行此脚本:
1 |
python3 main.py |
结果如下:
1 |
1.11 |
我们可以看到系统安装的 Django 版本是 1.11。但是我们想要本项目基于 Django 2.x 开发,当然我们可以选择将系统的 Django 版本升级,但这样又可能会影响其他的项目的运行,所以这并不是一个好的选择。为了不影响系统环境的 Django 版本,所以我们可以用 Pipenv 来创建一个虚拟环境。 在该目录下,输入 pipenv 命令即可查看命令的完整用法:
1 |
Usage: pipenv [OPTIONS] COMMAND [ARGS]... |
接下来我们首先验证一下当前的项目是没有创建虚拟环境的,调用如下命令:
1 |
pipenv --venv |
结果如下:
1 |
No virtualenv has been created for this project yet! |
这说明当前的项目尚未创建虚拟环境,接下来我们利用 Pipenv 来创建一个虚拟环境:
1 |
pipenv --three |
或
1 |
pipenv --python 3.6 |
都可以创建一个 Python3 的虚拟环境,—three 代表创建一个 Python3 版本的虚拟环境,—python 则可以指定特定的 Python 版本,当然 —two 则创建一个 Python2 版本的虚拟环境,但前提你的系统必须装有该版本的 Python 才可以。 执行完毕之后,样例输出如下:
1 |
Warning: the environment variable LANG is not set! |
这里显示 Pipenv 利用 /usr/local/bin/python3 作为 virtualenv 的解释器,然后在 /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin 目录下创建了一个新的 Python3 解释器,同时还创建了两个可执行文件别名 python3.6 和 python,另外我们还可以发现目录下多了一个 Pipfile 文件,这时虚拟环境就创建完成了。 我们切换到 PipenvTest-VSTVh89E/bin 目录查看一下文件结构,可以看到这里面包含了 pip、pip3、pip3.6、python、python3、python3.6 等可执行文件,实际上目录结构和使用 virtualenv 时是完全一样的,只不过文件夹的位置不同而已。 接下来我们可以切换到该虚拟环境下执行命令,执行如下命令即可:
1 |
pipenv shell |
执行完毕之后样例输出如下:
1 |
Spawning environment shell (/bin/zsh). Use 'exit' to leave. |
实际上这也和 virtualenv 激活的流程一样,也是调用了类似 source venv/bin/activate 方法将这个路径加到全局环境变量最前面,这样就会优先调用该路径下的 python、python3、python3.6 可执行文件了。 这时候我们会发现命令行的样子就变了,前面多了一个 (PipenvTest-VSTVh89E) 的标识,代表当前我们已经切换到了虚拟环境下。 这时我们用 which 或 where 命令查看一下 Python 可执行文件的路径,命令如下:
1 |
(PipenvTest-VSTVh89E) CQC-MAC% which python3 |
可以发现当前的 Python 可执行路径都被切换到了 PipenvTest-VSTVh89E/bin 目录下,调用的是虚拟环境中的 Python 解释器,这时我们重新执行刚才的脚本,命令如下:
1 |
(PipenvTest-VSTVh89E) CQC-MAC% python3 main.py |
这时我们可以发现报了如下错误:
1 |
Traceback (most recent call last): |
这其实是因为新的虚拟环境没有安装任何的 Python 第三方包,实际上如果直接使用 virtualenv 时也是这样的结果。这是因为新的虚拟环境是一个全新的 Python 环境,它默认只包含了 Python 内置的包以及 pip、wheel、setuptools 包,其他的第三方包都没有安装。 这时我们可以使用 Pipenv 来安装 django 包,命令如下:
1 |
pipenv install django |
运行后输出结果如下:
1 |
Installing django… |
如果有这样的输出结果就代表成功安装了 Django,可以看到此时安装的 Django 版本为 2.0,代表我们的虚拟环境成功安装了 Django 2.0 版本。 同时我们还注意到它输出了一句话叫做 Updated Pipfile.lock,这时我们可以发现项目路径下又生成了一个 Pipfile.lock 文件,内容如下:
1 |
{ |
可以看到里面标识了 Python 环境基本信息,以及依赖包的版本及 hashes 值。 另外我们还可以注意到 Pipfile 文件内容也有更新,[packages] 部分多了一句 django = ““,标识了本项目依赖于 Django,这个其实类似于 requirements.txt 文件。 那么到这里有小伙伴可能就会问了, Pipfile 和 Pipfile.lock 有什么用呢? Pipfile 其实一个 TOML 格式的文件,标识了该项目依赖包的基本信息,还区分了生产环境和开发环境的包标识,作用上类似 requirements.txt 文件,但是功能更为强大。Pipfile.lock 详细标识了该项目的安装的包的精确版本信息、最新可用版本信息和当前库文件的 hash 值,顾明思义,它起了版本锁的作用,可以注意到当前 Pipfile.lock 文件中的 Django 版本标识为 ==2.0.2,意思是当前我们开发时使用的就是 2.0.2 版本,它可以起到版本锁定的功能。 举个例子,刚才我们安装了 Django 2.0.2 的版本,即目前(2018.2.27)的最新版本。但可能 Django 以后还会有更新,比如某一天 Django 更新到了 2.1 版本,这时如果我们想要重新部署本项目到另一台机器上,假如此时不存在 Pipfile.lock 文件,只存在 Pipfile文件,由于 Pipfile 文件中标识的 Django 依赖为 django = ““,即没有版本限制,它会默认安装最新版本的 Django,即 2.1,但由于 Pipfile.lock 文件的存在,它会根据 Pipfile.lock 来安装,还是会安装 Django 2.0.2,这样就会避免一些库版本更新导致不兼容的问题。 请记住:任何情况下都不要手动修改 Pipfile.lock 文件! 好,接下来我们再回归正题,现在已经安装好了 Django 了,那么我们重新运行此脚本便可以成功输出 Django 版本信息了:
1 |
(PipenvTest-VSTVh89E) CQC-MAC% python3 main.py |
结果如下:
1 |
2.0.2 |
这样我们就成功安装了 Django 2.x 了,和系统的 Django 1.11 没有任何冲突。 在此模式的命令行下,我们就可以使用虚拟环境下的 Python 解释器,而且所安装的依赖包对外部系统没有任何影响,而且使用 Pipfile 和 Pipfile.lock 来管理项目的依赖更加方便和健壮。 如果想要退出虚拟环境,只需要输入 exit 命令即可:
1 |
(PipenvTest-VSTVh89E) CQC-MAC% exit |
输入退出命令之后,我们重新再运行此脚本,就会重新使用系统的 Python 解释器,Django 版本又重新回到了 1.11。 由此可以看来,有了 Pipenv,我们可以使用 Pipfile 和 Pipfile.lock 来方便地管理和维护项目的依赖包,而且可以实现虚拟环境运行,避免了包冲突问题,可谓一举两得。
常用命令
上文我们介绍了 Pipenv 的基本操作,下面我们再介绍一下它的一些常用命令。
虚拟环境路径
我们可以使用 —venv 参数来获得虚拟环境路径:
1 |
pipenv --venv |
样例输出如下:
1 |
/Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E |
可见这个路径是一个标准的路径,Pipenv 会把虚拟环境统一放到 virtualenvs 文件夹下,而不是本项目路径下。
Python 解释器路径
要获取虚拟环境 Python 解释器路径,可以使用 —py 参数:
1 |
pipenv --py |
样例输出如下:
1 |
/Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python |
加载系统 Python 包
默认情况下,新创建的虚拟环境是不包含任何第三方包的,但我们也可以开启加载系统 Python 包功能,使用 —site-packages 即可:
1 |
pipenv --site-packages |
这样创建的虚拟环境便可以使用系统已安装的 Python 包了。
开启虚拟环境
要开启虚拟环境只需要执行如下命令:
1 |
pipenv shell |
这样就可以进入虚拟环境,此时运行的 python、python3 命令都是虚拟环境下的。
安装 Python 包
安装 Python 包我们不再需要 pip 来安装,直接使用 Pipenv 也可安装,如安装 requests,命令如下:
1 |
pipenv install requests |
安装完成之后会同时更新项目目录下的 Pipfile 和 Pipfile.lock 文件。 有时候一些 Python 包是仅仅开发环境需要的,如 pytest,这时候我们通过添加 —dev 参数即可,命令如下:
1 |
pipenv install pytest --dev |
这时候,pytest 的依赖便会记录在 Pipfile 的 [dev-packages] 区域:
1 |
[dev-packages] |
获取包依赖
我们可以使用命令来清晰地呈现出当前安装的 Python 包版本及之间的依赖关系,命令如下:
1 |
pipenv graph |
样例结果如下:
1 |
Django==2.0.2 |
可以看到结果非常清晰,Django 当前安装了 2.0.2版本,依赖于 pytz 任何版本,已经安装了 2018.3 版本;pytest 已经安装了 3.4.1 版本,依赖 attrs>=17.2.0 版本,已经安装了 17.4.0 版本,另外还依赖 pluggy、py、setuptools、six 这些库。总之包的依赖关系一目了然。
卸载 Python 包
卸载 Python 包也非常简单,如卸载 requests 包,命令如下:
1 |
pipenv uninstall requests |
卸载完成之后,Pipfile 和 Pipfile.lock 文件同样会更新。 如果要卸载全部 Python 包,可以添加 —all 参数:
1 |
pipenv uninstall --all |
产生 Pipfile.lock
有时候可能 Pipfile.lock 文件不存在或被删除了,这时候我们可以使用如下命令生成:
1 |
pipenv lock |
以上便是一些常用的 Pipenv 命令,如果要查看更多用法可以参考其官方文档:https://docs.pipenv.org/#pipenv-usage。
结语
本文介绍了 Pipenv 的基本用法,作为 pip 和 virtualenv 的结合体,我们可以利用它更方便地创建和管理 Python 虚拟环境,还可以用更加科学的方式管理 Python 包,一举两得。 嗯,是时候抛弃 virtualenv 和 pip 了!