打包和分发项目#

页面状态:

已过时

上次审查:

2023-12-14

本节介绍使用 setuptools 配置、打包和分发 Python 项目的一些其他详细信息,这些详细信息未涵盖 打包 Python 项目 中的介绍性教程。它仍然假设您已经熟悉 安装包 页面中的内容。

本节旨在涵盖 Python 项目开发的最佳实践。例如,它不提供版本控制、文档或测试的指导或工具建议。

有关更多参考材料,请参阅 构建和分发包 中的 Setuptools 文档,但请注意,那里的一些建议内容可能已过时。如果发生冲突,请优先考虑 Python 打包用户指南中的建议。

打包和分发的要求#

  1. 首先,确保您已满足 安装包的要求

  2. 安装 “twine” [1]

    python3 -m pip install twine
    
    py -m pip install twine
    

您需要此项将您的项目 发行版 上传到 PyPI(请参阅 以下内容)。

配置您的项目#

初始文件#

setup.py#

最重要的文件是 setup.py,它位于项目目录的根目录中。有关示例,请参阅 setup.py 中的 PyPA 示例项目

setup.py 具有两个主要功能

  1. 它是配置项目各个方面所在的文件。 setup.py 的主要功能是它包含一个全局 setup() 函数。此函数的关键字参数是定义项目特定详细信息的方式。最相关的参数在 以下部分 中进行了解释。

  2. 它是用于运行与打包任务相关的各种命令的命令行界面。要获取可用命令的列表,请运行 python3 setup.py --help-commands

setup.cfg#

setup.cfg 是一个包含 setup.py 命令的选项默认值的 ini 文件。有关示例,请参阅 setup.cfg 中的 PyPA 示例项目

README.rst / README.md#

所有项目都应包含一个涵盖项目目标的自述文件。最常见的格式是扩展名为“rst”的 reStructuredText,尽管这不是必需的;也支持 Markdown 的多个变体(请查看 setup()long_description_content_type 参数)。

有关示例,请参阅 README.md 中的 PyPA 示例项目

注意

使用 Setuptools 0.6.27+ 的项目在默认情况下包含标准自述文件(README.rstREADME.txtREADME)在源发行版中。内置的 distutils 库从 Python 3.7 开始采用此行为。此外,Setuptools 36.4.0+ 将包含一个 README.md(如果找到)。如果您使用 setuptools,则无需在 MANIFEST.in 中列出您的自述文件。否则,请将其包含以明确说明。

MANIFEST.in#

当您需要打包未自动包含在源发行版中的其他文件时,需要 MANIFEST.in。有关编写 MANIFEST.in 文件的详细信息,包括默认情况下包含的内容列表,请参阅“使用 MANIFEST.in”。

但是,您可能不必使用 MANIFEST.in。例如,PyPA 示例项目 已删除其清单文件,因为所有必要的文件都已包含在 Setuptools 43.0.0 及更高版本中。

注意

MANIFEST.in 不会影响二进制发行版,例如轮子。

LICENSE.txt#

每个软件包都应包含一个详细说明分发条款的许可证文件。在许多司法管辖区,没有明确许可证的软件包不能合法地被版权持有人以外的任何人使用或分发。如果您不确定选择哪种许可证,可以使用 GitHub 的选择许可证 等资源或咨询律师。

有关示例,请参阅 LICENSE.txt 中的 PyPA 示例项目

<your package>#

虽然不是必需的,但最常见的做法是在一个与您的项目同名或非常接近的顶级软件包下包含您的 Python 模块和软件包。

有关示例,请参阅 sample 软件包,该软件包包含在 PyPA 示例项目 中。

setup() 参数#

如上所述,setup.py 的主要功能是它包含一个全局 setup() 函数。此函数的关键字参数是如何定义项目的具体详细信息。

在将信息移到其他地方之前,下面将暂时解释一些信息。可以在 setuptools 文档 中找到完整列表。

给出的大多数代码段都取自 setup.py,该文件包含在 PyPA 样本项目 中。

有关如何使用版本向用户传达兼容性信息的方法的更多信息,请参阅 版本控制

packages#

packages=find_packages(include=['sample', 'sample.*']),

packages 设置为项目中所有 的列表,包括其子包、子子包等。虽然可以手动列出包,但 setuptools.find_packages() 会自动查找它们。使用 include 关键字参数仅查找给定的包。使用 exclude 关键字参数省略不打算发布和安装的包。

py_modules#

py_modules=["six"],

如果项目包含任何不属于包的单文件 Python 模块,请将 py_modules 设置为模块名称的列表(不带 .py 扩展名),以便使 Setuptools 了解它们。

