← back

将 Python 项目发布到 PyPI

通过PyPI将自己的包进行发布,之后可以方便通过pip进行安装使用

本文将通过一个实际项目示例,讲解如何使用 pyproject.toml 对 Python 项目进行标准化打包、上传至 PyPI,并结合 GitHub Actions 实现自动化部署


1. 为什么将项目发布到 PyPI

将项目发布到 PyPI 可以方便他人(或自己)通过 pip install xxx 命令快速安装和使用你的库或工具。这也是很多开源项目默认的分发方式


2. 打包工具简介

当前 Python 官方推荐使用的打包工具是 build。其打包配置依赖以下几个常见文件:

文件名作用
pyproject.tomlbuild 的配置文件
MANIFEST.in控制源码包中包含哪些非 .py 文件

🎯 当前官方最佳实践推荐优先使用 pyproject.toml来管理元数据,也是本文使用的方式。但网上很多都是使用setup.py ,这是历史遗留问题且不符合PEP标准,建议逐渐弃用


3. 示例项目结构说明(以 nuscenes-hacker 项目为例)

项目目录如下:

nuscenes_hacker
├── LICENSE
├── MANIFEST.in
├── nuscenes_hacker # 源码
├── pyproject.toml
└── README.md

该项目是一个包含命令行工具和库模块的综合型项目。


4. 编写基础 pyproject.toml

以下是一个基础的 pyproject.toml 配置,可满足大多数项目需求:

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta" # 使用setuptools做为构建后端

[project]
dependencies = ["pyyaml", "nuscenes-devkit"]
name = "nuscenes-hacker"
version = "0.0.3"
description = "nuscenes-hacker"
authors = [{ name = "windzu", email = "windzu1@gmail.com" }]
readme = "README.md"
license = { text = "MIT" }
keywords = ["nuscenes"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Natural Language :: English",
    "Programming Language :: Python :: 3",
]
requires-python = ">=3.6,<3.8"

[project.scripts]
nuscenes-hacker = "nuscenes_hacker.main:main"

[tool.setuptools]
include-package-data = true

5. 添加非 Python 文件(MANIFEST.in)

为了将静态文件、配置文件等非 .py 文件打包进源代码包(sdist),我们使用 MANIFEST.in

include README.md
include LICENSE
include pyproject.toml

并在 pyproject.toml 中设置 include_package_data=true 才能生效。


6. 本地测试安装

pip install -e .  # 使用开发模式安装,可自动更新代码变更

7. 发布到 PyPI

⚠️:2023年后全面禁用密码验证,统一采用 API Token

(1)准备工作

(2)打包并上传

# 安装构建工具和上传工具
pip install build twine 

# 构建
python -m build

# 上传到测试环境
twine upload --repository testpypi dist/*

# 上传到正式环境
twine upload dist/* # 使用通用token
# twine upload --repository wadda dist/ # 使用指定token

8. 使用 GitHub Actions 自动部署

(1)设置 PyPI Token

(2)GitHub Action 配置示例

name: Publish to PyPI

on:
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v3
        with:
          python-version: "3.7"

      - name: Install build and twine
        run: |
          python -m pip install --upgrade pip
          pip install build twine

      - name: Build the package
        run: |
          rm -rf dist/ build/ *.egg-info
          python -m build

      - name: Publish to PyPI
        if: github.event.pull_request.merged == true
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
        run: |
          twine upload dist/*

⚠️ 注意:每次发布新版本前需要修改 setup.py 中的 version 字段。


附录:常用参数速查表

pyproject.toml 常用字段速查表(等效于 setup()

pyproject.toml 字段说明(与 setup() 中的字段等效)
name / version包名与版本号
authors / authors.email作者信息
description / keywords简短描述与关键词
readmeREADME 文件路径,可为字符串或对象(附带内容类型)
license / license.text许可证,如 "MIT"{ text = "MIT" }
urls项目主页及其他链接(如 GitHub、文档)
requires-pythonPython 最低版本要求
dependencies安装时所需依赖(原 install_requires
optional-dependencies可选依赖,如 dev, test, docs
scripts / gui-scripts命令行入口(原 entry_points['console_scripts']
entry-points其他类型的扩展点(如插件系统)
[tool.setuptools] include-package-data是否包含额外静态文件(原 include_package_data
[tool.setuptools.packages.find]包含的模块目录(原 find_packages()
zip-safe⚠️ 不再使用,默认为安全

参考资料

  1. 官方教程:https://packaging.python.org/en/latest/tutorials/packaging-projects/
  2. setuptools 文档:https://setuptools.pypa.io/en/latest/userguide/
  3. 打包发布博客参考:https://mp.weixin.qq.com/s/rNcrLbPSkBKfw3VNr9XYnA