0%

Requests库作者另一神器Pipenv的用法

前言

我们在运行 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
2
import django
print(django.get_version())

直接用系统的 Python3 运行此脚本:

1
python3 main.py

结果如下:

1
1.11

我们可以看到系统安装的 Django 版本是 1.11。但是我们想要本项目基于 Django 2.x 开发,当然我们可以选择将系统的 Django 版本升级,但这样又可能会影响其他的项目的运行,所以这并不是一个好的选择。为了不影响系统环境的 Django 版本,所以我们可以用 Pipenv 来创建一个虚拟环境。 在该目录下,输入 pipenv 命令即可查看命令的完整用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Usage: pipenv [OPTIONS] COMMAND [ARGS]...

Options:
--update Update Pipenv & pip to latest.
--where Output project home information.
--venv Output virtualenv information.
--py Output Python interpreter information.
--envs Output Environment Variable options.
--rm Remove the virtualenv.
--bare Minimal output.
--completion Output completion (to be eval'd).
--man Display manpage.
--three / --two Use Python 3/2 when creating virtualenv.
--python TEXT Specify which version of Python virtualenv should use.
--site-packages Enable site-packages for the virtualenv.
--jumbotron An easter egg, effectively.
--version Show the version and exit.
-h, --help Show this message and exit.


Usage Examples:
Create a new project using Python 3.6, specifically:
$ pipenv --python 3.6

Install all dependencies for a project (including dev):
$ pipenv install --dev

Create a lockfile containing pre-releases:
$ pipenv lock --pre

Show a graph of your installed dependencies:
$ pipenv graph

Check your installed dependencies for security vulnerabilities:
$ pipenv check

Install a local setup.py into your virtual environment/Pipfile:
$ pipenv install -e .

Commands:
check Checks for security vulnerabilities and against PEP 508 markers
provided in Pipfile.
graph Displays currently–installed dependency graph information.
install Installs provided packages and adds them to Pipfile, or (if none
is given), installs all packages.
lock Generates Pipfile.lock.
open View a given module in your editor.
run Spawns a command installed into the virtualenv.
shell Spawns a shell within the virtualenv.
uninstall Un-installs a provided package and removes it from Pipfile.
update Uninstalls all packages, and re-installs package(s) in [packages]
to latest compatible versions.

接下来我们首先验证一下当前的项目是没有创建虚拟环境的,调用如下命令:

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
2
3
4
5
6
7
8
9
10
Warning: the environment variable LANG is not set!
We recommend setting this in ~/.profile (or equivalent) for proper expected behavior.
Creating a virtualenv for this project
Using /usr/local/bin/python3 to create virtualenv…
⠋Running virtualenv with interpreter /usr/local/bin/python3
Using base prefix '/usr/local/Cellar/python3/3.6.1/Frameworks/Python.framework/Versions/3.6'
New python executable in /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3.6
Also creating executable in /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python
Installing setuptools, pip, wheel...done.
Virtualenv location: /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E

这里显示 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
2
3
4
Spawning environment shell (/bin/zsh). Use 'exit' to leave.
source /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/activate
CQC-MAC% source /Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/activate
(PipenvTest-VSTVh89E) CQC-MAC%

实际上这也和 virtualenv 激活的流程一样,也是调用了类似 source venv/bin/activate 方法将这个路径加到全局环境变量最前面,这样就会优先调用该路径下的 python、python3、python3.6 可执行文件了。 这时候我们会发现命令行的样子就变了,前面多了一个 (PipenvTest-VSTVh89E) 的标识,代表当前我们已经切换到了虚拟环境下。 这时我们用 which 或 where 命令查看一下 Python 可执行文件的路径,命令如下:

1
2
3
4
5
6
(PipenvTest-VSTVh89E) CQC-MAC% which python3
/Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3
(PipenvTest-VSTVh89E) CQC-MAC% which python3.6
/Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python3.6
(PipenvTest-VSTVh89E) CQC-MAC% which python
/Users/CQC/.local/share/virtualenvs/PipenvTest-VSTVh89E/bin/python

可以发现当前的 Python 可执行路径都被切换到了 PipenvTest-VSTVh89E/bin 目录下,调用的是虚拟环境中的 Python 解释器,这时我们重新执行刚才的脚本,命令如下:

1
(PipenvTest-VSTVh89E) CQC-MAC% python3 main.py

这时我们可以发现报了如下错误:

1
2
3
4
Traceback (most recent call last):
File "main.py", line 1, in <module>
import django
ModuleNotFoundError: No module named 'django'

这其实是因为新的虚拟环境没有安装任何的 Python 第三方包,实际上如果直接使用 virtualenv 时也是这样的结果。这是因为新的虚拟环境是一个全新的 Python 环境,它默认只包含了 Python 内置的包以及 pip、wheel、setuptools 包,其他的第三方包都没有安装。 这时我们可以使用 Pipenv 来安装 django 包,命令如下:

1
pipenv install django

运行后输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
Installing django…
Collecting django
Downloading Django-2.0.2-py3-none-any.whl (7.1MB)
Collecting pytz (from django)
Downloading pytz-2018.3-py2.py3-none-any.whl (509kB)
Installing collected packages: pytz, django
Successfully installed django-2.0.2 pytz-2018.3

Adding django to Pipfile's [packages]…
Locking [dev-packages] dependencies…
Locking [packages] dependencies…
Updated Pipfile.lock (e101fb)!

如果有这样的输出结果就代表成功安装了 Django,可以看到此时安装的 Django 版本为 2.0,代表我们的虚拟环境成功安装了 Django 2.0 版本。 同时我们还注意到它输出了一句话叫做 Updated Pipfile.lock,这时我们可以发现项目路径下又生成了一个 Pipfile.lock 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
"_meta": {
"hash": {
"sha256": "7b9623243d9c22b1f333ee710aff70d0cbcdf1dd7e0aac69230dc76855d27270"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "3.6.1",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "17.4.0",
"platform_system": "Darwin",
"platform_version": "Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64",
"python_full_version": "3.6.1",
"python_version": "3.6",
"sys_platform": "darwin"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"django": {
"hashes": [
"sha256:af18618ce3291be5092893d8522fe3919661bf3a1fb60e3858ae74865a4f07c2",
"sha256:9614851d4a7ff8cbd32b73c6076441f377c45a5bbff7e771798fb02c43c31f47"
],
"version": "==2.0.2"
},
"pytz": {
"hashes": [
"sha256:ed6509d9af298b7995d69a440e2822288f2eca1681b8cce37673dbb10091e5fe",
"sha256:f93ddcdd6342f94cea379c73cddb5724e0d6d0a1c91c9bdef364dc0368ba4fda",
"sha256:61242a9abc626379574a166dc0e96a66cd7c3b27fc10868003fa210be4bff1c9",
"sha256:ba18e6a243b3625513d85239b3e49055a2f0318466e0b8a92b8fb8ca7ccdf55f",
"sha256:07edfc3d4d2705a20a6e99d97f0c4b61c800b8232dc1c04d87e8554f130148dd",
"sha256:3a47ff71597f821cd84a162e71593004286e5be07a340fd462f0d33a760782b5",
"sha256:5bd55c744e6feaa4d599a6cbd8228b4f8f9ba96de2c38d56f08e534b3c9edf0d",
"sha256:887ab5e5b32e4d0c86efddd3d055c1f363cbaa583beb8da5e22d2fa2f64d51ef",
"sha256:410bcd1d6409026fbaa65d9ed33bf6dd8b1e94a499e32168acfc7b332e4095c0"
],
"version": "==2018.3"
}
},
"develop": {}
}

可以看到里面标识了 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
2
3
(PipenvTest-VSTVh89E) CQC-MAC% exit
➜ PipenvTest python3 main.py
1.11

输入退出命令之后,我们重新再运行此脚本,就会重新使用系统的 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
2
[dev-packages]
pytest = "*"

获取包依赖

我们可以使用命令来清晰地呈现出当前安装的 Python 包版本及之间的依赖关系,命令如下:

1
pipenv graph

样例结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
Django==2.0.2
- pytz [required: Any, installed: 2018.3]
pytest==3.4.1
- attrs [required: >=17.2.0, installed: 17.4.0]
- pluggy [required: <0.7,>=0.5, installed: 0.6.0]
- py [required: >=1.5.0, installed: 1.5.2]
- setuptools [required: Any, installed: 38.5.1]
- six [required: >=1.10.0, installed: 1.11.0]
requests==2.18.4
- certifi [required: >=2017.4.17, installed: 2018.1.18]
- chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
- idna [required: <2.7,>=2.5, installed: 2.6]
- urllib3 [required: <1.23,>=1.21.1, installed: 1.22]

可以看到结果非常清晰,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 了!