pipenv 與 virtualenv 的關係

pipenv 用來管理 python 的 app 是不錯(注意不是模組,模組還是該用 distutils),不過基於 python 社群的一貫風格還是要搞點問題出來才行。先前碰到 issue 2364,搞了好久才全部弄清楚。背景知識要先知道 pyvenv, pyenv, virtualenv, pip, pipenv 這一堆東西是幹嘛的(stackoverflow 上的說明),然後要知道 virtualenv 會綁初始 pip, setuptools, wheel 的版本,就比較容易弄清楚了。

狀況是:在 Ubuntu 19.04 上面用 pipenv 設定開發的專案,Python 版本綁在系統預設的 3.6. 後來移到 20.04,由於系統版本升到 3.8,所以先裝 pyenv 然後再 pipenv sync,理論上這樣 pipenv 會先用 pyenv 裝好 3.6、產生執行 3.6 版的 virtualenv、然後裝好所有需要的模組。但最後在安裝模組階段噴錯。研究後發現裡面用到的模組需要較新的 setuptools 版本才能正確安裝,而版本是由 virtualenv 產生環境時決定的,但 pipenv 的開發者認為 virtualenv 屬於使用者環境的一部分,他們不應該去修改,使用者一番爭論後答應看一看,就又沒下文了。

當安裝 pipenv 時 (python3 -m pip install pipenv),由於 virtualenv 是一個 dependency,所以會先檢查有沒有符合的版本,沒有的話會自動帶入。這就是在 20.04 那邊出錯的原因,因為 19.04 的系統沒裝 python3-virtualenv 但 20.04 的系統有,所以最早在 19.04 那台安裝 pipenv 時帶入了新版的 virtualenv,比 20.04 系統裡面預設安裝的新,所以一直沒問題。移到 20.04 時,pip 檢查到 virtualenv 已經存在,就用現有的,所以產生的環境裡面 setuptools 比較舊,安裝模組時就失敗了。

這邊要注意的是: pyenv 在這邊的作用,並不及於 pipenv 以及 virtualenv. 也就是說,在預設為 3.8 的系統上跑 pipenv install 時,是用 3.8 搭配的 pipenv,自然也執行到了 3.8 搭配的 virtualenv. 直到以 pipenv shell 或者 pipenv run 的時候,被執行的程式才會真正用到虛擬環境裡的 python 版本,也就是 3.6.

知道以上資訊後,解法也很簡單。不管系統裡面有沒有 virtualenv,反正都用 python3 -m pip install --user virtualenv 在 home 底下裝一份新的就好。這也表示在重建 build 環境時,除了 Pipfile 裡面已經綁死的東西,也要注意用到的 virtualenv 版本是否相容。