Tao
Tao

手把手教你打包Python项目到PyPI

这篇博文将带你一步步完成从项目准备到成功发布到 PyPI 的全过程,包括使用现代化的打包工具和最佳实践,例如利用 pyproject.tomlbuild 工具以及通过 TestPyPI 进行安全测试。

核心工具: pyproject.toml, setuptools, build, twine, uv (或 pip)

一个清晰的项目结构是成功打包的基础。推荐使用 src 布局,它能很好地将你的实际包代码与项目其他文件(如测试、文档、配置文件)隔离开来。

假设你的包名叫 mytoolpack

text

mytoolpack_project/
├── src/
   └── mytoolpack/          <-- 这是你的实际包代码
       ├── __init__.py      # 包初始化文件,让目录成为一个包
       └── module.py        # 你的核心代码,例如包含一个 cool_function
├── tests/                   <-- 测试代码
   └── test_module.py
├── .gitignore
├── LICENSE                  <-- 选择一个开源许可证!
├── README.md                <-- 详细的项目说明
└── pyproject.toml           <-- 打包和项目配置的核心文件

pyproject.toml 文件是现代 Python 打包的基石 (PEP 517/518/621)。它定义了项目的构建系统、元数据(如名称、版本、作者)以及依赖项。

这是一个 pyproject.toml 的示例:

toml

# mytoolpack_project/pyproject.toml

[build-system]
requires = ["setuptools>=61.0", "wheel"] # 声明构建依赖
build-backend = "setuptools.build_meta"
backend-path = ["."] # 可选,确保 setuptools 能找到

[project]
name = "mytoolpack" # PyPI 上的名字,也是用户导入的名字
version = "0.1.0"   # 遵循语义化版本 (semver.org)
authors = [
    { name="你的名字", email="你的邮箱@example.com" },
]
description = "一个能做很酷事情的示例工具包。"
readme = "README.md" # 指向你的 README 文件
requires-python = ">=3.8" # 项目支持的 Python 版本
license = { file = "LICENSE" } # 指向你的许可证文件,或使用 {text = "MIT"} 等
classifiers = [ # PyPI 分类器,帮助用户找到你的包
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License", # 与你选择的许可证对应
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Utilities",
]
keywords = ["tool", "utility", "example", "packaging"] # 包的关键词

# 项目的运行时依赖
dependencies = [
    # "requests>=2.20.0", # 例如:如果你的包需要 requests
    # "numpy",
]

[project.urls] # 可选链接
"Homepage" = "https://github.com/你的用户名/mytoolpack_project"
"Bug Tracker" = "https://github.com/你的用户名/mytoolpack_project/issues"
"Documentation" = "https://readthedocs.org/projects/mytoolpack/" # 如果有文档

# --- Setuptools 特定配置 (用于 src-layout) ---
[tool.setuptools.packages.find]
where = ["src"]  # 告诉 setuptools 在 src 目录下寻找包
# include = ["mytoolpack*"] # 更精确地指定

关键点:

  • [build-system]: 指定构建项目所依赖的build工具。
  • [project]: 包含所有关于你项目的重要元数据。
    • name: PyPI 上的唯一名称。
    • version: 包的版本号,每次发布新版本时都需要更新。
    • dependencies: 你的包运行时所依赖的其他包。
  • [tool.setuptools.packages.find]: 帮助 setuptools 找到你的包代码,特别是当你使用 src 布局时。

src/mytoolpack/__init__.py 文件中,你可以控制当用户导入你的包时,哪些功能是可用的:

python

# src/mytoolpack/__init__.py

from .module import cool_function  # 从同目录下的 module.py 导入 cool_function

__version__ = "0.1.0"  # 与 pyproject.toml 中的版本保持一致
__all__ = ['cool_function']  # 定义 `from mytoolpack import *` 时导入的内容

假设 src/mytoolpack/module.py 内容如下:

python

# src/mytoolpack/module.py

def cool_function():
    print("这是一个很酷的功能!")
    return True

