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)