pdm install 后 python main.py 找不到包,是因为 pep 582 需通过 pdm run 触发路径注入,直接调用系统 python 不会加载 __pypackages__/x.y/lib;pdm.lock 是安装唯一依据,pyproject.toml 仅声明依赖意图。

pdm 默认启用 PEP 582,不依赖虚拟环境也能隔离依赖——但锁文件行为和路径加载机制容易让人误判“已经装好了”,结果运行就报 ModuleNotFoundError。
为什么 pdm install 后 python main.py 还是找不到包?
根本原因:PEP 582 不是“自动生效”的魔法,它依赖 Python 解释器启动时的特定条件。
-
python main.py调用的是系统或全局 Python,sys.path完全不含__pypackages__/X.Y/lib -
pdm run python main.py才会把__pypackages__/X.Y/lib插入sys.path[0],优先于 site-packages - 即使
pdm install成功,锁文件pdm.lock只管“该装什么版本”,不管“怎么让 Python 看见它”
常见错误现象:ImportError: cannot import name 'xxx' from 'yyy' 或直接 ModuleNotFoundError,但 pdm list 显示包明明已安装。
pdm.lock 和 pyproject.toml 怎么协同工作?
pdm.lock 是确定性安装的唯一依据,不是可选副产品。
-
pdm add requests→ 只改pyproject.toml的[project.dependencies],触发重新解析并更新pdm.lock -
pdm install→ 完全忽略pyproject.toml中的约束,只读pdm.lock,解压对应哈希的 wheel 到__pypackages__/X.Y/lib -
pdm sync(推荐用于 CI)→ 行为同install,但跳过解析环节,更快更稳定,且默认只装生产依赖
关键区别:add 是声明意图,install/sync 是执行锁定状态。删掉 pdm.lock 后只跑 pdm install 会失败,必须先 add 或 sync --no-lock。
IDE 和非 pdm 命令(如 pytest/mypy)怎么接入 PEP 582?
它们不会自动识别 __pypackages__,必须显式注入路径或走 pdm run。
- VS Code:在
.vscode/settings.json中设"python.defaultInterpreterPath"为pdm python list输出的路径;再把__pypackages__/3.11/lib标为 Source Folder - PyCharm:File → Settings → Project → Python Interpreter → Add → “System Interpreter” → 选
pdm python list返回的路径;然后右键__pypackages__/3.11/lib→ “Mark as Sources Root” - 命令行工具:
pdm run pytest tests/✅,pytest tests/❌;也可在pyproject.toml的[tool.pdm.scripts]里定义test = "pytest tests/",再用pdm run test
注意:__pypackages__/X.Y/lib 下的包对 pip list 不可见,这是设计使然——它绕过了 pip 的 site-packages 管理逻辑。
Docker 中禁用虚拟环境却仍要小心 PATH 和 PYTHONPATH
很多人在 Dockerfile 里加了 pdm config python.use_venv false 就以为万事大吉,其实 PEP 582 在容器里仍需手动补位。
- 如果禁用 venv,
pdm install仍会把包放进__pypackages__/X.Y/lib,但容器内默认 Python 不知道这个路径 - 必须显式设置:
ENV PYTHONPATH="/project/__pypackages__/3.11/lib"(版本号需匹配实际 Python) - 或者更稳妥:所有入口命令统一走
pdm run,例如command: pdm run python ./src/app.py - 切勿混用:
pip install和pdm install同时存在会导致路径冲突,__pypackages__和site-packages里的同名包可能版本打架
最易被忽略的一点:PEP 582 的路径注入只发生在解释器启动瞬间,一旦进程运行起来,sys.path 就固定了——所以动态修改 PYTHONPATH 环境变量对已启动的进程无效,必须在 exec 前就准备好。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











