打包 Python 项目¶
本教程将指导您如何打包一个简单的 Python 项目。它将向您展示如何添加必要的文件和结构来创建包,如何构建包,以及如何将其上传到 Python 包索引 (PyPI)。
提示
如果您在运行本教程中的命令时遇到问题,请复制命令及其输出,然后在 GitHub 上的 packaging-problems 仓库中提出问题。我们将尽力帮助您!
有些命令需要较新版本的 pip,所以首先请确保您已安装最新版本。
python3 -m pip install --upgrade pip
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
文件,因为 __init__.py
文件的存在允许用户将目录作为常规包导入,即使(如本教程中所示)__init__.py
为空。[1]
example.py
是包中模块的一个示例,它可以包含包的逻辑(函数、类、常量等)。打开该文件并输入以下内容:
def add_one(number):
return number + 1
如果您不熟悉 Python 的模块和导入包,请花几分钟阅读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,但它与 Setuptools、Flit、PDM 以及其他支持 元数据 的 [project]
表的后端工作方式相同。
注意
一些构建后端是大型工具的一部分,这些工具提供带有额外功能(如项目初始化和版本管理)以及构建、上传和安装包的命令行界面。本教程使用独立工作的专用工具。
pyproject.toml
告诉 构建前端 工具(如 pip 和 build)为您的项目使用哪个后端。下面是一些常见构建后端的示例,但请查阅您后端自己的文档以获取更多详细信息。
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[build-system]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["flit_core >= 3.12.0, <4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["pdm-backend >= 2.4.0"]
build-backend = "pdm.backend"
[build-system]
requires = ["uv_build >= 0.9.2, <0.10.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 版本以及您的包适用的操作系统。有关分类器的完整列表,请参见 https://pypi.ac.cn/classifiers/。license
是您的包的SPDX 许可证表达式。license-files
是许可证文件的全局路径列表,相对于pyproject.toml
所在的目录。urls
允许您列出任意数量的额外链接以在 PyPI 上显示。通常这可以是源代码、文档、问题跟踪器等。
有关这些以及可以在 [project]
表中定义的其他字段的详细信息,请参阅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¶
上传到 Python 包索引的每个包都包含许可证非常重要。这告诉安装您包的用户可以使用您包的条款。有关选择许可证的帮助,请参阅 https://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,该文件将自动包含在包中。
包含其他文件¶
上面列出的文件将自动包含在您的源分发中。如果您想包含其他文件,请参阅您的构建后端文档。
生成分发归档文件¶
下一步是为包生成分发包。这些是上传到 Python 包索引并可以通过 pip 安装的归档文件。
确保您已安装最新版本的 PyPA build:
python3 -m pip install --upgrade build
py -m pip install --upgrade build
提示
如果您在安装这些时遇到问题,请参阅安装包教程。
现在从 pyproject.toml
所在的同一目录运行此命令:
python3 -m build
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 是一个单独的包索引实例,用于测试和实验。它非常适合像本教程这样的情况,我们不一定想上传到真实的索引。要注册帐户,请访问 https://test.pypi.org/account/register/ 并完成该页面上的步骤。您还需要验证您的电子邮件地址才能上传任何包。有关更多详细信息,请参阅使用 TestPyPI。
为了安全地上传您的项目,您需要一个 PyPI API 令牌。在 https://test.pypi.org/manage/account/#api-tokens 创建一个,将“范围”设置为“整个帐户”。在复制并保存令牌之前,请勿关闭页面——您将无法再次看到该令牌。
现在您已注册,可以使用 twine 上传分发包。您需要安装 Twine:
python3 -m pip install --upgrade twine
py -m pip install --upgrade twine
安装后,运行 Twine 上传 dist
下的所有归档文件:
python3 -m twine upload --repository testpypi dist/*
py -m twine upload --repository testpypi dist/*
系统会提示您输入 API 令牌。使用令牌值,包括 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 安装您的包:
python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE
py -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE
请务必在包名中指定您的用户名!
pip 应该从 TestPyPI 安装包,输出应该类似于这样:
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
注意
本示例使用 --index-url
标志来指定 TestPyPI 而不是实时的 PyPI。此外,它还指定了 --no-deps
。由于 TestPyPI 没有与实时 PyPI 相同的包,因此尝试安装依赖项可能会失败或安装一些意想不到的东西。虽然我们的示例包没有任何依赖项,但使用 TestPyPI 时避免安装依赖项是一种好习惯。
您可以通过导入包来测试它是否正确安装。确保您仍在虚拟环境中,然后运行 Python:
python3
py
并导入包:
>>> from example_package_YOUR_USERNAME_HERE import example
>>> example.add_one(2)
3
下一步¶
恭喜,您已经打包并分发了一个 Python 项目! ✨ 🍰 ✨
请记住,本教程向您展示了如何将包上传到 Test PyPI,这不是一个永久存储。测试系统偶尔会删除包和帐户。最好将 TestPyPI 用于测试和实验,例如本教程。
当您准备将真实包上传到 Python 包索引时,您可以像本教程中那样做很多事情,但有这些重要的区别:
为您的包选择一个令人难忘且唯一的名称。您不必像本教程中那样附加您的用户名,但您不能使用现有名称。
在 https://pypi.ac.cn 上注册一个帐户 - 请注意,这是两个独立的服务器,测试服务器的登录详细信息不会与主服务器共享。
使用
twine upload dist/*
上传您的包,并输入您在真实 PyPI 上注册的帐户的凭据。现在您正在生产环境中上传包,您无需指定--repository
;包将默认上传到 https://pypi.ac.cn/。使用
python3 -m pip install [your-package]
从真实的 PyPI 安装您的包。
此时,如果您想了解更多关于打包 Python 库的信息,以下是一些您可以做的事情:
阅读有关您选择的构建后端的高级配置:Hatchling、setuptools、Flit、PDM。
注释