src目录结构通过物理隔离源码强制统一开发与用户环境:必须pip install -e .才能导入,避免本地侥幸成功;关键需在pyproject.toml的[project]中配置packages = [{include = "mypackage", from = "src"}],否则无效。

src目录结构能避免“本地导入侥幸成功”导致的环境不一致
你写完代码,python main.py 一跑就通,但用户 pip install mypackage 后却报 ModuleNotFoundError——这大概率是因为你没用 src/。Python 默认把当前工作目录(也就是项目根目录)加进 sys.path[0],所以扁平结构下,import mypackage 实际加载的是本地文件夹,不是安装后的包。而 src/ 把源码物理隔离,强制你必须先 pip install -e . 才能正常导入,让开发环境和用户环境行为对齐。
pyproject.toml里不配packages.from="src",src就等于白建
只建 src/ 目录不生效,关键在构建配置。常见错误是只写了 [build-system] 段,却漏掉 [project] 中的包声明:
[project]
name = "mypackage"
version = "0.1.0"
# 必须显式告诉打包工具:包名叫 mypackage,代码在 src/ 下
packages = [{include = "mypackage", from = "src"}]验证是否生效很简单:pip install -e . && python -c "import mypackage; print(mypackage.__file__)",输出路径里必须含 src/,否则就是配置没起作用。
tests/ 必须与 src/ 平级,且用绝对导入而非相对路径
测试目录放错位置或写错导入语句,会直接绕过 src 的保护机制:
立即学习“Python免费学习笔记(深入)”;
- ❌ 错误:把
tests/塞进src/里,或放在子目录中 - ❌ 错误:测试文件里写
from src.mypackage.utils import helper - ✅ 正确:
tests/和src/平级;测试中统一用from mypackage.utils import helper
这样 pytest 在根目录运行时,才能通过已安装的包路径导入,而不是靠临时修改 sys.path 或硬编码路径——后者会让 CI 环境和本地行为不一致。
IDE 和类型检查器依赖 pyproject.toml 的 packages 声明
VS Code、PyCharm、mypy 这些工具不会自动理解 “src 是源码根”,它们靠 pyproject.toml 里的 packages 配置定位模块。如果没配,IDE 可能标红 from mypackage import xxx,类型检查也会失效。这不是插件问题,是配置缺失。尤其当你用 __init__.py 做版本声明(比如 __version__ = "0.1.0"),Flit 或 Hatch 会从这里读取动态版本——但前提是它们得先找到这个包,而这完全依赖 packages.from="src"。
真正容易被忽略的点是:src 不是目录习惯,而是导入契约。它不解决“怎么写代码”,而是定义“代码该怎么被加载”。一旦跳过这层约束,后续所有测试、CI、协作、发布都会在某个时间点突然崩塌——往往发生在你以为项目“差不多能用了”的时候。


















