打包 Python 项目

本教程介绍如何打包一个简单的 Python 项目:添加创建包所需的文件和目录结构,构建包,再上传到 Python Package Index(PyPI)。

部分命令需要较新的 pip,首先确认已经安装最新版本。

Unix/macOS

python3 -m pip install --upgrade pip

Windows

py -m pip install --upgrade pip

一个简单项目

本教程使用 example_package_YOUR_USERNAME_HERE 项目。如果用户名是 me,包名就应为 example_package_me,以避免与其他读者上传的包冲突。建议先原样跟随本示例,再打包自己的项目。

在本地创建以下结构:

packaging_tutorial/
└── src/
    └── example_package_YOUR_USERNAME_HERE/
        ├── __init__.py
        └── example.py

包含 Python 文件的目录应与项目名一致,既能简化配置,也让安装包的用户更容易理解。

建议创建 __init__.py。即使文件为空,它的存在也允许用户将目录作为普通包导入。example.py 是包内模块,可以包含函数、类、常量等逻辑。打开它,写入:

def add_one(number):
    return number + 1

如果尚不熟悉 Python 模块与导入包,请先花几分钟阅读文档。完成上述结构后,本教程所有命令都应在 packaging_tutorial 目录中运行。

创建打包所需文件

接下来添加创建包所需的文件。完成后,目录结构应如下所示:

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_YOUR_USERNAME_HERE/
│       ├── __init__.py
│       └── example.py
└── tests/

创建测试目录

tests/ 用作测试文件的占位目录,目前保持为空。

选择构建后端

pip 与 build 等工具不会亲自将源代码转换为 wheel 等分发包;这项工作由构建后端完成。后端决定项目如何声明元数据与输入文件等配置;元数据包括 PyPI 上显示的名称和标签等项目信息。

不同后端支持的功能不同,例如是否支持构建扩展模块,应按需求和偏好选择。本教程默认使用 Hatchling,但对于支持 [project] 元数据表的 Setuptools、Flit、PDM 等后端,同样适用。

有些后端属于更大的工具,还提供初始化、版本管理、构建、上传和安装等统一命令行功能。本教程使用能够独立工作的单一用途工具。

pyproject.toml 告诉 pip、build 等构建前端应使用哪个后端。以下为常见后端的配置示例,详情以各后端文档为准。

Hatchling

[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"

Setuptools

[build-system]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"

Flit

[build-system]
requires = ["flit_core >= 3.12.0, <5"]
build-backend = "flit_core.buildapi"

PDM

[build-system]
requires = ["pdm-backend >= 2.4.0"]
build-backend = "pdm.backend"

uv-build

[build-system]
requires = ["uv_build >= 0.12.19, <0.13.0"]
build-backend = "uv_build"

requires 列出构建包所需的依赖,构建前端通常会自动安装它们。前端一般在隔离环境中构建,遗漏依赖可能导致构建错误。这个列表必须包含后端包,也可能包括其他构建依赖。上例中的最低版本是引入新许可证元数据支持的版本。

build-backend 是前端用来执行构建的 Python 对象名称。这两个值都由后端文档或命令行工具提供,通常无需自行定制。

其他构建设置可能位于 pyproject.toml 的 tool 部分,或构建工具指定的文件中。例如 Setuptools 还可使用 setup.py、setup.cfg;配置 setuptools.build_meta 后,构建工具就能自动找到并使用它们。

配置元数据

打开 pyproject.toml,输入以下内容。将 name 中的占位文本替换为你的用户名,避免包名冲突:

[project]
name = "example_package_YOUR_USERNAME_HERE"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
    "Programming Language :: Python :: 3",
    "Operating System :: OS Independent",
]
license = "MIT"
license-files = ["LICEN[CS]E*"]

[project.urls]
Homepage = "https://github.com/pypa/sampleproject"
Issues = "https://github.com/pypa/sampleproject/issues"
  • name 是分发包名称,只能包含字母、数字、.、_ 和 -,且不能与 PyPI 已有包重名。本教程务必加入你的用户名。
  • version 是包版本;有些后端也允许从文件或 Git 标签读取版本。
  • authors 标识作者,可分别列出姓名和邮箱;maintainers 使用同样格式。
  • description 是一句话的简短描述。
  • readme 指向包含详细说明的文件,内容显示在 PyPI 包详情页。本例读取常用的 README.md;另有更高级的表格形式,见 pyproject.toml 指南。
  • requires-python 指定支持的 Python 版本。pip 等安装器会向前查找旧包版本,直到找到匹配当前 Python 版本的版本。
  • classifiers 为索引和 pip 提供额外元数据。这里声明仅支持 Python 3、与操作系统无关。至少应注明支持的 Python 版本和操作系统;完整列表见 PyPI classifiers。
  • license 是分发归档的 SPDX 许可证表达式。
  • license-files 是许可证文件 glob 路径列表,相对于 pyproject.toml 所在目录。
  • urls 可列出在 PyPI 展示的额外链接,例如源码、文档、问题追踪地址等。

更多字段见 pyproject.toml 指南。常见的还有提高可发现性的 keywords,以及安装依赖 dependencies。