install_requires#

install_requires=['peppercorn'],

“install_requires” 应用于指定项目运行所需的最低依赖项。当项目由 pip 安装时,这是用于安装其依赖项的规范。

有关使用“install_requires”的更多信息,请参阅 install_requires 与 requirements 文件

package_data#

package_data={
    'sample': ['package_data.dat'],
},

通常,需要将其他文件安装到 中。这些文件通常是与包的实现密切相关的数据,或者包含可能对使用该包的程序员有用的文档的文本文件。这些文件称为“包数据”。

值必须是包名称到应复制到包中的相对路径名称列表的映射。路径解释为相对于包含该包的目录。

有关更多信息,请参阅 包含数据文件,该文件来自 setuptools 文档

data_files#

data_files=[('my_data', ['data/data_file'])],

虽然配置 package_data 足以满足大多数需求,但在某些情况下,您可能需要将数据文件放置在 外部data_files 指令允许您这样做。如果您需要安装其他程序使用的文件(这些程序可能不知道 Python 包),它非常有用。

序列中的每个 (directory, files) 对指定安装目录和要安装在其中的文件。 directory 必须是相对路径(虽然将来可能会更改,请参阅 wheel 问题 #92),并且它相对于安装前缀进行解释(对于默认安装,Python 的 sys.prefix;对于用户安装,site.USER_BASE)。 files 中的每个文件名相对于项目源发行版顶部的 setup.py 脚本进行解释。

有关更多信息,请参阅 安装其他文件 的 distutils 部分。

注意

将包安装为 egg 时,不支持 data_files。因此,如果你的项目使用 Setuptools,则必须使用 pip 安装它。或者,如果你必须使用 python setup.py,则需要传递 --old-and-unmanageable 选项。

scripts#

虽然 setup() 支持 scripts 关键字以指向要安装的预制脚本,但实现跨平台兼容性的推荐方法是使用 创建可执行脚本 入口点(请参阅下文)。

选择版本控制方案#

请参阅 版本控制 以了解有关常见版本方案以及如何从中进行选择的信息。

在“开发模式”下工作#

在处理项目时,可以在“可编辑”或“开发”模式下安装项目。以可编辑方式安装时,可以在不重新安装的情况下就地编辑项目:对以可编辑方式安装的项目中的 Python 源文件所做的更改将在下次启动解释器进程时反映出来。

要在“可编辑”/“开发”模式下安装 Python 包,请将目录更改为项目目录的根目录并运行

python3 -m pip install -e .

pip 命令行标志 -e--editable 的缩写,而 . 指的是当前工作目录,因此两者合起来表示以可编辑模式安装当前目录(即你的项目)。这还将安装使用 install_requires 声明的任何依赖项以及使用 console_scripts 声明的任何脚本。依赖项将以通常的非可编辑模式安装。

你可能还想以可编辑模式安装一些依赖项。例如,假设你的项目需要“foo”和“bar”,但你希望从 VCS 以可编辑模式安装“bar”,那么你可以构建一个如下所示的 requirements 文件

-e .
-e bar @ git+https://somerepo/bar.git

第一行表示安装你的项目和任何依赖项。第二行覆盖“bar”依赖项,使其从 VCS 而不是 PyPI 中获取。

但是,如果你希望从本地目录以可编辑模式安装“bar”,则 requirements 文件应如下所示,其中本地路径位于文件顶部

-e /path/to/project/bar
-e .

否则,由于 requirements 文件的安装顺序,依赖项将从 PyPI 中获取。有关 requirements 文件的更多信息,请参阅 pip 文档中的 Requirements 文件 部分。有关 VCS 安装的更多信息,请参阅 pip 文档中的 VCS 支持 部分。

最后,如果你根本不想安装任何依赖项,则可以运行

python3 -m pip install -e . --no-deps

有关更多信息,请参阅 Setuptools 文档中的 开发模式 部分。

打包你的项目#

要让你的项目可以从 软件包索引(如 PyPI)安装,你需要为你的项目创建一个 发行版(又称“软件包”)。

在为你的项目构建 wheels 和 sdists 之前,你需要安装 build 软件包

python3 -m pip install build
py -m pip install build

源发行版#

至少,你应该创建一个 源发行版

python3 -m build --sdist
py -m build --sdist

“源发行版”是未构建的(即它不是 已构建发行版),并且在通过 pip 安装时需要一个构建步骤。即使发行版是纯 Python(即不包含扩展),它仍然涉及从 setup.py 和/或 setup.cfg 构建安装元数据的一个构建步骤。

Wheels#

你还应该为你的项目创建一个 wheel。wheel 是一个 已构建软件包,无需经过“构建”过程即可安装。对于最终用户来说,安装 wheels 比从源发行版安装要快得多。

如果你的项目是纯 Python,那么你将创建一个 “纯 Python Wheel”(见下文)

如果你的项目包含已编译的扩展,那么你将创建一个所谓的 *平台 Wheel*(见下文)

注意

如果你的项目还支持 Python 2 并且 不包含 C 扩展,那么你应该通过将以下内容添加到 setup.cfg 文件中来创建一个所谓的通用 Wheel

[bdist_wheel]
universal=1

仅当你的项目没有任何 C 扩展并且支持 Python 2 和 3 时才使用此设置。

纯 Python Wheels#

纯 Python Wheels 不包含已编译的扩展,因此只需要一个 Python wheel。

要构建 wheel

python3 -m build --wheel
py -m build --wheel

wheel 软件包将检测到代码是纯 Python,并构建一个以使其可在任何 Python 3 安装上使用的名称命名的 wheel。有关 wheel 文件命名的详细信息,请参见 PEP 425

如果你运行 build 而没有 --wheel--sdist,它将为你构建这两个文件;当你不不需要多个 wheels 时,这很有用。

平台 Wheels#

平台 Wheels 是特定于某些平台(如 Linux、macOS 或 Windows)的 wheels,通常是因为它们包含已编译的扩展。

要构建 wheel

python3 -m build --wheel
py -m build --wheel

wheel 软件包将检测到代码不是纯 Python,并构建一个以使其仅可在其构建的平台上使用的名称命名的 wheel。有关 wheel 文件命名的详细信息,请参见 PEP 425

注意

PyPI 目前支持上传适用于 Windows、macOS 和多发行版 manylinux* ABI 的平台轮子。后者的详细信息在 PEP 513 中定义。

将您的项目上传到 PyPI#

当您运行命令以创建发行版时,会在项目根目录下创建一个新目录 dist/。您可以在其中找到要上传的发行版文件。

注意

仅在您运行命令以创建发行版时才会创建这些文件。这意味着,任何时候只要您更改项目的源代码或 setup.py 文件中的配置,都需要重新构建这些文件,然后才能将更改分发到 PyPI。

注意

在主 PyPI 仓库中发布之前,您可能更愿意在 PyPI 测试站点 上进行训练,该站点会定期进行清理。请参阅 使用 TestPyPI,了解如何设置配置以使用它。

警告

在其他资源中,您可能会遇到有关使用 python setup.py registerpython setup.py upload 的引用。强烈建议不要使用这些注册和上传包的方法,因为在某些 Python 版本上,它可能会使用明文 HTTP 或未经验证的 HTTPS 连接,从而允许在传输过程中拦截您的用户名和密码。

提示

PyPI 上使用的 reStructuredText 解析器不是 Sphinx!此外,为了确保所有用户的安全,某些类型的 URL 和指令是被禁止或剥离的(例如,.. raw:: 指令)。在尝试上传发行版之前,您应该检查 setup.py 中提供的简短/长描述是否有效。您可以通过对包文件运行 twine check 来执行此操作。

twine check dist/*

创建帐户#

首先,您需要一个 PyPI 用户帐户。您可以 使用 PyPI 网站上的表单创建帐户

现在,您将创建一个 PyPI API 令牌,以便能够安全地上传您的项目。

转到 https://pypi.ac.cn/manage/account/#api-tokens 并创建一个新的 API 令牌;不要将其范围限制到特定项目,因为您正在创建一个新项目。

在复制并保存令牌之前不要关闭页面——您将不再看到该令牌。

注意

为避免每次上传时都必须复制并粘贴令牌,您可以创建一个 $HOME/.pypirc 文件。

[pypi]
username = __token__
password = <the token value, including the `pypi-` prefix>

请注意,这会以明文形式存储您的令牌。

有关更多详细信息,请参阅 规范,了解 .pypirc

上传您的发行版#

拥有帐户后,您可以使用 twine 将您的发行版上传到 PyPI

上传版本的流程与项目在 PyPI 上是否存在无关——如果它还不存在,则在上传第一个版本时会自动创建它。

对于第二个及后续版本,PyPI 仅要求新版本的版本号与之前的任何版本不同。

twine upload dist/*

您可以通过导航到 URL https://pypi.ac.cn/project/<sampleproject> 来查看您的包是否已成功上传,其中 sampleproject 是您上传的项目的名称。您的项目可能需要一两分钟才能出现在网站上。