构建配置#
pdm
使用 PEP 517 来构建包。构建后端驱动构建系统从任意源树构建源分布和轮子。
pdm
还附带了自己的构建后端 pdm-pep517
。除了 PEP 621项目元数据,它还读取存储在 [tool.pdm.build]
中的其他配置来控制构建行为。要使用它,请在 pyproject.toml
中包含以下内容(如果您使用 pdm init
或 pdm import
来创建文件,它将自动完成):
[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"
备注
本文档的以下部分假设您使用的是上面提到的 pdm-pep517
后端。不同的后端会有不同的配置。
动态版本#
pdm-pep517
支持两个源码的动态版本。要启用动态版本控制,请记住在 PEP 621 元数据的 dynamic
字段中包含 version
:
[project]
...
dynamic = ["version"]
来自文件的动态版本#
[tool.pdm]
version = { source = "file", path = "mypackage/__version__.py" }
后端将在给定文件中搜索模式 __version__ = "{version}"
,并使用该值作为版本。
小技巧
多亏了 TOML 语法,上面的例子等价于以下内容:
[tool.pdm.version]
source = "file"
path = "mypackage/__version__.py"
或者
[tool.pdm]
version.source = "file"
version.path = "mypackage/__version__.py"
来自 SCM 的动态版本#
如果您使用过 setuptools-scm
,就会熟悉这种方法。pdm-pep517
还可以从 SCM 存储库的标记中读取版本:
[tool.pdm]
version = { source = "scm" }
手动指定版本#
在构建包时,pdm-pep517
将要求 SCM 可用来填充版本。如果不是这样,您仍然可以使用环境变量 PDM_PEP517_SCM_VERSION
指定版本:
export PDM_PEP517_SCM_VERSION="1.2.3"
pdm build
将版本写入文件#
对于从 SCM 读取的动态版本,在构建轮子时将评估值写入文件将很有帮助,这样您就不需要 importlib.metadata
来获取代码中的版本。
[tool.pdm.version]
source = "scm"
write_to = "mypackage/__version__.py"
write_template = "__version__ = '{}'" # optional, default to "{}"
对于源代码发行版,版本将被 冻结 并转换为 pyproject.toml
文件中的静态版本,它将包含在发行版中。
包含和排除文件#
指定包括和排除文件的方法很简单,它们是以 glob 模式的列表形式给出的:
[tool.pdm.build]
includes = [
"**/*.json",
"mypackage/",
]
excludes = [
"mypackage/_temp/*"
]
备注
当使用 includes
时,默认的 includes
将被覆盖。您必须手动添加包路径。
如果你希望某些文件只包含在 sdist 中,你可以使用 source-includes
字段。
[tool.pdm.build]
includes = [...]
excludes = [...]
source-includes = ["tests/"]
注意,在 source-includes
中定义的文件将在非 dist 构建中自动排除。
包括和排除的默认值#
如果你没有指定任何这些字段,PDM 也提供智能默认值,以适应最常见的工作流程。
顶级包将被包括在内。
tests
包将被排除在 非 sdist 的构建之外。src
目录将被检测为package-dir
,如果它存在的话。
如果你的项目遵循上述惯例,你不需要配置任何这些字段,它就能工作。请注意 PDM 不会自动添加 PEP 420 隐式命名空间包,它们应该总是在 includes
中明确指定。
选择另一个软件包目录来寻找软件包#
与 setuptools
的 package_dir
设置类似,人们可以在 pyproject.toml
中轻松指定另一个软件包目录,如 src
。
[tool.pdm.build]
package-dir = "src"
如果没有给出软件包目录,PDM 也可以识别 src
作为 package-dir
,如果:
src/__init__.py
不存在,意味着它不是一个有效的 Python 包,并且src/*
下存在一些软件包。
隐式命名空间包#
正如 PEP 420 中规定的那样,如果一个目录被确认为一个命名空间包,那么:
<package>/__init__.py
不存在,并且在
<package>/*
下存在正常的包和/或其他命名空间的包,并且<package>
被明确地列在了includes
中
自定义文件生成#
在构建过程中,您可能希望生成其他文件或从 internet 下载资源。你可以通过 setup-script
构建配置来实现:
[tool.pdm.build]
setup-script = "build.py"
在 build.py
脚本中,pdm-pep517
查找 build
函数并使用两个参数调用它:
src
: (str) 源目录的路径dst
: (str) 分发目录的路径
示例:
# build.py
def build(src, dst):
target_file = os.path.join(dst, "mypackage/myfile.txt")
os.makedirs(os.path.dirname(target_file), exist_ok=True)
download_file_to(dst)
生成的文件将被复制到具有相同层次结构的结果轮子中,如果需要,您需要创建父目录。
构建平台特定的轮子#
setup-script
也可以用来构建特定于平台的轮子,比如 C 扩展。目前,构建 C 扩展仍然依赖于 setuptools
。
在 setup-script
下设置 run-setuptools = true
,pdm-pep517
将生成 setup.py
,其中包含脚本中的自定义 build
函数,然后运行 python setup.py build
来构建轮子和任何扩展:
# pyproject.toml
[tool.pdm.build]
setup-script = "build_setuptools.py"
run-setuptools = true
在 setup-script
本中,预期的 build
函数接收要传递给 setuptools.setup()
调用的参数字典。在函数中,您可以根据需要使用任何附加值或更改值更新关键字字典。
下面是取自 MarkupSafe
的例子:
# build_setuptools.py
from setuptools import Extension
ext_modules = [
Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])
]
def build(setup_kwargs):
setup_kwargs.update(ext_modules=ext_modules)
如果您运行 pdm build
(或任何其他构建前端,如 build), pdm
将构建特定于平台的轮子文件以及 sdist。
默认情况下,每次构建都是在一个干净和隔离的环境中进行,只有构建需求可以被看到。如果你的构建有依赖项目环境的可选需求,你可以通过 pdm build --no-isolation
关闭环境隔离,或者将配置 build_isolation
设置为假值。
重写 “Is-Purelib” 值#
如果未指定此值,如果 run-setuptools
为 true
,pdm-pep517
将构建特定于平台的轮子。
有时您可能希望构建特定于平台的轮子,但没有构建脚本(二进制文件可能由其他工具构建或获取)。在这种情况下,你可以在 pyproject.toml
中设置 is-purelib
值为 false
:
[tool.pdm.build]
is-purelib = false
可编辑的构建后端#
PDM 利用 PEP 660 来建立可编辑安装的轮子。人们可以从这两种方法中选择如何生成轮子:
path
(默认):setuptools 使用的传统方法,在软件包路径下创建.pth
文件。editables
: 在软件包路径下创建代理模块。由于代理模块是在运行时查找的,因此它可能无法与某些静态分析工具一起工作。
请阅读 PEP,了解这两种方法的区别以及它们的工作原理。
在 pyproject.toml
中指定该方法,如下所示:
[tool.pdm.build]
editable-backend = "path"
editables
后端更值得推荐,但有一个已知的限制,即它不能与 PEP 420 命名空间包一起工作。所以在这种情况下,你需要改成 path
。
关于 Python 2 的兼容性
由于 PDM 管理的项目的构建后端需要 Python>=3.6,如果 Python 2 被用作主机解释器,你将不能安装当前项目。你仍然可以安装其他没有 PDM 支持的依赖项。
使用其他 PEP 517 后端#
除了 pdm-pep517
之外,pdm
还可以与任何读取 PEP 621 元数据的 PEP 517 构建后端一起使用。在撰写本文时,flit
(后端 flit-core
)和 hatch
(后端 hatchling
)与 PEP 621 一起工作得很好,setuptools
有实验支持。要使用其中一个,你可以在 pyproject.toml
中指定后端:
[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"
当执行 pdm build
时,PDM 将调用正确的后端。