创建 README.md

打开 README.md 并输入以下内容,也可以按需修改:

# Example Package

This is a simple example package. You can use
[GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
to write your content.

创建 LICENSE

上传到 PyPI 的每个分发归档都应该包含许可证,说明用户可以在什么条件下使用它。选择许可证可参考 choosealicense.com。选定后,将许可文本写入 LICENSE。例如,若选择 MIT 许可证:

Copyright (c) 2018 The Python Packaging Authority
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

多数后端会自动将许可证文件加入包。详情以各后端文档为准。如果在 pyproject.toml 的 license-files 中列出路径,且后端支持 PEP 639,文件将自动包含在包中。

包含其他文件

上面列出的文件会自动包含在源码分发包中。若需加入其他文件,请阅读所选后端文档。

生成分发归档

下一步为项目生成分发包。它们是可上传到 PyPI 并由 pip 安装的归档文件。先安装最新 PyPA build:

Unix/macOS

python3 -m pip install --upgrade build

Windows

py -m pip install --upgrade build

安装遇到问题时,参阅安装包教程。在 pyproject.toml 所在目录运行:

Unix/macOS

python3 -m build

Windows

py -m build

命令会输出较多文本,结束后应在 dist 中生成两个文件:

dist/
├── example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
└── example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz

.tar.gz 是源码分发包,.whl 是构建分发包。新版 pip 优先安装构建分发包,必要时回退到源码分发包。应始终上传源码分发包,并为兼容的平台提供构建分发包。这里的示例兼容所有平台上的 Python,所以只需一个构建分发包。

上传分发归档

最后将包上传到 Python 包索引。先在 TestPyPI 注册账号并验证邮箱。它是专门用于测试实验的独立索引,适合本教程这种无需上传真实索引的场景。更多说明见使用 TestPyPI。

安全上传需要 PyPI API token。在 账号 token 设置中创建,原教程要求 Scope 选择 Entire account。不要在复制并安全保存 token 前关闭页面,因为它不会再次显示。

注册完成后,用 Twine 上传分发包。先安装:

Unix/macOS

python3 -m pip install --upgrade twine

Windows

py -m pip install --upgrade twine

安装后,上传 dist 下的所有归档:

Unix/macOS

python3 -m twine upload --repository testpypi dist/*

Windows

py -m twine upload --repository testpypi dist/*

系统将提示输入 API token。输入时应包含 pypi- 前缀;输入内容会隐藏,请确认粘贴正确。完成后输出类似:

Uploading distributions to https://test.pypi.org/legacy/
Enter your API token:
Uploading example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 kB • 00:01 • ?
Uploading example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.8/6.8 kB • 00:00 • ?

上传成功后,应能在 TestPyPI 查看包,例如 https://test.pypi.org/project/example_package_YOUR_USERNAME_HERE。

安装刚上传的包

用 pip 安装并验证包。先创建虚拟环境,再从 TestPyPI 安装:

Unix/macOS

python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE

Windows

py -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE

务必在包名中替换你的用户名。安装输出类似:

Collecting example-package-YOUR-USERNAME-HERE
  Downloading https://test-files.pythonhosted.org/packages/.../example_package_YOUR_USERNAME_HERE_0.0.1-py3-none-any.whl
Installing collected packages: example_package_YOUR_USERNAME_HERE
Successfully installed example_package_YOUR_USERNAME_HERE-0.0.1

还可以导入包,检查是否正确安装。确认仍在虚拟环境中,然后启动 Python:

Unix/macOS

python3

Windows

py

导入模块并调用函数:

>>> from example_package_YOUR_USERNAME_HERE import example
>>> example.add_one(2)
3

下一步

完成上述步骤后,你已经打包并分发了一个 Python 项目。记住,TestPyPI 不是永久存储,测试系统偶尔会删除包和账号,适合用于测试和实验。

准备发布真实包时,过程大致相同,但有以下区别:

  • 选择好记且独特的包名,不必附加用户名,但不能使用已有名称。
  • 在 PyPI 注册账号;测试站与正式站是两个独立服务器,不共享登录信息。
  • 运行 twine upload dist/*,输入正式 PyPI 账号的凭据。不必指定 --repository,默认上传到正式 PyPI。
  • 通过 python3 -m pip install [your-package] 从正式 PyPI 安装。

继续学习时,可以阅读 Hatchling、Setuptools、Flit、PDM 的高级配置;查看本站指南获得更多实践信息,或通过讨论了解背景。也可以考虑为项目管理和打包提供统一命令行界面的 Hatch、Flit、PDM、Poetry 等工具。

注释

技术上可以不创建 __init__.py,但这样的包称为命名空间包,属于高级话题,不在本教程范围内。刚开始学习打包时,建议使用普通包和 __init__.py,即使文件为空也保留。

原文:Packaging Python Projects,Python Packaging User Guide;Copyright © 2013–2020 PyPA;原页最后更新于2026年10月2日。依转载授权汉化,保留源码、示例输出和完整 MIT 许可示例;示例中的 MIT 声明并不替代你自己项目的许可选择。本稿未执行构建、安装或上传,未创建账号和 token。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容