现在你的项目结构和配置文件都准备好了,是时候构建你的分发包了。这些是最终上传到 PyPI 的文件。

  1. 安装构建工具 build: 如果你还没有安装 build 包,可以使用 uv (或 pip) 安装它:

    bash

    uv python install build
    # 或者 pip install build
  2. 运行构建命令: 在你的项目根目录 (mytoolpack_project/) 下运行:

    bash

    python -m build

    这个命令会调用 pyproject.toml 中指定的构建后端 (这里是 setuptools) 来创建分发文件。成功后,你会在项目根目录下看到一个新的 dist/ 目录,其中包含两个文件:

    • 一个 .tar.gz 文件 (源码分发 sdist)
    • 一个 .whl 文件 (wheel 包,预编译的二进制分发)

在直接将包上传到真正的PyPI之前,强烈建议先在 TestPyPI (test.pypi.org) 上进行测试。TestPyPI 是 PyPI 的一个独立测试实例,你可以随意上传、删除包,而不会弄乱官方仓库。

  1. 注册 TestPyPI 账号: 前往 test.pypi.org 并注册一个账户。

  2. 安装 twine: twine 是用于将包安全上传到 PyPI (和 TestPyPI) 的工具。

    bash

    uv python install twine
    # 或者 pip install twine
  3. 上传到 TestPyPI: 使用 twinedist/ 目录下的文件上传到 TestPyPI。你需要使用之前注册的 TestPyPI 用户名和密码(或者创建一个 API Token)。

    bash

    twine upload --repository testpypi dist/*

    按照提示输入你在 TestPyPI的API token

  4. 从 TestPyPI 安装并测试: 上传成功后,尝试在一个新的虚拟环境中从 TestPyPI 安装你的包,以验证一切是否按预期工作:

    bash

    # 创建并激活新虚拟环境 (使用 uv 或 venv)
    # uv venv .test_env
    # source .test_env/bin/activate
    
    uv python install -i https://test.pypi.org/simple/ mytoolpack
    # 或者 pip install -i https://test.pypi.org/simple/ mytoolpack
    
    # 现在测试你的包
    # python -c "import mytoolpack; mytoolpack.cool_function()"

    如果安装和基本功能都正常,那么你的包很可能已经准备好进入真正的 PyPI 了!

一旦你在TestPyPI 上确认一切顺利,就可以将你的包发布到官方的 PyPI (pypi.org)。

  1. 注册 PyPI 账号: 前往 pypi.org 并注册一个账户(如果你还没有的话)。与 TestPyPI 账号是独立的。

  2. 创建 API Token (推荐): 为了安全起见,不要直接使用你的 PyPI 用户名和密码上传。在你的 PyPI 账户设置中创建一个 API Token。创建时,可以限制其权限范围(比如只允许上传特定项目)。创建后立即复制并保存好这个 Token,因为它只显示一次。

  3. 上传到 PyPI:

    bash

    twine upload dist/*

    twine 提示输入用户名时,输入 __token__。当提示输入密码时,粘贴你刚刚创建的 PyPI API Token。

    如果你的 ~/.pypirc 文件正确配置了 PyPI 和 TestPyPI 的 token,twine 可能会自动选择。但通常手动指定或依赖 __token__ 方式更清晰。

恭喜你! 你的Python包现在已经发布到 PyPI,全世界的Python用户都可以通过 pip install mytoolpack 来安装和使用它了!

  • 选择一个独特的包名:pyproject.toml 中选择 name 之前,先去PyPI.org 搜索一下,确保你选择的名字还没有被占用。
  • 优秀的 README.md 这是用户了解你项目的第一扇窗,确保它清晰、详尽。
  • 包含 LICENSE 文件: 选择一个合适的开源许可证,并在 pyproject.toml 中声明它。
  • 版本控制: 遵循语义化版本(例如 1.0.1, 1.1.0, 2.0.0),并在每次发布新版本时更新 pyproject.toml (以及 __init__.py 中的 __version__)。
  • 持续集成/持续部署 (CI/CD): 对于更成熟的项目,可以考虑设置CI/CD流水线 (如 GitHub Actions, GitLab CI)来自动化测试、构建和发布流程。

将 Python 项目打包并发布到PyPI可能看起来有些复杂,但一旦你掌握了现代化的工具和流程,它就会变得非常直接。通过使用 pyproject.toml 进行清晰的配置,利用 build 工具构建,借助TestPyPI 进行安全测试,以及通过 twine 上传,你的代码就在PyPi中被别人使用。