打包和分发项目¶
Packaging and distributing projects
- 页面状态:
过时(Outdated)
- 最后更新:
2023-12-14
本节介绍了使用 setuptools
配置、打包和分发 Python 项目的一些额外细节,这些内容没有包含在 打包Python项目 的入门教程中。它假定您已经熟悉 安装软件包 页面中的内容。
本节的目标 不是 涵盖 Python 项目开发的最佳实践。例如,它不提供关于版本控制、文档编写或测试的指导或工具推荐。
有关更多参考资料,请参见 构建和分发包 中的 Setuptools 文档,但请注意,其中的一些建议内容可能已经过时。如有冲突,请优先参考 Python 打包用户指南中的建议。
This section covers some additional details on configuring, packaging and
distributing Python projects with setuptools
that aren't covered by the
introductory tutorial in 打包Python项目. It still assumes
that you are already familiar with the contents of the
安装软件包 page.
The section does not aim to cover best practices for Python project development as a whole. For example, it does not provide guidance or tool recommendations for version control, documentation, or testing.
For more reference material, see Building and Distributing Packages in the Setuptools docs, but note that some advisory content there may be outdated. In the event of conflicts, prefer the advice in the Python Packaging User Guide.
打包和分发的要求¶
Requirements for packaging and distributing
First, make sure you have already fulfilled the requirements for installing packages.
Install "twine" [1]:
python3 -m pip install twine
py -m pip install twine
You'll need this to upload your project distributions to PyPI (see below).
配置您的项目¶
Configuring your project
初始文件¶
Initial files
setup.py¶
最重要的文件是 setup.py
,它位于项目目录的根目录下。示例请参见 PyPA 示例项目 中的 setup.py。
setup.py
主要有两个功能:
它是配置项目各个方面的文件。
setup.py
的主要特点是它包含一个全局的setup()
函数。该函数的关键字参数定义了项目的具体细节。最相关的参数将在 下面的部分 中进行解释。它是一个命令行接口,用于运行与打包任务相关的各种命令。要查看可用命令的列表,请运行
python3 setup.py --help-commands
。
The most important file is setup.py
which exists at the root of your
project directory. For an example, see the setup.py in the PyPA
sample project.
setup.py
serves two primary functions:
It's the file where various aspects of your project are configured. The primary feature of
setup.py
is that it contains a globalsetup()
function. The keyword arguments to this function are how specific details of your project are defined. The most relevant arguments are explained in the section below.It's the command line interface for running various commands that relate to packaging tasks. To get a listing of available commands, run
python3 setup.py --help-commands
.
setup.cfg¶
setup.cfg
is an ini file that contains option defaults for
setup.py
commands. For an example, see the setup.cfg in the PyPA
sample project.
README.rst / README.md¶
所有项目应包含一个涵盖项目目标的自述文件。最常见的格式是 reStructuredText,其扩展名为 "rst",尽管这不是强制要求;也支持多种变体的 Markdown 格式(请查看 setup()
函数的 long_description_content_type 参数)。
备注
使用 Setuptools 0.6.27+ 的项目默认在源分发包中包含标准的自述文件(README.rst
、README.txt
或 README
)。内置的 distutils 库从 Python 3.7 开始采用这一行为。此外,Setuptools 36.4.0+ 版本会包含找到的 README.md
文件。如果您使用的是 setuptools,则不需要在 MANIFEST.in
中列出自述文件,否则请显式地将其包括在内。
All projects should contain a readme file that covers the goal of the project.
The most common format is reStructuredText with an "rst" extension, although
this is not a requirement; multiple variants of Markdown are supported as well (look
at setup()
's long_description_content_type argument).
For an example, see README.md from the PyPA sample project.
备注
Projects using Setuptools 0.6.27+ have standard readme files
(README.rst
, README.txt
, or README
) included in
source distributions by default. The built-in distutils library adopts
this behavior beginning in Python 3.7. Additionally, Setuptools
36.4.0+ will include a README.md
if found. If you are using
setuptools, you don't need to list your readme file in MANIFEST.in
.
Otherwise, include it to be explicit.
MANIFEST.in¶
当您需要打包源分发包中未自动包含的其他文件时,您需要一个 MANIFEST.in
文件。有关如何编写 MANIFEST.in
文件的详细信息,包括默认包含的文件列表,请参见 "使用 MANIFEST.in"。
然而,您可能不需要使用 MANIFEST.in
文件。例如, PyPA 示例项目 已删除其清单文件,因为所有必要的文件都已被 Setuptools 43.0.0 及更高版本自动包含。
备注
MANIFEST.in
不影响二进制分发包,如 wheel。
A MANIFEST.in
is needed when you need to package additional files that
are not automatically included in a source distribution. For details on
writing a MANIFEST.in
file, including a list of what's included by
default, see "Using MANIFEST.in".
However, you may not have to use a MANIFEST.in
. For an example, the PyPA
sample project has removed its manifest
file, since all the necessary files have been included by Setuptools 43.0.0
and newer.
备注
MANIFEST.in
does not affect binary distributions such as wheels.
LICENSE.txt¶
每个包都应包含一个许可证文件,详细说明分发条款。在许多法域中,没有明确许可证的包不能被除版权所有者以外的任何人合法使用或分发。如果您不确定选择哪种许可证,可以使用诸如 GitHub的选择许可证 等资源,或咨询律师。
例如,您可以查看 PyPA 示例项目 中的 LICENSE.txt 文件。
Every package should include a license file detailing the terms of distribution. In many jurisdictions, packages without an explicit license can not be legally used or distributed by anyone other than the copyright holder. If you're unsure which license to choose, you can use resources such as GitHub's Choose a License or consult a lawyer.
For an example, see the LICENSE.txt from the PyPA sample project.
<您的包>¶
<your package>
Although it's not required, the most common practice is to include your Python modules and packages under a single top-level package that has the same name as your project, or something very close.
For an example, see the sample package that's included in the PyPA sample project.
setup() 参数¶
setup() args
如上所述, setup.py
的主要特性是它包含一个全局的 setup()
函数。该函数的关键字参数用于定义项目的具体细节。
其中一些参数在下面暂时解释,直到它们的信息移至其他地方。完整的列表可以在 setuptools 文档 中找到。
大部分代码片段来自于 PyPA 示例项目 中的 setup.py。
有关如何使用版本来传达兼容性信息给用户的更多信息,请参见 选择版本控制方案。
As mentioned above, the primary feature of setup.py
is that it contains
a global setup()
function. The keyword arguments to this function are how
specific details of your project are defined.
Some are temporarily explained below until their information is moved elsewhere. The full list can be found in the setuptools documentation.
Most of the snippets given are taken from the setup.py contained in the PyPA sample project.
See 版本控制 for more information on ways to use versions to convey compatibility information to your users.
packages
¶
packages=find_packages(include=['sample', 'sample.*']),
将 packages
设置为项目中所有 包 的列表,包括它们的子包、子子包等。尽管可以手动列出这些包,但 setuptools.find_packages()
可以自动找到它们。使用 include
关键字参数仅查找指定的包。使用 exclude
关键字参数排除不打算发布和安装的包。
packages=find_packages(include=['sample', 'sample.*']),
Set packages
to a list of all packages in your
project, including their subpackages, sub-subpackages, etc. Although the
packages can be listed manually, setuptools.find_packages()
finds them
automatically. Use the include
keyword argument to find only the given
packages. Use the exclude
keyword argument to omit packages that are not
intended to be released and installed.
py_modules
¶
py_modules=["six"],
如果您的项目包含任何不属于包的单文件 Python 模块,请将 py_modules
设置为模块名称的列表(不包含 .py
扩展名),以便让 Setuptools 知道它们。
py_modules=["six"],
If your project contains any single-file Python modules that aren't part of a
package, set py_modules
to a list of the names of the modules (minus the
.py
extension) in order to make Setuptools aware of them.
install_requires
¶
install_requires=['peppercorn'],
install_requires
应用于指定一个项目运行所需的最小依赖。当通过 pip 安装项目时,依赖项将根据此规范进行安装。
有关使用 install_requires
的更多信息,请参见 install_requires 与 Requirements 文件。
install_requires=['peppercorn'],
"install_requires" should be used to specify what dependencies a project minimally needs to run. When the project is installed by pip, this is the specification that is used to install its dependencies.
For more on using "install_requires" see install_requires 与需求文件.
package_data
¶
package_data={
'sample': ['package_data.dat'],
},
通常,额外的文件需要安装到 package 中。这些文件通常是与包的实现紧密相关的数据,或是包含文档的文本文件,可能对使用该包的程序员有用。这些文件被称为 "包数据"。
值必须是从包名到应复制到包中的相对路径名的映射。路径被解释为相对于包含该包的目录。
有关更多信息,请参见 包括数据文件,以及 setuptools 文档。
package_data={
'sample': ['package_data.dat'],
},
Often, additional files need to be installed into a package. These files are often data that’s closely related to the package’s implementation, or text files containing documentation that might be of interest to programmers using the package. These files are called "package data".
The value must be a mapping from package name to a list of relative path names that should be copied into the package. The paths are interpreted as relative to the directory containing the package.
For more information, see Including Data Files from the setuptools docs.
data_files
¶
data_files=[('my_data', ['data/data_file'])],
虽然配置 包数据 足以满足大多数需求,但在某些情况下,您可能需要将数据文件 放置在 包 外部。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
选项。
data_files=[('my_data', ['data/data_file'])],
Although configuring package_data is sufficient for most needs, in some
cases you may need to place data files outside of your packages. The data_files
directive allows you to do that.
It is mostly useful if you need to install files which are used by other
programs, which may be unaware of Python packages.
Each (directory, files)
pair in the sequence specifies the installation
directory and the files to install there. The directory
must be a relative
path (although this may change in the future, see
wheel Issue #92),
and it is interpreted relative to the installation prefix
(Python’s sys.prefix
for a default installation;
site.USER_BASE
for a user installation).
Each file name in files
is interpreted relative to the setup.py
script at the top of the project source distribution.
For more information see the distutils section on Installing Additional Files.
备注
When installing packages as egg, data_files
is not supported.
So, if your project uses Setuptools, you must use pip
to install it. Alternatively, if you must use python setup.py
,
then you need to pass the --old-and-unmanageable
option.
scripts
¶
选择版本控制方案¶
Choosing a versioning scheme
在“开发模式”下工作¶
Working in "development mode"
你可以在开发过程中以“可编辑”或“开发”模式安装项目。 当项目以可编辑模式安装时,你可以在不重新安装的情况下直接编辑项目:对安装为可编辑模式的项目中的 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 File 部分。有关 VCS 安装的更多信息,请参阅 pip 文档中的 VCS Support 部分。
最后,如果你不想安装任何依赖项,可以运行:
python3 -m pip install -e . --no-deps
有关更多信息,请参见 Development Mode 部分,在 Setuptools 文档中。
You can install a project in "editable" or "develop" mode while you're working on it. When installed as editable, a project can be edited in-place without reinstallation: changes to Python source files in projects installed as editable will be reflected the next time an interpreter process is started.
To install a Python package in "editable"/"development" mode Change directory to the root of the project directory and run:
python3 -m pip install -e .
The pip command-line flag -e
is short for --editable
, and .
refers
to the current working directory, so together, it means to install the current
directory (i.e. your project) in editable mode. This will also install any
dependencies declared with install_requires
and any scripts declared with
console_scripts
. Dependencies will be installed in the usual, non-editable
mode.
You may want to install some of your dependencies in editable mode as well. For example, supposing your project requires "foo" and "bar", but you want "bar" installed from VCS in editable mode, then you could construct a requirements file like so:
-e .
-e bar @ git+https://somerepo/bar.git
The first line says to install your project and any dependencies. The second line overrides the "bar" dependency, such that it's fulfilled from VCS, not PyPI.
If, however, you want "bar" installed from a local directory in editable mode, the requirements file should look like this, with the local paths at the top of the file:
-e /path/to/project/bar
-e .
Otherwise, the dependency will be fulfilled from PyPI, due to the installation order of the requirements file. For more on requirements files, see the Requirements File section in the pip docs. For more on VCS installs, see the VCS Support section of the pip docs.
Lastly, if you don't want to install any dependencies at all, you can run:
python3 -m pip install -e . --no-deps
For more information, see the Development Mode section of the Setuptools docs.
打包您的项目¶
Packaging your project
要使你的项目可以从像 PyPI 这样的 包索引 (Package Index)进行安装,你需要为你的项目创建一个分发 包 (Distribution Package)。
在你构建项目的 wheels 和 sdists 之前,需要先安装 build 包:
python3 -m pip install build
py -m pip install build
To have your project installable from a Package Index like PyPI, you'll need to create a Distribution (aka "Package") for your project.
Before you can build wheels and sdists for your project, you'll need to install the
build
package:
python3 -m pip install build
py -m pip install build
源代码分发¶
Source distributions
至少,你应该创建一个 源分发包 (Source Distribution,简称 "sdist"):
python3 -m build --sdist
py -m build --sdist
“源分发包”是未构建的(即它不是 已构建分发包),并且在通过 pip 安装时需要进行构建步骤。即使该分发包是纯 Python(即不包含扩展),它仍然需要一个构建步骤来从 setup.py
和/或 setup.cfg
构建安装元数据。
Minimally, you should create a Source Distribution:
python3 -m build --sdist
py -m build --sdist
A "source distribution" is unbuilt (i.e. it's not a Built
Distribution), and requires a build step when installed by pip. Even if the
distribution is pure Python (i.e. contains no extensions), it still involves a
build step to build out the installation metadata from setup.py
and/or
setup.cfg
.
Wheels¶
Wheels
你还应该为你的项目创建一个 wheel (Wheel 包)。Wheel 是一种 已构建包 (Built Distribution),可以在无需经过“构建”过程的情况下直接安装。与从源分发包安装相比,安装 Wheel 包对于最终用户来说要快得多。
如果你的项目是纯 Python 项目,那么你将创建一个 纯 Python Wheel (参见下文部分)。
如果你的项目包含编译扩展,那么你将创建一个所谓的 平台 Wheel (参见下文部分)。
备注
如果你的项目同时支持 Python 2 且 不包含 C 扩展,那么你应该通过在 setup.cfg
文件中添加以下内容来创建所谓的 通用 Wheel:
[bdist_wheel]
universal=1
只有在你的项目没有任何 C 扩展且支持 Python 2 和 3 时,才使用此设置。
You should also create a wheel for your project. A wheel is a built package that can be installed without needing to go through the "build" process. Installing wheels is substantially faster for the end user than installing from a source distribution.
If your project is pure Python then you'll be creating a "Pure Python Wheel" (see section below).
If your project contains compiled extensions, then you'll be creating what's called a *Platform Wheel* (see section below).
备注
If your project also supports Python 2 and contains no C extensions,
then you should create what's called a Universal Wheel by adding the
following to your setup.cfg
file:
[bdist_wheel]
universal=1
Only use this setting if your project does not have any C extensions and supports Python 2 and 3.
纯 Python Wheels¶
Pure Python Wheels
纯 Python Wheel 不包含任何编译扩展,因此只需要一个单独的 Python Wheel。
要构建 Wheel 包:
python3 -m build --wheel
py -m build --wheel
wheel 包将会检测到代码是纯 Python,并构建一个名为可以在任何 Python 3 安装上使用的 Wheel 包。有关 Wheel 文件命名的详细信息,请参见 PEP 425。
如果你在没有指定 --wheel
或 --sdist
的情况下运行 build,它将同时为你构建这两个文件;这在你不需要多个 Wheel 包时很有用。
Pure Python Wheels contain no compiled extensions, and therefore only require a single Python wheel.
To build the wheel:
python3 -m build --wheel
py -m build --wheel
The wheel
package will detect that the code is pure Python, and build a
wheel that's named such that it's usable on any Python 3 installation. For
details on the naming of wheel files, see PEP 425.
If you run build
without --wheel
or --sdist
, it will build both
files for you; this is useful when you don't need multiple wheels.
平台 Wheels¶
Platform Wheels
平台 Wheel 是针对特定平台(如 Linux、macOS 或 Windows)的 Wheel,通常由于包含编译扩展而与平台相关。
要构建平台特定的 Wheel:
python3 -m build --wheel
py -m build --wheel
wheel 包将检测到代码不是纯 Python,并构建一个平台特定的 Wheel 文件,其命名方式使得它只能在构建时使用的平台上安装。有关 Wheel 文件命名的详细信息,请参见 PEP 425。
备注
当前, PyPI (Python 包索引)支持上传 Windows、macOS 和多发行版的 manylinux*
ABI 平台 Wheel。关于 manylinux*
的详细内容,请参见 PEP 513。
Platform Wheels are wheels that are specific to a certain platform like Linux, macOS, or Windows, usually due to containing compiled extensions.
To build the wheel:
python3 -m build --wheel
py -m build --wheel
The wheel
package will detect that the code is not pure Python, and build
a wheel that's named such that it's only usable on the platform that it was
built on. For details on the naming of wheel files, see PEP 425.
将您的项目上传到 PyPI¶
Uploading your Project to PyPI
当你运行命令创建分发包时,一个新的目录 dist/
会在你的项目根目录下创建。你可以在这个目录中找到你的分发文件,以便上传。
备注
这些文件只有在你运行创建分发包的命令时才会生成。这意味着,每次你更改项目源代码或更新 setup.py
文件中的配置时,都需要重新生成这些文件,才能将更改分发到 PyPI。
备注
在将包发布到主 PyPI 仓库之前,你可以先在 PyPI 测试站点 上进行测试,该站点会定期清理。请参见 使用 TestPyPI 了解如何配置以使用该站点。
警告
在其他资源中,你可能会看到使用 python setup.py register
和 python setup.py upload
的方法。这些注册和上传包的方式 强烈不推荐,因为在某些 Python 版本中,这些方法可能会使用明文 HTTP 或未验证的 HTTPS 连接,从而在传输过程中让你的用户名和密码受到拦截。
小技巧
PyPI 使用的 reStructuredText 解析器 不是 Sphinx!此外,为了确保用户安全,某些类型的 URL 和指令被禁止或会被删除(例如 .. raw::
指令)。 在 尝试上传你的分发包之前,你应该检查在 setup.py
中提供的简短描述和长描述是否有效。你可以通过运行以下命令检查包文件:
twine check dist/*
When you ran the command to create your distribution, a new directory dist/
was created under your project's root directory. That's where you'll find your
distribution file(s) to upload.
备注
These files are only created when you run the command to create your
distribution. This means that any time you change the source of your project
or the configuration in your setup.py
file, you will need to rebuild
these files again before you can distribute the changes to PyPI.
备注
Before releasing on main PyPI repo, you might prefer training with the PyPI test site which is cleaned on a semi regular basis. See 使用 TestPyPI on how to setup your configuration in order to use it.
警告
In other resources you may encounter references to using
python setup.py register
and python setup.py upload
. These methods
of registering and uploading a package are strongly discouraged as it may
use a plaintext HTTP or unverified HTTPS connection on some Python versions,
allowing your username and password to be intercepted during transmission.
小技巧
The reStructuredText parser used on PyPI is not Sphinx!
Furthermore, to ensure safety of all users, certain kinds of URLs and
directives are forbidden or stripped out (e.g., the .. raw::
directive). Before trying to upload your distribution, you should check
to see if your brief / long descriptions provided in setup.py
are
valid. You can do this by running twine check on
your package files:
twine check dist/*
创建帐户¶
Create an account
首先,你需要一个 PyPI 用户账户。你可以通过 PyPI 网站上的注册表单 创建一个账户。
接下来,你需要创建一个 PyPI API 令牌 以便能够安全地上传你的项目。
访问 https://pypi.org/manage/account/#api-tokens 并创建一个新的 API 令牌 ;不要将其范围限制为某个特定项目,因为你正在创建一个新的项目。
在复制和保存令牌之前不要关闭页面——令牌仅显示一次,无法再次查看。
注意
为避免每次上传时都需要复制和粘贴令牌,你可以创建一个 $HOME/.pypirc
文件:
[pypi]
username = __token__
password = <令牌值,包括 `pypi-` 前缀>
请注意,这会将你的令牌以明文方式存储。
有关更多详细信息,请参阅 .pypirc
的 规范。
First, you need a PyPI user account. You can create an account using the form on the PyPI website.
Now you'll create a PyPI API token so you will be able to securely upload your project.
Go to https://pypi.org/manage/account/#api-tokens and create a new API token; don't limit its scope to a particular project, since you are creating a new project.
Don't close the page until you have copied and saved the token — you won't see that token again.
备注
To avoid having to copy and paste the token every time you
upload, you can create a $HOME/.pypirc
file:
[pypi]
username = __token__
password = <the token value, including the `pypi-` prefix>
Be aware that this stores your token in plaintext.
For more details, see the specification for .pypirc
.
上传您的分发¶
Upload your distributions
一旦你拥有了一个账户,你可以使用 twine 将你的分发包上传到 PyPI。
上传发布的过程对于是否已经在 PyPI 上存在该项目是相同的——如果项目尚不存在,它将在第一次上传发布时自动创建。
对于第二次及之后的发布,PyPI 只要求新发布的版本号与任何先前的版本不同。
twine upload dist/*
你可以通过访问网址 https://pypi.org/project/<sampleproject>
来查看你的包是否已成功上传,其中 sampleproject
是你上传的项目名称。可能需要一两分钟,项目才会出现在网站上。
Once you have an account you can upload your distributions to PyPI using twine.
The process for uploading a release is the same regardless of whether or not the project already exists on PyPI - if it doesn't exist yet, it will be automatically created when the first release is uploaded.
For the second and subsequent releases, PyPI only requires that the version number of the new release differ from any previous releases.
twine upload dist/*
You can see if your package has successfully uploaded by navigating to the URL
https://pypi.org/project/<sampleproject>
where sampleproject
is
the name of your project that you uploaded. It may take a minute or two for
your project to appear on the site.