用过Python的开发者应该都了解virtualenv和pip这俩个工具,不过随着项目越来越多,要么虚拟环境中第三方包越来越多,依赖越来越复杂,有时候还会冲突,要么就为每个项目都创建1个虚拟环境,这样并不是不推荐的,只是环境来来回回的切换比较麻烦也容易出错,__pipenv__就是为了解决这些问题诞生的
Python这一类的工具还有不少,pyenv、poetry等,这里不考虑,这里从实际出发,假如新起了一个flask项目,这里一步一步的尝试下pipenv的功能
入门
pipenv版本: 2020.11.4
安装pipenv
➜ ~ pip install pipenv
创建项目目录和虚拟环境
➜ ~ mkdir flaskr
➜ ~ cd flaskr
➜ ~/flaskr pipenv --three
Creating a virtualenv for this project...
...
✔ Successfully created virtual environment!
Virtualenv location: ~/.local/share/virtualenvs/flaskr-o1eL91q6
pipenv --three表示用创建Python3的虚拟环境,如果想用Python2的话,pipenv --two即可,然后会在~/.local/share/virtualenvs/flaskr-o1eL91q6(Mac)创建个虚拟环境
创建好虚拟环境之后,项目目录下多了两个文件Pipfile和Pipfile.lock,这两个文件是pipenv包管理的核心,先安装个flask看这两个文件会有什么变化(安装/更新/删除包都不需要进入虚拟环境,除非要执行脚本,才需要pipenv shell进入虚拟环境来指定Python解释器版本,命令行开头会有虚拟环境的名称,这里是(flaskr))
安装包
(flaskr) ➜ ~/flaskr pipenv install <packages>
(flaskr) ➜ ~/flaskr pipenv install flask
Installing flask...
...
✔ Success!
Installing dependencies from Pipfile.lock (662286)...
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
(flaskr) ➜ ~/flaskr cat Pipfile
[[source]]
# pypi镜像,可以替换成清华或者阿里云的源都行,提速的最有效的方法
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
# 这里是安装的包的信息,类似requirements.txt
flask = "*"
[dev-packages]
# 这里是【开发环境】安装的包的信息,类似requirements-dev.txt
[requires]
python_version = "3.7"
Pipfile.lock则是把这些包的版本锁住,也记录了包的哈希值为了校验,告诉pipenv,这些包是哪个版本的Pipfile是requirements.txt的开发环境和线上环境的集成版,开发和线上区分开这是合理的,因为我们在开发环境中的一些包是不需要部署到线上的,比如测试相关的一些包,Pipfile和requirements.txt之间来回切换也是比较方便的
pipenv和requirements.txt/requirements-dev.txt之间切换
# [packages]下的包生成requirements.txt
(flaskr) ➜ ~/flaskr pipenv lock -r > requirements.txt
# [dev-packages]下的包生成requirements-dev.txt
(flaskr) ➜ ~/flaskr pipenv lock -r --dev-only > requirements-dev.txt
# 从requirements.txt安装包到[packages]
(flaskr) ➜ ~/flaskr pipenv install -r requirements.txt
# 从requirements-dev.txt安装包到[packages]
(flaskr) ➜ ~/flaskr pipenv install --dev -r requirements-dev.txt
打印包的相互依赖的关系
(flaskr) ➜ ~/flaskr pipenv graph
Flask-Script==2.0.6
- Flask [required: Any, installed: 1.1.2]
- click [required: >=5.1, installed: 7.1.2]
- itsdangerous [required: >=0.24, installed: 1.1.0]
- Jinja2 [required: >=2.10.1, installed: 2.11.2]
- MarkupSafe [required: >=0.23, installed: 1.1.1]
- Werkzeug [required: >=0.15, installed: 1.0.1]
python-dateutil==2.8.1
- six [required: >=1.5, installed: 1.15.0]
删除包和虚拟环境
# 删除指定的包
(flaskr) ➜ ~/flaskr pipenv uninstall flask
# 删除虚拟环境
(flaskr) ➜ ~/flaskr pipenv --rm
更新包
(flaskr) ➜ ~/flaskr pipenv update python-dateutil
...
✔ Success!
Installing dependencies from Pipfile.lock (187b7b)...
🐍 ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 1/1 — 00:00:00
All dependencies are now up-to-date!
All dependencies are now up-to-date!这句话看着就离谱
放弃
-
删除包的时候发现只能删除指定的内个包,比如我想删除上面的flask包,实际上并没有删除掉相关的依赖,是因为pipenv本质上用的还是pip,只是做了一层封装,
pipenv graph其实没什么意义,就是美观了一些,这个问题其实还好,不影响使用,可是下面发生的事情就有点离谱了 -
用
pipenv update <packages>更新包,假如我们在以前安装了三个包的版本分别是
flask: 1.0 (最新的是1.1.2)
flask-script: 0.6.2 (最新的是2.0.6)
python-dateutil: 2.0 (最新的是2.8.1)
此时的Pipfile是这样的
[packages]
flask = "*"
flask-script = "*"
python-dateutil = "*"
但是Pipfile.lock中是记录了flask: 1.0, flask-script: 0.6.2, python-dateutil: 2.0的版本信息的,那么此时我想要更新flask到最新,一般来说应该是只更新flask到最新版本,但是执行了pipenv update flask之后,会发现所有的包都更新了,连python-dateutil这个完全不相关的包都更新到了最新版

