使用pipenv管理你的项目
/ / / 阅读数:4252前言
刚才使用 pipenv 发现了一个 bug, 顺手提了个的 PR。无聊之下翻了下贡献者列表,貌似没有一个我国的开发者!我的普及工作任重而道远啊,我写篇文章给大家介绍下这个终极大杀器。
Python 开发者应该听过 pip、easy_install 和 virtualenv,如果看过我的书应该还知道 virtualenvwrapper、virtualenv-burrito 和 autoenv,再加上 pyvenv、venv(Python 3 标准库)、pyenv...
额,是不是有种发懵的感觉?
那么现在有个好消息,你可以只使用终极方案: pipenv + autoenv(可选)。
「终极方案」,听起来好噱头呀。给出我的理由之前我们先了解一下 Python 虚拟环境和包管理的历史吧。
历史
在 Python 发展史上出现了很多创建和发布包的工具。 当你想要把你的项目分享出去,放到 PYPI 或者其他托管服务上的时候,就需要借助这样的工具来构建和分发项目。早在 1998 年 Python 标准库内置了模块 distutils,但是只提供了有限的支持,之后社区选择通过 setuptools 这个包实现构建和发布,它自带 easy_install,能帮助你找到、下载、安装以及更新需要使用的包。不过依然功能很有限,比如不能删除包。
当你做一个专职的 Python 开发,独立的虚拟环境也是一个开发中迫切需要的功能,在这里请大家记住一个 Ian Bicking(下称 ianb) 的开发者,08 年,他开发了 virtualenv。
社区一些 Python 核心开发者和知名项目(如 Django)核心开发者也在支持和推动这件事,后来成立了 pypa (Python Packaging Authority),pypa 早期做的就是 pip - 现在最主流的安装包的工具。再提一下,ianb 也是 pip 的早期核心开发者。
不过非常遗憾,由于和社区产生了一些矛盾,ianb 很早之前就不再写 Python 项目(可见这矛盾多大呀 😮),virtualenv 也转给了其他开发者,这是 Python 社区一个极大的损失。
现在 pip 和 virtualenv 已经被大家所熟知,甚至可以说是 Python 官方的包管理和虚拟环境选择。不过其实还是有问题,我举几个例子:
- 必须手动安装或删除某些特定版本的包,并记得定期更新 requirements.txt 文件,以保持项目环境的一致
- 有时项目中需要有多个 requirements.txt 文件,比如开发时应该用 dev-requirements.txt,现有的模式不能满足这些复杂的需要
- 卸载包的时候只是卸载包自己,不能处理相关依赖,时间久了项目环境就混乱了
pipenv 是什么?
Pipenv 是 Python 项目的依赖管理器。其实它不是什么先进的理念和技术,如果你熟悉 Node.js 的 npm/yarn 或 Ruby 的 bundler,那么就非常好理解了,它在思路上与这些工具类似。尽管 pip 可以安装 Python 包,但仍推荐使用 Pipenv,因为它是一种更高级的工具,可简化依赖关系管理的常见使用情况。
主要特性包含:
- 根据 Pipfile 自动寻找项目根目录。
- 如果不存在,可以自动生成 Pipfile 和 Pipfile.lock。
- 自动在项目目录的 .venv 目录创建虚拟环境。(当然这个目录地址通过设置 WORKON_HOME 改变)
- 自动管理 Pipfile 新安装和删除的包。
- 自动更新 pip。
对于新手和老手来说都是更好的选择。
pipenv 都包含什么?
pipenv 是 Pipfile 主要倡导者、requests 作者 Kenneth Reitz 写的一个命令行工具,主要包含了 Pipfile、pip、click、requests 和 virtualenv。Pipfile 和 pipenv 本来都是 Kenneth Reitz 的个人项目,后来贡献给了 pypa 组织。Pipfile 是社区拟定的依赖管理文件,用于替代过于简陋的 requirements.txt 文件。
Pipfile 的基本理念是:
- Pipfile 文件是 TOML 格式而不是 requirements.txt 这样的纯文本。
- 一个项目对应一个 Pipfile,支持开发环境与正式环境区分。默认提供 default 和 development 区分。
- 提供版本锁支持,存为 Pipfile.lock。
click 是 Flask 作者 Armin Ronacher 写的命令行库,现在 Flask 已经集成了它。
接下来,我们看看怎么使用它吧
入门
pipenv 兼容 Python 2/3,我们这里以 Mac 下 Python 3 为例:
安装 pipenv
❯ brew install python3 # 如果已经安装了可以忽略 ❯ python3 -m pip install --upgrade --force-reinstall pip ❯ pip3 install pipenv --user # 推荐安装在个人目录下 ❯ export PATH="/Users/dongweiming/Library/Python/3.6/bin:$PATH" # 把用户目录下bin放在最前面,这样可以直接使用pipenv了 |
使用 pipenv
用一个空目录体验一下:
❯ mkdir test_pipenv ❯ cd test_pipenv ❯ pipenv install # 创建一个虚拟环境 Creating a virtualenv for this project… ... Installing setuptools, pip, wheel...done. Virtualenv location: /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5 Creating a Pipfile for this project… Pipfile.lock not found, creating… Locking [dev-packages] dependencies… Locking [packages] dependencies… Updated Pipfile.lock (c23e27)! Installing dependencies from Pipfile.lock (c23e27)… 🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00 To activate this project's virtualenv, run the following: $ pipenv shell ❯ which python3 /usr/local/Cellar/python3/3.6.1/bin/python3 # 还是mac自带的Python ❯ pipenv shell # 激活虚拟环境 Spawning environment shell (/bin/zsh). Use 'exit' to leave. source /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/activate ❯ which python3 # 已经在虚拟环境里了 /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/python3 ❯ exit # 退出虚拟环境 ❯ which python3 /usr/local/Cellar/python3/3.6.1/bin/python3 |
以上就是原来 virtualenv 的基本用法了。我们看一下当前目录现在是什么样子的:
❯ ls Pipfile Pipfile.lock |
这个环境下目前什么都没有。我们安装 2 个包:
❯ pipenv install elasticsearch-dsl requests Installing elasticsearch-dsl… ... Adding elasticsearch-dsl to Pipfile's [packages]… Installing requests… ... Adding requests to Pipfile's [packages]… PS: You have excellent taste! ✨ 🍰 ✨ Locking [dev-packages] dependencies… Locking [packages] dependencies… Updated Pipfile.lock (8d307d)! |
现在 Pipfile.lock 已经更新了,包含了 elasticsearch-dsl、requests 和相关依赖的包信息。
另外如果你添加 --two 或 --three 标志到上面的最后一个命令,它分别使用 Python 2 或 3 来初始化你的项目。 否则将使用默认版本的 Python。
可以看一下依赖关系:
❯ pipenv graph elasticsearch-dsl==6.1.0 - elasticsearch [required: <7.0.0,>=6.0.0, installed: 6.1.1] - urllib3 [required: <1.23,>=1.21.1, installed: 1.22] - ipaddress [required: Any, installed: 1.0.19] - python-dateutil [required: Any, installed: 2.6.1] - six [required: >=1.5, installed: 1.10.0] - six [required: Any, installed: 1.10.0] requests==2.18.4 - certifi [required: >=2017.4.17, installed: 2017.11.5] - chardet [required: <3.1.0,>=3.0.2, installed: 3.0.4] - idna [required: <2.7,>=2.5, installed: 2.6] - urllib3 [required: <1.23,>=1.21.1, installed: 1.22] |
可以看到,他俩都依赖了 urllib3。虽然现在 pipenv 不能直接卸载包及其依赖,但是由于它提供了良好的接口,我们还是可以实现:
❯ pipenv uninstall `pipenv graph --json |python3 depends.py requests` Un-installing certifi… Uninstalling certifi-2017.11.5: Successfully uninstalled certifi-2017.11.5 No package certifi to remove from Pipfile. Un-installing requests… Uninstalling requests-2.18.4: Successfully uninstalled requests-2.18.4 Removing requests from Pipfile… Un-installing idna… Uninstalling idna-2.6: Successfully uninstalled idna-2.6 No package idna to remove from Pipfile. Un-installing chardet… Uninstalling chardet-3.0.4: Successfully uninstalled chardet-3.0.4 No package chardet to remove from Pipfile. Locking [dev-packages] dependencies… Locking [packages] dependencies… Updated Pipfile.lock (c05ac4)! |
其中 depends.py 脚本会解析依赖关系,排除其他包依赖的项目然后删除:
❯ cat depends.py import sys import json package = sys.argv[1] other_dependencies = set() removing_dependencies = set([package]) for i in json.load(sys.stdin): for p in i['dependencies']: key = p['key'] if i['package']['key'] == package: removing_dependencies.add(key) else: other_dependencies.add(key) print(' '.join(removing_dependencies - other_dependencies)) |
再看一下现在环境中的包依赖关系:
❯ pipenv graph elasticsearch-dsl==6.1.0 - elasticsearch [required: >=6.0.0,<7.0.0, installed: 6.1.1] - urllib3 [required: >=1.21.1,<1.23, installed: 1.22] - ipaddress [required: Any, installed: 1.0.19] - python-dateutil [required: Any, installed: 2.6.1] - six [required: >=1.5, installed: 1.10.0] - six [required: Any, installed: 1.10.0] |
是不是很干净呢?
其他功能
除了上述基本功能以外,pipenv 还有很多附加的功能,我举几个日常比较常用的例子:
❯ pipenv run which python # 「pipenv run」可以激活虚拟环境,并使用shell命令 /Users/dongweiming/.virtualenvs/test_pipenv-GP_s2TW5/bin/python ❯ pipenv check # 检查装的包的安全性 Checking PEP 508 requirements… Passed! Checking installed package safety… All good! ❯ pipenv --man # 传统的看文档的方法 ❯ pipenv check --style depends.py # 代码Flake8检查,在这里我修改了depends.py让它故意有问题 /Users/dongweiming/test_pipenv/depends.py:1:1: F401 'os' imported but unused |
另外由于 autoenv 也是 Kenneth Reitz 写的,所以 pipenv 默认也包含了对.env 文件的支持。
是不是方便很多呢?
pipenv 发现了一个 bug, 顺手提了个的 PR。无聊之下翻了下贡献者列表,貌似没有一个我国的开发者 腾讯明希不是吗?,不过明希说也是被人带入了 pipenv 的坑