最简方案是在pyproject.toml中配置[tool.setuptools.packages.find] exclude = ["tests", "tests."],确保tests目录不被构建进wheel或sdist。

构建时跳过 tests/ 目录的最简方案
PyPI 上传或 pip install . 构建时,默认会把整个源码树打包进去,tests/ 目录若没显式排除,就会进 wheel 或 sdist。这不是“是否运行测试”的问题,而是“是否打包”的问题。
最稳妥的做法是在 setup.py 或 pyproject.toml 中声明排除规则,而非依赖 .gitignore 或临时脚本。
- 用
setup.py:在setup()调用中加packages=find_packages(exclude=["tests*", "tests.*"]) - 用
pyproject.toml(推荐):在[tool.setuptools.packages.find]下写exclude = ["tests*", "tests.*"] - ⚠️ 注意:
find_packages()不处理非 Python 文件(如tests/data/example.json),这类需额外用MANIFEST.in
MANIFEST.in 怎么精准剔除敏感配置文件
MANIFEST.in 控制的是 sdist(源码包)里包含哪些非 Python 文件,它对 wheel 无效——wheel 只打包 packages 和 data_files 显式声明的内容。所以如果你的 config/ 下有 prod.env、secrets.yaml 这类文件,又没被 packages 包含,它们本不该进包;但万一被 recursive-include config *.yaml 这类宽泛规则捕获,就危险了。
- 绝对不要写
recursive-include * *.env—— 它会把tests/fixtures/.env也拖进去 - 应写成
prune config/secrets.*或prune **/secrets.*,用prune显式剔除 - 验证是否生效:运行
python setup.py sdist --dry-run,看输出列表里有没有敏感文件名
CI 构建前用脚本做二次校验
人工维护 MANIFEST.in 和 setup.py 容易漏,尤其当团队多人提交、目录结构变动频繁时。可以在 CI 的构建步骤前加一道检查:
- 用
find . -name "*.env" -o -name "secrets.*" -o -name "config.*" | grep -v "^\./venv\|^\./.git"扫描工作区 - 再用
tar -tzf dist/*.tar.gz | grep -E '\.(env|yaml|yml|json)$' | grep -v 'test'检查已生成的 sdist - 如果命中敏感文件名,立即
exit 1中断发布流程 - 这个检查必须放在
python -m build之后、twine upload之前
为什么不能靠 __pycache__ 或 .pyc 过滤来防泄漏
有人误以为删掉 __pycache__ 就安全了,其实完全无关。Python 编译缓存不包含源码逻辑,更不会把 os.environ["DB_PASSWORD"] 这种值编译进去。真正风险来自三类文件:硬编码密钥的 .py、未 exclude 的 config/、以及被 MANIFEST.in 错误 include 的 .env。
最隐蔽的坑是:某些模板引擎(如 Jinja2)会把 config/ 当作静态资源自动收集;某些打包工具(如 poetry build)默认忽略 MANIFEST.in,只认 include 配置——这时候得去查 poetry.toml 里的 [[tool.poetry.include]] 是否漏写了排除项。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











