编写你的 pyproject.toml
¶
Writing your pyproject.toml
pyproject.toml
是一个配置文件,供打包工具以及其他工具(如代码检查工具、类型检查器等)使用。此文件中有三个可能的 TOML 表格。
[build-system]
表格是 强烈推荐 使用的。它允许你声明所使用的 构建后端 以及构建项目所需的其他依赖项。[project]
表格是大多数构建后端用来指定项目基本元数据的格式,例如依赖项、你的名字等。[tool]
表格包含工具特定的子表格,例如[tool.hatch]
、[tool.black]
、[tool.mypy]
。我们这里只是简要提到这个表格,因为其内容由每个工具定义。请参考特定工具的文档,了解它可能包含的内容。
备注
[build-system]
表格应该始终存在,无论你使用的是哪个构建后端([build-system]
定义 了你使用的构建工具)。
另一方面, [project]
表格被 大多数 构建后端理解,但某些构建后端使用不同的格式。
截至 2024 年 8 月,Poetry 是一个不使用 [project]
表格的著名构建后端,它使用 [tool.poetry]
表格代替。此外,setuptools 构建后端支持 [project]
表格和 setup.cfg
或 setup.py
中的旧格式。
对于新项目,建议使用 [project]
表格,并仅在需要某些编程配置(例如构建 C 扩展)时保留 setup.py
,但 setup.cfg
和 setup.py
格式仍然有效。参见 setup.py 已被弃用吗?。
pyproject.toml
is a configuration file used by packaging tools, as
well as other tools such as linters, type checkers, etc. There are
three possible TOML tables in this file.
The
[build-system]
table is strongly recommended. It allows you to declare which build backend you use and which other dependencies are needed to build your project.The
[project]
table is the format that most build backends use to specify your project's basic metadata, such as the dependencies, your name, etc.The
[tool]
table has tool-specific subtables, e.g.,[tool.hatch]
,[tool.black]
,[tool.mypy]
. We only touch upon this table here because its contents are defined by each tool. Consult the particular tool's documentation to know what it can contain.
备注
The [build-system]
table should always be present,
regardless of which build backend you use ([build-system]
defines the
build tool you use).
On the other hand, the [project]
table is understood by most build
backends, but some build backends use a different format.
As of August 2024, Poetry is a notable build backend that does not use
the [project]
table, it uses the [tool.poetry]
table instead.
Also, the setuptools build backend supports both the [project]
table,
and the older format in setup.cfg
or setup.py
.
For new projects, use the [project]
table, and keep setup.py
only if
some programmatic configuration is needed (such as building C extensions),
but the setup.cfg
and setup.py
formats are still valid. See
setup.py 已被弃用吗?.
声明构建后端¶
Declaring the build backend
[build-system]
表格包含一个 build-backend
键,用于指定要使用的构建后端。它还包含一个 requires
键,这是一个依赖项列表,列出了构建项目所需的依赖项——通常只是构建后端包,但也可能包含其他依赖项。你还可以约束版本,例如, requires = ["setuptools >= 61.0"]
。
通常,你只需复制构建后端文档中建议的内容(参见: 选择构建后端)。以下是一些常见构建后端的配置示例:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["flit_core >= 3.4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
The [build-system]
table contains a build-backend
key, which specifies
the build backend to be used. It also contains a requires
key, which is a
list of dependencies needed to build the project -- this is typically just the
build backend package, but it may also contain additional dependencies. You can
also constrain the versions, e.g., requires = ["setuptools >= 61.0"]
.
Usually, you'll just copy what your build backend's documentation suggests (after choosing your build backend). Here are the values for some common build backends:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["flit_core >= 3.4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
静态与动态元数据¶
Static vs. dynamic metadata
本指南的其余部分将重点介绍 [project]
表格。
大多数时候,你会直接写入 [project]
字段的值。例如: requires-python = ">= 3.8"
,或 version = "1.0"
。
然而,在某些情况下,让构建后端为你计算元数据会更有用。例如:许多构建后端可以从代码中的 __version__
属性、Git 标签或类似的地方读取版本信息。在这种情况下,你应该使用如下方式将字段标记为动态:
[project]
dynamic = ["version"]
当一个字段是动态的时,填充该字段的责任就由构建后端来承担。请查阅构建后端的文档,了解它是如何处理的。
The rest of this guide is devoted to the [project]
table.
Most of the time, you will directly write the value of a [project]
field. For example: requires-python = ">= 3.8"
, or version =
"1.0"
.
However, in some cases, it is useful to let your build backend compute
the metadata for you. For example: many build backends can read the
version from a __version__
attribute in your code, a Git tag, or
similar. In such cases, you should mark the field as dynamic using, e.g.,
[project]
dynamic = ["version"]
When a field is dynamic, it is the build backend's responsibility to fill it. Consult your build backend's documentation to learn how it does it.
基本信息¶
Basic information
name
¶
在 PyPI 上注册你的项目名称。这个字段是必填的,并且是唯一一个不能标记为动态的字段。
[project]
name = "spam-eggs"
项目名称必须由 ASCII 字母、数字、下划线 "_
"、连字符 "-
" 和句点 ".
" 组成。它不能以下划线、连字符或句点开始或结束。
项目名称的比较不区分大小写,并且将连续的下划线、连字符和/或句点视为相等。例如,如果你注册了一个名为 cool-stuff
的项目,用户将能够通过以下任何一种拼写方式下载它或声明对其的依赖:Cool-Stuff
、cool.stuff
、COOL_STUFF
、CoOl__-.-__sTuFF
。
Put the name of your project on PyPI. This field is required and is the only field that cannot be marked as dynamic.
[project]
name = "spam-eggs"
The project name must consist of ASCII letters, digits, underscores "_
",
hyphens "-
" and periods ".
". It must not start or end with an
underscore, hyphen or period.
Comparison of project names is case insensitive and treats arbitrarily long runs
of underscores, hyphens, and/or periods as equal. For example, if you register
a project named cool-stuff
, users will be able to download it or declare a
dependency on it using any of the following spellings: Cool-Stuff
,
cool.stuff
, COOL_STUFF
, CoOl__-.-__sTuFF
.
version
¶
填写你项目的版本。
[project]
version = "2020.0.0"
一些更复杂的版本说明符,如 2020.0.0a1
(表示一个 alpha 版本),也是可以的;详细信息请参阅 指定版本 。
此字段是必填的,尽管它通常会标记为动态,使用如下方式:
[project]
dynamic = ["version"]
这允许使用场景,例如从 __version__
属性或 Git 标签填充版本号。有关更多详细信息,请参阅 单一来源项目版本 讨论。
Put the version of your project.
[project]
version = "2020.0.0"
Some more complicated version specifiers like 2020.0.0a1
(for an alpha
release) are possible; see the specification
for full details.
This field is required, although it is often marked as dynamic using
[project]
dynamic = ["version"]
This allows use cases such as filling the version from a __version__
attribute or a Git tag. Consult the 单一来源项目版本
discussion for more details.
依赖项和要求¶
Dependencies and requirements
dependencies
/optional-dependencies
¶
dependencies
/optional-dependencies
如果你的项目有依赖,按如下方式列出它们:
[project]
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
有关完整的版本约束语法,请参阅 指定依赖项。
如果某些依赖项仅用于包的特定功能,可以将它们设置为可选依赖。在这种情况下,将它们放在 optional-dependencies
中。
[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
"rich",
"click",
]
每个键定义一个“打包扩展”。在上面的例子中,可以使用例如 pip install your-project-name[gui]
来安装支持 GUI 的项目,并添加 PyQt5 作为依赖。
If your project has dependencies, list them like this:
[project]
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
See Dependency specifiers for the full syntax you can use to constrain versions.
You may want to make some of your dependencies optional, if they are
only needed for a specific feature of your package. In that case, put
them in optional-dependencies
.
[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
"rich",
"click",
]
Each of the keys defines a "packaging extra". In the example above, one
could use, e.g., pip install your-project-name[gui]
to install your
project with GUI support, adding the PyQt5 dependency.
requires-python
¶
requires-python
这让你声明你支持的 Python 最小版本。
[project]
requires-python = ">= 3.8"
This lets you declare the minimum version of Python that you support [#requires-python-upper-bounds]_.
[project]
requires-python = ">= 3.8"
创建可执行脚本¶
Creating executable scripts
要将一个命令作为你包的一部分安装,请在 [project.scripts]
表中声明它。
[project.scripts]
spam-cli = "spam:main_cli"
在这个例子中,安装你的项目后, spam-cli
命令将可用。执行该命令相当于执行 from spam import main_cli; main_cli()
。
在 Windows 上,这种方式打包的脚本需要一个终端,因此,如果你从图形应用程序中启动它们,它们会弹出一个终端窗口。为了防止这种情况发生,可以使用 [project.gui-scripts]
表,而不是 [project.scripts]
。
[project.gui-scripts]
spam-gui = "spam:main_gui"
在这种情况下,从命令行启动脚本时将立即返回控制,将脚本置于后台运行。
[project.scripts]
和 [project.gui-scripts]
之间的区别仅在 Windows 上相关。
To install a command as part of your package, declare it in the
[project.scripts]
table.
[project.scripts]
spam-cli = "spam:main_cli"
In this example, after installing your project, a spam-cli
command
will be available. Executing this command will do the equivalent of
from spam import main_cli; main_cli()
.
On Windows, scripts packaged this way need a terminal, so if you launch
them from within a graphical application, they will make a terminal pop
up. To prevent this from happening, use the [project.gui-scripts]
table instead of [project.scripts]
.
[project.gui-scripts]
spam-gui = "spam:main_gui"
In that case, launching your script from the command line will give back control immediately, leaving the script to run in the background.
The difference between [project.scripts]
and
[project.gui-scripts]
is only relevant on Windows.
关于您的项目¶
About your project
description
¶
readme
¶
这是你项目的更长描述,将显示在 PyPI 上的项目页面中。通常,你的项目会有一个 README.md
或 README.rst
文件,你只需要在这里写出该文件的文件名。
[project]
readme = "README.md"
README 的格式会根据扩展名自动检测:
README.md
→ GitHub 风格的 Markdown,
你也可以显式指定格式,如下所示:
[project]
readme = {file = "README.txt", content-type = "text/markdown"}
# 或者
readme = {file = "README.txt", content-type = "text/x-rst"}
This is a longer description of your project, to display on your project
page on PyPI. Typically, your project will have a README.md
or
README.rst
file and you just put its file name here.
[project]
readme = "README.md"
The README's format is auto-detected from the extension:
README.md
→ GitHub-flavored Markdown,README.rst
→ reStructuredText (without Sphinx extensions).
You can also specify the format explicitly, like this:
[project]
readme = {file = "README.txt", content-type = "text/markdown"}
# or
readme = {file = "README.txt", content-type = "text/x-rst"}
license
¶
这可以有两种形式。你可以将许可证放在一个文件中,通常是 LICENSE
或 LICENSE.txt
,然后在这里链接该文件:
[project]
license = {file = "LICENSE"}
或者你也可以直接写出许可证的名称:
[project]
license = {text = "MIT License"}
如果你使用的是标准的、知名的许可证,那么不需要使用这个字段。相反,你应该使用一个以 License::
开头的 分类器。 (一般来说,使用标准的、知名的许可证是一个好主意,既可以避免混淆,也因为一些组织会避免使用未获批准的许可证的软件。)
This can take two forms. You can put your license in a file, typically
LICENSE
or LICENSE.txt
, and link that file here:
[project]
license = {file = "LICENSE"}
or you can write the name of the license:
[project]
license = {text = "MIT License"}
If you are using a standard, well-known license, it is not necessary to use this
field. Instead, you should use one of the classifiers starting with License
::
. (As a general rule, it is a good idea to use a standard, well-known
license, both to avoid confusion and because some organizations avoid software
whose license is unapproved.)
keywords
¶
这将帮助 PyPI 的搜索框在用户搜索这些关键词时建议你的项目。
[project]
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
This will help PyPI's search box to suggest your project when people search for these keywords.
[project]
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
classifiers
¶
这是一个适用于你项目的 PyPI 分类器列表。查看 完整的分类器列表 。
classifiers = [
# 这个项目的成熟度如何?常见的值有:
# 3 - Alpha
# 4 - Beta
# 5 - 生产/稳定版
"Development Status :: 4 - Beta",
# 指明你的项目面向的对象
"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",
# 根据需要选择你的许可证(也见上面的 "license" 字段)
"License :: OSI Approved :: MIT License",
# 在这里指定你支持的 Python 版本。
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
]
虽然分类器列表常用于声明项目支持的 Python 版本,但这些信息仅用于 PyPI 上项目的搜索和浏览,并不会用于安装项目。要实际限制项目可以安装的 Python 版本,请使用 requires-python 参数。
要防止一个包被上传到 PyPI,可以使用特殊的 Private :: Do Not Upload
分类器。PyPI 将始终拒绝带有以 Private ::
开头的分类器的包。
A list of PyPI classifiers that apply to your project. Check the full list of possibilities.
classifiers = [
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
"Development Status :: 4 - Beta",
# Indicate who your project is intended for
"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",
# Pick your license as you wish (see also "license" above)
"License :: OSI Approved :: MIT License",
# Specify the Python versions you support here.
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
]
Although the list of classifiers is often used to declare what Python versions a project supports, this information is only used for searching and browsing projects on PyPI, not for installing projects. To actually restrict what Python versions a project can be installed on, use the requires-python argument.
To prevent a package from being uploaded to PyPI, use the special Private ::
Do Not Upload
classifier. PyPI will always reject packages with classifiers
beginning with Private ::
.
urls
¶
这是与你项目相关的 URL 列表,将显示在 PyPI 项目页面的左侧边栏。
备注
请参阅 知名标签 以查看 PyPI 和其他打包工具专门识别的标签列表,并查看 PyPI 项目的元数据文档 了解 PyPI 特定的 URL 处理方式。
[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
请注意,如果标签包含空格,则需要用引号括起来,例如:
Website = "https://example.com"
,但
"Official Website" = "https://example.com"
。
建议用户在适当的情况下使用 知名标签 作为项目的 URL,因为元数据的消费者(如包索引)可以根据这些标签调整展示方式。
例如,在以下元数据中, MyHomepage
或 "Download Link"
都不是已知标签,因此它们将原样呈现:
[project.urls]
MyHomepage = "https://example.com"
"Download Link" = "https://example.com/abc.tar.gz"
而在这段元数据中, HomePage
和 DOWNLOAD
都有已知的等效标签( homepage
和 download
),因此它们可以根据这些语义进行展示(项目的主页和外部下载位置,分别)。
[project.urls]
HomePage = "https://example.com"
DOWNLOAD = "https://example.com/abc.tar.gz"
A list of URLs associated with your project, displayed on the left sidebar of your PyPI project page.
备注
See 知名标签 for a listing of labels that PyPI and other packaging tools are specifically aware of, and PyPI's project metadata docs for PyPI-specific URL processing.
[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
Note that if the label contains spaces, it needs to be quoted, e.g.,
Website = "https://example.com"
but
"Official Website" = "https://example.com"
.
Users are advised to use 知名标签 for their project URLs where appropriate, since consumers of metadata (like package indices) can specialize their presentation.
For example in the following metadata, neither MyHomepage
nor
"Download Link"
is a well-known label, so they will be rendered verbatim:
[project.urls]
MyHomepage = "https://example.com"
"Download Link" = "https://example.com/abc.tar.gz"
Whereas in this metadata HomePage
and DOWNLOAD
both have
well-known equivalents (homepage
and download
), and can be presented
with those semantics in mind (the project's home page and its external
download location, respectively).
[project.urls]
HomePage = "https://example.com"
DOWNLOAD = "https://example.com/abc.tar.gz"
高级插件¶
Advanced plugins
一些包可以通过插件进行扩展。例如 Pytest 和 Pygments 。要创建这样的插件,你需要在 [project.entry-points]
的子表中声明它,如下所示:
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"
有关更多信息,请参阅 插件指南。
Some packages can be extended through plugins. Examples include Pytest
and Pygments. To create such a plugin, you need to declare it in a subtable
of [project.entry-points]
like this:
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"
See the Plugin guide for more information.
完整示例¶
A full example
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "spam-eggs"
version = "2020.0.0"
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
requires-python = ">=3.8"
authors = [
{name = "Pradyun Gedam", email = "pradyun@example.com"},
{name = "Tzu-Ping Chung", email = "tzu-ping@example.com"},
{name = "Another person"},
{email = "different.person@example.com"},
]
maintainers = [
{name = "Brett Cannon", email = "brett@example.com"}
]
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python"
]
[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
"rich",
"click",
]
[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
"Bug Tracker" = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
[project.scripts]
spam-cli = "spam:main_cli"
[project.gui-scripts]
spam-gui = "spam:main_gui"
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"