真正推进Python工程化需从模块组织、pyproject.toml配置、CI覆盖率校准和Docker构建四大问题入手:src/目录解决包结构混乱,pyproject.toml取代setup.py需满足构建系统与元数据要求,coverage需排除测试文件并启用parallel,Docker中禁用-e安装以保障缓存与稳定性。

这标题不是学习路线,是营销包装出来的信息噪音。真正要推进 Python 工程化,得从具体问题出发:模块怎么组织不混乱?setup.py 和 pyproject.toml 到底该用哪个?CI 流水线里 pytest 怎么跑才不漏测?
Python 包结构设计:为什么 src/ 目录比平铺更可靠
平铺式结构(myproject/__init__.py、myproject/main.py)在本地能 import,但发布后常出现 ModuleNotFoundError —— 因为安装时 Python 会把整个顶层目录当包名,而开发时又直接运行 python main.py,路径逻辑错位。
-
src/目录强制隔离源码与入口,pip install -e .只暴露src/下的包,避免“本地可跑、线上报错” -
pyproject.toml中必须声明packages = [{include = "myproject", from = "src"}],否则build阶段根本不会打包进去 - IDE(如 PyCharm)需手动标记
src/为 Sources Root,否则代码补全和调试路径会异常
pyproject.toml 替代 setup.py 的三个硬性条件
不是“推荐用”,而是某些场景下 setup.py 已失效:比如使用 poetry 或 hatch 构建,或需要 PEP 621 元数据支持(如 requires-python = ">=3.9")。
- 必须存在
[build-system]段,且requires至少含setuptools和wheel(哪怕只用pip build) -
[project]下的dependencies不支持动态表达式(如requests>=2.25; python_version >= "3.8"必须写成requires-python+ 环境标记) - 若项目含 C 扩展,
setup.py仍不可替代——pyproject.toml目前无法完整描述distutils编译逻辑
CI 中 pytest 覆盖率失真的真实原因
本地 pytest --cov=myproject 显示 92%,CI 流水线却只有 63%——大概率是没排除测试文件本身被计入覆盖率统计。
立即学习“Python免费学习笔记(深入)”;
- 在
pyproject.toml的[tool.coverage.run]下加omit = ["*/tests/*", "*/test_*.py"],否则test_main.py里调用main()会被算作“已覆盖”,实际是测试代码在执行 -
coverage run -m pytest比pytest --cov更可控,前者明确以模块方式启动,避免路径解析歧义 - 多进程测试(如
pytest-xdist)必须启用parallel = true并合并报告:coverage combine && coverage report
[tool.coverage.run] source = ["myproject"] omit = ["*/tests/*", "*/test_*.py"] parallel = true
Docker 构建中 pip install -e . 的陷阱
开发时习惯 -e 安装,但镜像里用它会导致:每次构建都重新解析依赖、缓存失效、甚至因 git+https 依赖触发认证失败。
- 生产镜像应改用
pip install .(非 editable),配合COPY pyproject.toml setup.py .提前利用 Docker 层缓存 - 若依赖含
gitURL,必须在pip install前配置 SSH key 或用token替换 URL(如git+https://${GITHUB_TOKEN}@github.com/user/repo.git) -
poetry export -f requirements.txt | pip install -r /dev/stdin在 Docker 中更稳定,尤其处理extras时
工程化的关键不在学多少“原理”,而在每次 pip install 失败、每次 CI 覆盖率跳变、每次 Docker 镜像体积暴涨时,能快速定位是 pyproject.toml 配置偏差、还是 coverage 统计口径错位——这些细节不记文档,只留在 debug 过的终端历史里。










