大概在 2 年前就聽過 Poetry 的大名,不過那時我還沒有套件相依性管理的強烈需求,加上看起來需要一些學習成本(確實如此),所以就一直擱在一旁,直到真正體會到了 pip 的不足。

pip 是 Python 內建的套件管理工具,而它的最大罩門,就是對於「套件間的相依性管理」能力不足。尤其是在「移除」套件時的依賴解析——可以說沒有。這也是我提議改用 Poetry 的根本原因。

怎麼說?看完下面的例子就能明白。

pip uninstall的困境:以 Flask 為例

假設現在你的工作專案中有開發 API 的需求,經過一番研究與討論,決定使用 Flask 網頁框架來進行開發。

我們知道,很多套件都有依賴的套件,也就是使用「別人已經造好的輪子」來構成套件功能的一部分。

安裝主套件時,這些依賴套件也必須一併安裝,主套件才能正常運作,這裡的 Flask 就是如此。安裝 Flask 時,不會只安裝單一個flask套件,還會安裝所有 Flask 的必要構成部分——也就是依賴套件,結果如下:

❯ pip install flask
Collecting flask
  Downloading Flask-2.1.1-py3-none-any.whl (95 kB)
     |████████████████████████████████| 95 kB 993 kB/s
...
Installing collected packages: zipp, MarkupSafe, Werkzeug, Jinja2, itsdangerous, importlib-metadata, click, flask
Successfully installed Jinja2-3.1.1 MarkupSafe-2.1.1 Werkzeug-2.1.1 click-8.1.2 flask-2.1.1 importlib-metadata-4.11.3 itsdangerous-2.1.2 zipp-3.7.0

從上可知,pip install flask還會一併安裝importlib-metadataitsdangerous等 7 個依賴套件,實際上總共安裝了 8 個套件!

可以說,pip 在「安裝」套件時的相依性管理還是可以的,這並不難,因為套件的依賴要求都寫在安裝檔裡了,根本不需要「管理」。


附帶一提,這 8 個套件包括flask,除了importlib-metadatazipp外,其餘 6 個實際上都是由 Flask 團隊自行開發

但並非只有 Flask 框架會使用(依賴)這些套件。

比如其中的 Click 就是一個被廣泛使用的命令列製作工具。套件官網是這麼介紹的:

Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary.

別的套件也可能依賴click來提供命令列的功能,換句話說,主套件的依賴套件也可能被其他第三方套件所依賴、使用。這就產生了「衝突」的可能。


好,一切都很美好,就這樣一年過去,團隊決定改用火紅的 FastAPI 取代 Flask 來實作專案的 API,作為 API 的主要開發人員,你對新技術充滿了期待(或排斥),興高采列地安裝了 FastAPI,更新了所有程式碼,最後要移除 Flask,這時問題就來了。

安裝 Flask 的時候,我們只需要pip install flask,pip 就會幫你一併安裝所有依賴套件。現在要移除它,也只要pip uninstall flask就可以了嗎?

很遺憾,答案是否定的。