
Python 包发布实战Twine 6.2.0 与 setuptools 68.0.0 配置 5 个关键步骤在 Python 生态系统中将自己的代码打包并发布到 PyPIPython Package Index是分享和分发代码的标准方式。随着工具链的不断更新现代 Python 打包流程已经发生了显著变化。本文将详细介绍使用最新版 Twine 6.2.0 和 setuptools 68.0.0 进行 Python 包发布的完整流程涵盖从项目配置到 CI/CD 集成的五个关键步骤。1. 现代 Python 项目结构配置现代 Python 项目已经逐渐从传统的setup.py转向更规范的pyproject.toml和setup.cfg配置方式。这种转变带来了更好的可维护性和更清晰的依赖管理。一个标准的现代 Python 项目结构应该如下所示my_package/ ├── src/ │ └── my_package/ │ ├── __init__.py │ └── module.py ├── tests/ │ └── test_module.py ├── pyproject.toml ├── setup.cfg ├── README.md └── LICENSE关键文件说明pyproject.toml: 定义构建系统要求和项目元数据setup.cfg: 包含包的详细配置信息src/目录: 遵循推荐的src 布局避免开发时意外导入pyproject.toml 配置示例[build-system] requires [setuptools68.0.0, wheel] build-backend setuptools.build_meta [project] name my-package version 0.1.0 description My awesome Python package readme README.md requires-python 3.8 authors [ {name Your Name, email your.emailexample.com} ] license {text MIT} classifiers [ Development Status :: 3 - Alpha, Intended Audience :: Developers, License :: OSI Approved :: MIT License, Programming Language :: Python :: 3, ] dependencies [ requests2.25.0, numpy1.20.0, ] [project.urls] Homepage https://github.com/you/my-package Documentation https://github.com/you/my-package#readmesetup.cfg 补充配置[metadata] description-file README.md long_description file: README.md long_description_content_type text/markdown [options] package_dir src packages find: python_requires 3.8 install_requires requests2.25.0 numpy1.20.0 [options.packages.find] where src注意pyproject.toml和setup.cfg可以协同工作前者更适合定义构建系统和项目元数据后者则更适合定义包的具体配置。2. 安全认证配置.pypirc 文件最佳实践传统上开发者会在.pypirc文件中存储 PyPI 的用户名和密码但这存在安全风险。现代实践推荐使用 API Token 进行认证既安全又方便。创建 PyPI API Token登录 PyPI 账户进入Account Settings → API tokens点击Add API token设置 token 作用范围建议限制为特定项目复制生成的 token只会显示一次安全配置 .pypirc 文件在用户主目录下创建或修改~/.pypirc文件[distutils] index-servers pypi testpypi [pypi] username __token__ password pypi-你的API令牌内容 [testpypi] repository https://test.pypi.org/legacy/ username __token__ password pypi-测试环境的API令牌内容安全建议永远不要将.pypirc文件提交到版本控制为不同环境使用不同的 token定期轮换 token设置 token 的 scope 限制重要如果使用 CI/CD 系统应该通过环境变量传递 token 而不是存储在文件中。例如export TWINE_USERNAME__token__ export TWINE_PASSWORDpypi-your-token-here3. 构建与验证使用最新工具链安装必要工具确保你安装了最新版本的构建工具pip install --upgrade pip setuptools wheel twine构建分发包现代 Python 打包推荐使用build工具虽然本文聚焦 setuptools但了解替代方案很重要python -m build这个命令会生成两种分发包源码分发(sdist):dist/your-package-0.1.0.tar.gz构建分发(wheel):dist/your_package-0.1.0-py3-none-any.whl构建过程关键点构建类型文件扩展名优点缺点sdist.tar.gz兼容所有Python版本需要用户端构建wheel.whl安装更快无需编译可能有平台限制验证分发包在上传前使用 twine 检查分发包twine check dist/*这个检查会验证包的元数据是否正确长描述README是否能正确渲染文件结构是否符合标准4. 上传到 PyPI安全发布流程测试上传可选在正式发布前可以先上传到 TestPyPI 进行验证twine upload --repository testpypi dist/*正式上传到 PyPItwine upload dist/*上传过程注意事项确保版本号在 PyPI 上是唯一的一旦发布特定版本的包不能被修改只能 yank大型项目考虑分阶段发布版本管理策略现代 Python 打包推荐使用语义化版本控制 (SemVer):版本号部分示例何时递增MAJOR1.0.0不兼容的API变化MINOR0.1.0向后兼容的功能新增PATCH0.0.1向后兼容的问题修复版本号管理技巧在pyproject.toml中使用动态版本控制如从 git tag 读取考虑使用setuptools-scm自动管理版本号预发布版本使用后缀如1.0.0-alpha.15. CI/CD 集成自动化发布流程将包发布流程集成到 CI/CD 系统中可以确保每次发布的一致性和可靠性。以下是 GitHub Actions 的配置示例GitHub Actions 工作流示例在.github/workflows/publish.yml中创建name: Publish Python Package on: release: types: [published] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: 3.x - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build package run: python -m build - name: Check package run: twine check dist/* - name: Publish to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: twine upload dist/*关键配置说明触发器当创建新的 GitHub Release 时自动运行安全PyPI token 存储在 GitHub Secrets 中步骤安装 → 构建 → 检查 → 上传高级 CI/CD 技巧测试发布流程添加一个只在 pull request 时运行的工作流上传到 TestPyPI版本验证添加步骤验证pyproject.toml中的版本是否与 git tag 匹配多平台构建对于包含 C 扩展的包考虑使用 cibuildwheel 构建多平台 wheel- name: Build wheels uses: pypa/cibuildwheelv2.16.2 env: CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* CIBW_SKIP: *-musllinux_*现代 Python 打包的进阶技巧动态元数据管理现代 Python 打包支持从文件动态读取元数据# 在 __init__.py 中定义版本 __version__ 0.1.0然后在pyproject.toml中引用[project] version {attr my_package.__version__}可执行脚本配置在pyproject.toml中定义命令行工具[project.scripts] my-cli my_package.cli:main开发依赖管理使用optional-dependencies管理开发依赖[project.optional-dependencies] dev [ pytest7.0.0, black22.0, mypy0.900, ] test [ pytest7.0.0, pytest-cov3.0.0, ]安装时使用pip install -e .[dev,test]包数据包含策略在setup.cfg中指定包含的非代码文件[options.package_data] my_package *.json data/*.csv templates/*.html多环境兼容性测试使用 tox 自动化测试不同 Python 版本下的兼容性[tox] envlist py38, py39, py310, py311 isolated_build true [testenv] deps pytest pytest-cov commands pytest --covmy_package tests/常见问题与解决方案1. 上传时出现 File already exists 错误原因尝试上传相同版本号的包。解决方案递增版本号或者使用--skip-existing参数twine upload --skip-existing dist/*2. README.md 在 PyPI 上渲染不正确检查步骤确保long_description_content_type设置正确本地运行twine check验证测试 Markdown 语法是否兼容3. 依赖解析问题最佳实践使用宽松的依赖说明如numpy1.20.0而不是numpy1.20.0考虑使用python_requires指定 Python 版本范围4. 包安装后导入失败可能原因项目结构不符合标准__init__.py文件缺失包名与目录名不匹配调试方法python -c import my_package; print(my_package.__file__)5. 需要撤销某个版本操作步骤登录 PyPI 账户进入项目页面选择要撤销的版本点击 Yank 按钮注意撤销不会删除包只是标记为不推荐使用。性能优化建议优先构建 wheelwheel 安装速度比 sdist 快得多减少依赖仔细评估每个依赖的必要性延迟导入对于可选功能考虑延迟导入大型依赖压缩资源文件对于包内包含的数据文件考虑压缩使用slots对于内存敏感的应用考虑使用__slots__减少内存占用安全最佳实践使用 HTTPS确保所有下载都通过 HTTPS依赖审查定期检查依赖是否有安全漏洞签名发布考虑使用 GPG 签名发布最小权限原则API token 只授予必要权限敏感信息排除确保配置文件.pypirc不被提交到版本控制发布后的维护及时更新文档确保 PyPI 上的文档与最新版本同步处理 issue建立 issue 响应机制版本支持策略明确声明支持哪些 Python 版本弃用策略有计划地弃用旧功能给予用户迁移时间社区建设鼓励贡献建立行为准则工具链更新趋势Python 打包生态系统正在快速发展以下是一些值得关注的趋势PEP 517/PEP 518定义了现代构建系统标准PEP 621标准化pyproject.toml中的项目元数据PEP 660可编辑安装的新标准Hatch新兴的现代项目管理工具PDM另一个创新的 Python 包管理工具保持对打包工具链更新的关注可以让你始终使用最有效、最安全的方式分发 Python 代码。