后来查阅文档发现update是有--selective-upgrade参数的,就是只更新指定的包
(flaskr) ➜ ~/flaskr pipenv update --selective-upgrade flask
...
✔ Success!
All dependencies are now up-to-date!
All dependencies are now up-to-date!,又是把所有的包,包括python-dateutil都更新到了最新版本,然后再查阅文档发现了这个参数--keep-outdated
(flaskr) ➜ ~/flaskr pipenv update --selective-upgrade --keep-outdated flask
...
✔ Success!
All dependencies are now up-to-date!
All dependencies are now up-to-date!,提示还是把所有的包都更新了,但是实际上__并·没·有__做任何操作,--keep-outdated参数是生效了,但是把我要更新的包flask也给忽略了
折腾了1小时之后,发现了个曲线救国的方法,就是在Pipfile中不要保持<package> = "*",而是要指定版本<packages> = "==版本号",但是这样一来pipenv install flask/pipenv update flask又无法更新了,因为pipenv认为Pipfile.lock文件中已经锁住了版本,最终的办法就是弃用update命令,用install而且只能以__指定版本号__的方式pipenv install "flask>=1.1"才会生效,Pipfile和Pipfile.lock中的信息同时会被改写
[packages]
# 这样你不管更新谁,加什么参数,要么是谁都不更新,要么是所有都更新,总之就是一把梭
flask = "*"
[packages]
# 这样可以保证依赖的包版本不被破坏,但是升级的话就需要重新安装,且指定版本
flask = "==1.0"
那这样和requirements.txt又有什么区别呢?真的只是封装了一下api啊?!
总结
pipenv集成了virtualenv和pip,便利性得到的一定的提升,用户不需要关心虚拟环境的创建,在离线开发阶段是比较好用的,可以快速迭代,但是不推荐在线上应用中使用,第三方包的管理做的虽然逻辑混乱了一些,但是如果能主动地避免一些坑的话,仍然不失为一款比较好的工具,也就是我们无法100%依赖pipenv来进行包管理,仍然要采取对待requirements.txt的特别谨慎的态度来对待,而且现在官方也推荐使用,PyCharm也集成了pipenv环境,祝越来越好吧
顺便说一句,个人觉得Pipenv的slogan换成“Python Dev Workflow for Some Humans”更准确些(至少目前),有些逻辑实在是搞不懂为啥要这么设计
参考
😝