這一直是我在使用 Python 和 pip 處理非設計為套件安裝的專案時,最令人困擾的問題之一:你該如何處理依賴關係?

想像一下像是應用程式後端、Python 腳本、REST API 等等的專案。

如果你曾經為此苦惱過,你一定會愛上這項功能:由 Sebastian Höffner 提出的 #13895 PR,將為 pip 新增一個全域旗標,讓你能直接安裝 pyproject.toml 中的依賴,而不需安裝套件本身。

pip install --only-deps .

至少對我來說,這將大幅改善我在伺服器上管理和分發專案的方式。能大幅簡化多年來累積的手動 workaround。

由於這項功能已醞釀超過十年,讓我們來回顧一下那些苦苦尋找方法、試圖為自己的腳本或應用程式安裝依賴的 Python 開發者們的歷史。

requirements.txt?

python -m pip freeze [options]

pip freeze 是最經典且穩定的第一步,能精確記錄你安裝的套件版本,達到可重現性。

之後你就可以在任何環境中重建!

env1/bin/python -m pip freeze > requirements.txt
env2/bin/python -m pip install -r requirements.txt

pip freeze 會匯出所有依賴(包含依賴的依賴)

這些依賴非常依賴你的環境與硬體。當你試圖在其他機器重建環境時,用 freeze 的結果進行安裝很容易失敗。不同的 Python 版本、作業系統版本、函式庫或機器硬體,都會導致套件版本需求不同(甚至是完整套件)。

把 == 拿掉

這是我最早記得的 workaround,雖然不是最好的,但我清楚記得 15 年前我就保留了這個指令:
pip freeze --local | grep -v '^\-e' | cut -d = -f 1

對我來說(也可能是更早的其他人),這有時會演變成手動在 requirements.txt 中列出依賴清單,我認為這是一個不錯的捷徑。

pip install -e .

這個指令從 pip 誕生(2008 年)以來就一直能正確運作,是最快讓 pip 安裝依賴清單的方法。這些依賴過去記錄在 Python 的 setup.py(以及其他檔案)中,這些檔案早於 pip 出現。後來依賴已遷移至 pyproject.toml

但 pip install -e . 會安裝套件本身

這對函式庫和某些專案來說很有效,但對應用程式或 API 框架來說會變成麻煩,因為你可能會有包裝器在執行 Python 程式碼。

可編輯安裝也不是部署的最佳實踐。

安裝為套件也會在 2021 年之前產生 distribution egg 檔案,若不小心維護,這些檔案很容易過時。

16 年來人們尋找 workaround 的歷史

包含我在內的人們,數十年來在 StackOverflow 上詢問如何只安裝依賴:

PIP: Installing only the dependencies(16 年前) -> 使用 pip install -e .

pip freeze without dependencies of installed packages(15 年前) -> 使用第三方套件 pip-chill

pip freeze without dependencies of installed packages(10 年前) -> 使用第三方套件 pipreqs

Is there a smarter way to build requirements.txt files?(2 年前) -> 使用第三方套件 poetryhatch

Installing dependencies without the package(1 年前) -> 使用第三方套件 uv

看看這些 StackOverflow、Reddit 和 Python.org 討論的演進。你會發現第三方函式庫的解決方案越來越實用。

奠基工作:pyproject.toml 與依賴

pyproject.toml 推出後,2017 年的 PEP 517 為 pyproject 新增了 [build-system] 的 hooks,讓 pip、hatch 或後來的 uv 可以使用。

另一個關鍵(最終解決方案會用到)是 2023 年的 PEP 735(Dependency Groups),它將依賴分組(如 dev)引入 pyproject.toml,讓使用者可以選擇特定群組的依賴進行安裝。

破局者:UV

最後,我們來談談 Python 生態系的寵兒 uv。它證明了自己的實用性,也促使 Python / pip 做出許多原本停滯的改變。我認為值得注意的是,雖然上述討論中出現過許多不同的建置工具,但沒有一個達到 uv 這樣的普及程度。

uv 的解決方案:uv sync --no-install-project

uv 橫空出世,利用了之前的所有 groundwork,並展示了使用者對快速且能解決實際問題的建置工具的需求有多麼強烈。

Pip:GitHub Issue 成形

提議的解決方案

pip install --only-deps .  # project.dependencies
pip install --only-deps .[doc]  # project.dependencies and project.optional-dependencies

Höffner 的 PR 新增了一個全域選項,能根據你的 `pyproject.toml` 安裝專案的依賴,而不安裝專案本身。終於,在 pip 誕生近 20 年後,應用程式類型專案或腳本的依賴安裝功能來了。

2026:一個優雅的解決方案,來自社群的共同努力

回顧這十年來的工作,有太多步驟需要由 Python 社群的 PEP、pip 維護者,甚至第三方套件共同完成。但現在我們終於迎來了 pip 26.2 的一個小而美好的改進。

我希望大家都能和我一樣享受這個功能。