这是我在使用 Python 和 pip 处理非包形式项目时遇到的最大烦恼之一,如何管理依赖?

想想那些应用后端、Python 脚本、REST API 等项目

如果你也为此苦恼,你会喜欢这个方案:Sebastian Höffner 提交的 PR #13895 将为 pip 添加一个新的全局标志,让你可以直接安装 pyproject.toml 中的依赖,而无需安装项目本身。

pip install --only-deps .

这对我来说,将极大提升我在服务器上管理和分发项目的方式,大幅简化多年来积累的手动变通方案。

由于这个问题已经存在十多年,让我们回顾一下 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 会导出所有依赖(包括依赖的依赖)

这些依赖高度依赖于你的环境和硬件。在其他机器上重建环境时,pip freeze 往往会失效。不同的 Python 版本、操作系统版本、库或硬件会导致不同的包版本需求。

去掉 ==

这是我最早的 workaround 之一,虽然不算完美,但我记得 15 年前就保留了这个命令:
pip freeze --local | grep -v '^\-e' | cut -d = -f 1

后来我(以及更早的开发者)有时会手动把依赖列表写入 requirements.txt,这算是一个不错的捷径。

pip install -e .

自 pip 诞生(2008 年)以来,这个命令一直是安装依赖列表的最快方式。依赖最初定义在 setup.py 中,后来迁移到了 pyproject.toml

但 pip install -e . 会安装项目本身

这对库和某些项目很有效,但对应用或 API 框架来说会成为麻烦,因为你可能需要包装器来运行 Python 代码。

可编辑安装也不是部署的最佳实践

安装为包还会生成分发 egg 文件,直到 2021 年才被弃用,若不小心管理,这些文件容易过期。

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[build-system] 添加了钩子,供 pip、hatch 或后来的 uv 使用。

另一个关键是 2023 年的 PEP 735(Dependency Groups),它允许在 pyproject.toml 中对依赖进行分组(如 dev),用户可以选择安装特定组的依赖。

搅局者:UV

最后我们来看 Python 生态的宠儿 uv。它展示了构建工具的巨大潜力,促使 Python/pip 团队重视长期未解决的问题。值得注意的是,尽管之前有多种构建工具,但没有一个能达到 uv 的流行程度。

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

uv 的出现充分利用了之前的成果,证明了用户对快速、实用的构建工具的强烈需求。

Pip:GitHub Issue 引发关注

提议的方案

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

Höffner 的 PR 添加了一个全局选项,使 pip 能够根据 pyproject.toml 安装项目依赖,而不安装项目本身。历经近 20 年,pip 终于支持为应用类项目或脚本安装依赖。

2026:社区协作的优雅解决方案

回顾这段历程,Python 社区的 PEP、pip 维护者和第三方工具都做出了贡献。现在,一个小而实用的改进即将进入 pip 26.2。

希望大家和我一样享受这个改进。