Pipenv 让我用的很痛苦,有一种被欺骗的感受,并且后悔在《Flask Web 开发实战》里采用它。python
大部分状况下,它很好用,但却存在太多问题,有一些问题让人简直无法接受。我知道有人会说「这是开源程序,有 bug 就本身去修」、「爱用不用,没人强迫你」,但问题是,一个进行大肆推广,甚至借 PyPA 作背书来宣传(常常让人误觉得是 Python 官方推荐)的工具却连基本的使用流程都没作好,这不是合理和正常的行为。 引用这个 HN 评论的话说就是:git
Kenneth Retiz 滥用他在 PyPA 的位置(并且快速把一个其实是 beta 状态的产品的版本号从 0 升到 18)来暗示 Pipenv 已经很是稳定,受到大力支持而且很是官方,但事实却并非这样。
在这篇(劝退)文章里,我会分别从包的安装、更新、卸载来测试并指出 Pipenv 的一些问题。github
假设想给这个旧项目 Bluelog 添加新功能,拿到旧项目的代码,打算安装一个 Flask-Avatars 包。查了文档,发现安装包要使用 pipenv install 命令:shell
(https://docs.pipenv.org/en/latest/basics/#pipenv-install)flask
因此执行了下面的命令:promise
$ pipenv install flask-avatars复制代码
结果发现其余全部的不相干依赖都被更新了……app
WTF,这不是反人类吗(说好的「Python Development Workflow for Humans.」呢)?我安装一个包,默认行为居然是更新其余全部不相干且已经锁定版本的依赖!ide
翻了文档才发现,要加一个 --keep-outdated 选项才能避免更新其余锁定的依赖:svg
$ pipenv install --help
...
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED]复制代码
好吧,那先忍着,多打一个命令行选项就是了: 工具
$ pipenv install --keep-outdated flask-avatars复制代码
WTF,为何全部依赖仍是被更新了?
好吧,有 bug 很正常,我来提个 issue 吧,哎,好像有不少 issue 了?
重点评论:
(https://github.com/pypa/pipenv/issues/1554#issuecomment-370850330)
Kenneth Reitz 先是说 lockfile 只要是过时了就老是会被从新生成(这是什么逻辑?),接着又说用 pipenv update depname,但其余人都回复不起做用(我下面会进行单独测试)。
接着,看到其余评论提到用 --selective-upgrade 选项:
$ pipenv install --help
...
--selective-upgrade Update specified packages.复制代码
我又继续使用 --selective-upgrade 选项:
$ pipenv install --selective-upgrade flask-avatars复制代码
仍然会更新全部依赖……
对了,顺便还测试了这个命令,依然没用:
$ pipenv install --keep-outdated --selective-upgrade flask-avatars复制代码
除了安装某个包会致使全部依赖版本被更新,Pipenv 在解决依赖的冲突上面也有一些不足,好比执行下面的安装命令(具体见 Poetry README):
$ pipenv install oslo.utils==1.4.0复制代码
会提示没法安装成功:
ERROR: ERROR: Could not find a version that matches pbr!=0.7,!=2.1.0,<1.0,>=0.6,>=2.0.0复制代码
假设我想更新 Bluelog 这个项目用的 Flask 版本(从 1.0.2 更新到最新的 1.1.1)。查了文档,找到了 update 命令,文档是这样写的:
(https://docs.pipenv.org/en/latest/basics/#example-pipenv-upgrade-workflow)
因而我执行下面的命令:
$ pipenv update flask
Locking [dev-packages] dependencies…
Success!
Locking [packages] dependencies…
Success!
Updated Pipfile.lock (fd55e3)!
Installing dependencies from Pipfile.lock (fd55e3)…
================================ 26/26 - 00:00:12
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
All dependencies are now up-to-date!复制代码
忽然看到最后一行赫然写着「All dependencies are now up-to-date!」,我觉得是搞错了,赶忙看了下 Pipfile.lock,WTF,为何我全部的依赖(包括和 Flask 彻底不相关的)又都被更新了?
依然,已经有不少相关 issue:
重点评论:
(https://github.com/pypa/pipenv/issues/966#issuecomment-346204439)
若是这个 issue 没有被锁定,这一句「I have no idea.」下面的图标不知道还会被点多少次。我猜 Kenneth Reitz 对这个 issue 让多少人头疼也没有 idea。
继续搜索,查文档,发现 update 命令也有 --keep-outdated 和 --selective-upgrade 两个选项:
$ pipenv update --help
...
--selective-upgrade Update specified packages.
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED]复制代码
先来试下 --keep-outdated:
$ pipenv update --keep-outdated flask复制代码
no luck,仍是更新了全部依赖。继续试一下 --selective-upgrade:
$ pipenv update --selective-upgrade flask复制代码
依然没用,仍然会更新全部依赖……
继续查 issue,发现下面这些:
在 #3461 里发现了下面这个评论:
(https://github.com/pypa/pipenv/issues/3461#issuecomment-455740272)
(由于 Frost Ming 是国内的同窗,也是核心维护者,说明一下,这里无心冒犯,引用这个评论只是想说明 Pipenv 如今的开发状态。)
这段评论的重点是「In fact, the package name passed as argument is not used at all.」。
也就是说,pipenv update 其实是不接受包名称参数的。这在下面这个评论也获得了印证:
Here is the important caveat:
pipenv update
alwaystargets every package in your lockfile, without exception. It does not accept arguments.
(https://github.com/pypa/pipenv/issues/3461#issuecomment-493847853)
一个还没实现的功能就写到文档里了?这真的不是开玩笑吗?不只是写到了文档里,还写到了命令行帮助文档里:
$ pipenv update --help
Usage: pipenv update [OPTIONS] [PACKAGES]...复制代码
相似下面的场景:
最终的结果就是,若是你想更新一个包,那就只能手动把更新版本的包版本和 hash 编辑到 Pipfile.lock 里。这么作实在是太蠢了。
假设我决定再也不使用 Gunicorn,须要卸载它,在文档里查到 pipenv uninstall 命令:
(https://docs.pipenv.org/en/latest/basics/#pipenv-uninstall)
因而执行下面的命令:
$ pipenv uninstall gunicorn复制代码
结果呢?为何我全部的依赖又都被更新了!?好,我已经习惯了。看命令行帮助文档,一样有 --keep-outdated 命令:
$ pipenv uninstall --help
...
--keep-outdated Keep out-dated dependencies from being updated in
Pipfile.lock. [env var: PIPENV_KEEP_OUTDATED]复制代码
再试一下,虽然我已经不抱期待了:
$ pipenv uninstall --keep-outdated gunicorn复制代码
顺便说一句,卸载的另外一个问题是,当你卸载一个包的时候,只会卸载这个包自己,而这个包引入的相关依赖都会被保留,须要手动使用 clean 或 sync 命令修正(参考 Poetry README)。
固然,Pipenv 一直在改进。好比对 Windows 的支持,Lock 很是慢的问题,都有过不少的优化。
可是,种种证据都在代表,这实际上是一个半成品。承诺了不少,兑现的却不多。或许过一段时间等它真正成熟了,可以保证基本使用流程,而且能够修改哪些反人类的设定之后再考虑用它(我怀疑这一条是否能实现,除非彻底「去 Kenneth Reitz 化」,而且有一个核心维护者可以来推进执行)。
如今,请不要使用它。
我很抱歉在《Flask Web 开发实战》以及文章《Pipenv:新一代Python项目环境与依赖管理工具》中,推进更多人用它,给你们带来潜在的麻烦。我计划了一些补救措施,会逐一执行:
你能够选择用回 virtualenv+pip(+virtualenvwrapper),或是尝试新工具,我会在下一篇文章介绍主要替代品 Poetry 的基本用法。
(1)