简体中文
项目里混入 Python 脚本后,依赖该装在哪?venv 与 PEP 668 实战

本文从一个真实报错切入,讲的不是"怎么修好这一条",而是多语言项目里 Python 依赖的边界该怎么划。 无论你的项目是 Node、Go、Rust 还是纯前端,只要混进了
.py脚本,这篇都适用。
一、案发现场:两条命令,两个报错
项目里有个 Python 检查脚本(scripts/*.py),本地开发时通过 npm script 串起来跑。某天敲下启动命令,第一环就挂了:
text
> python3 scripts/source-language-docs-check-colon.py
Traceback (most recent call last):
File ".../scripts/source-language-docs-check-colon.py", line 5, in <module>
import yaml
ModuleNotFoundError: No module named 'yaml'第一反应是装一下就好了:
bash
python3 -m pip install pyyaml结果撞上第二个报错:
text
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
xyz, where xyz is the package you are trying to
install.
If you wish to install a Python library that isn't in Homebrew,
use a virtual environment:
python3 -m venv path/to/venv
source path/to/venv/bin/activate
python3 -m pip install xyz
If you wish to install a Python application that isn't in Homebrew,
it may be easiest to use 'pipx install xyz', which will manage a
virtual environment for you. You can install pipx with
brew install pipx
You may restore the old behavior of pip by passing
the '--break-system-packages' flag to pip, or by adding
'break-system-packages = true' to your pip.conf file. The latter
will permanently disable this error.
If you disable this error, we STRONGLY recommend that you additionally
pass the '--user' flag to pip, or set 'user = true' in your pip.conf
file. Failure to do this can result in a broken Homebrew installation.
Read more about this behavior here: <https://peps.python.org/pep-0668/>
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.一个简单的 import,被 Python 拦了两道。下面把这两道关拆开看。
二、第一坑:yaml 不是 Python 标准库
看到 import yaml,直觉会以为它是内置模块,其实不是。Python 标准库里没有 yaml。
这里的 yaml 来自第三方包 PyYAML:
- 安装名:
pyyaml - 导入名:
yaml
名字对不上,是很多人第一次装它时踩的坑。所以正确做法确实是 pip install pyyaml。真正的问题不在"装不装",而在装到哪个环境。
三、第二坑:PEP 668 与 "externally managed environment"
externally-managed-environment 这个报错来自 PEP 668。它的含义是:
这个 Python 环境由系统(如 Homebrew、Debian/Ubuntu 的 apt)管理,请不要往里面塞第三方包,以免破坏系统 Python。
这实际上是保护机制。过去很多人用 sudo pip install 把系统 Python 搞崩,陷入"升级就坏、不升级也坏"的循环。PEP 668 相当于给系统 Python 贴了封条。报错信息本身也把出路列全了:
- 路径 A:
brew install xyz,适用于 Homebrew 已打包的库 - 路径 B:
python3 -m venv ...,适用于任意第三方库 - 路径 C:
pipx install xyz,适用于命令行工具 - 路径 D:
--break-system-packages,逃生门,风险自负
四、核心问题:依赖到底该装在哪?
当项目里混入 Python 脚本,摆在面前的是四种装法,各有利弊:
| 装法 | 命令 | 适用场景 | 风险 |
|---|---|---|---|
| 系统 Python | sudo pip install | 不要用 | 污染系统、升级即崩 |
| 用户级 | pip install --user | 个人机器上的小工具 | 跨项目版本冲突 |
| 逃生门 | --break-system-packages | 临时救急 | 破坏 Homebrew 管理 |
| 项目 venv | python3 -m venv .venv | 项目级依赖 | 几乎无 |
结论:项目依赖放项目 venv。 这是唯一隔离、可重建、可复现的选项。
bash
cd /path/to/your-project
# 1. 创建虚拟环境
python3 -m venv .venv
# 2. 激活
source .venv/bin/activate
# 3. 安装依赖(注意在 venv 激活状态下)
pip install pyyaml
# 4. 验证
python3 -c "import yaml; print(yaml.__version__)"成功后打印版本号(如 6.0.3)即完成。
五、为什么不用 --break-system-packages
最省事的做法显然是 python3 -m pip install --break-system-packages pyyaml,一行搞定,报错消失。但不要把它当首选,理由:
- 破坏 Homebrew 的管理契约:将来
brew upgrade python可能冲突。 - 问题延迟爆发:今天不报错,不代表半年后不报。跨项目库版本互相干扰,是最难查的那类 bug。
- 不可复现:换台机器、换个同事,环境就不同。
venv 的成本只有三次命令加一行 .gitignore。用"可重建"换"省事",在项目级依赖上永远不亏。
六、一个细节:为什么不用改脚本
你的 package.json(或 Makefile、shell 脚本)里调用的通常是裸的 python3:
json
{
"scripts": {
"check": "python3 scripts/some-check.py"
}
}venv 激活后,python3 会自动解析到 .venv/bin/python3,靠 PATH 优先级搞定。所以:
- 不需要改脚本里的
python3路径 - 不需要设
PYTHONPATH - 只需要"先激活,再运行"
bash
source .venv/bin/activate
npm run docs:dev # 或 make dev,或任何调用 python3 的命令七、收尾:把经验固化下来
修一次是运气,修成规范才是资产。三样东西值得固化进仓库:
1. .gitignore 忽略 venv
gitignore
.venv/2. requirements.txt 固定版本
警告
如果项目还没有 requirements.txt 文件,请先创建:
bash
touch requirements.txt然后填入:
txt
pyyaml==6.0.33. README 补「首次配置」
markdown
### 前置依赖(Python)
本项目部分脚本使用 Python,需创建虚拟环境:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```可选:make dev 一键搞定
makefile
dev:
@if [ ! -d .venv ]; then python3 -m venv .venv && .venv/bin/pip install -r requirements.txt; fi
@. .venv/bin/activate && npm run docs:dev之后一句 make dev,venv 不存在会自动创建并装依赖,新人 clone 下来零思考。
八、可以带走的五条经验
import yaml不是标准库。它来自 PyYAML,装包名与导入名不一致,别被名字骗。- PEP 668 是好事。
externally-managed-environment是在保护系统 Python,遇到它该感谢,而不是想绕过。 --break-system-packages是最后手段。它存在,但不该是第一反应。- venv 成本极低。三次命令加一行
.gitignore,就能永久解决"包装哪儿"的问题。 - 写完才算修完。把踩坑写成文档、README 或
maketarget,是团队收益最高的收尾动作。
一句话总结:再看到 ModuleNotFoundError: No module named 'xxx',先别急着 pip install,先问一句「这个依赖属于哪个项目?」。答案通常就是那个项目自己的 venv。
