PDM 锁定目标(Lock Targets):为特定平台与 Python 版本生成独立锁文件
2026/9/16 14:13:47 网站建设 项目流程

PDM 锁定目标(Lock Targets):为特定平台与 Python 版本生成独立锁文件

【免费下载链接】pdmA modern Python package and dependency manager supporting the latest PEP standards项目地址: https://gitcode.com/GitHub_Trending/pd/pdm

PDM(Python Development Master)默认生成的锁文件会尽力兼容pyproject.tomlrequires-python声明的全部 Python 版本与所有平台,这在日常开发中非常方便:在开发环境生成一次锁文件,即可在 CI/CD 或生产环境原样复现依赖版本。但当项目存在平台相关依赖、或按 Python 版本区分的条件依赖时,单一分辨率根本无法同时满足所有环境。本文围绕docs/usage/lock-targets.md展开,系统讲解 PDM 的锁定目标(Lock Targets)机制——包括--python--platform--implementation--append等核心参数、多锁文件与单锁文件的组织策略,并结合仓库源码说明其底层解析与校验逻辑,帮助你在复杂依赖场景下正确生成、组织和使用多目标锁文件。

为什么需要锁定目标:条件依赖的解析困境

PDM 在默认情况下会生成一个“跨平台、跨 Python 版本”的锁文件,即把requires-python声明的所有 Python 版本纳入考虑范围,尝试对每个包求出一个在所有环境下都成立的版本解。开发阶段这种做法的收益很直接:锁文件只在开发机上生成一次,CI 与生产环境无需各自执行解析。

然而,一旦项目出现平台相关依赖或随 Python 版本变化的条件依赖,这种“一锁解千愁”的模式就会失效。例如:

[project] name = "myproject" requires-python = ">=3.9" dependencies = [ "numpy<1.25; python_version < '3.9'", "numpy>=1.25; python_version >= '3.9'", "pywin32; sys_platform == 'win32'", ]

这里numpy的版本选择依赖python_version,而pywin32只存在于 Windows。若试图在requires-python = ">=3.9"的全范围内对每个包求出唯一版本,几乎不可能得到一个同时满足所有平台与 Python 版本的解析结果。此时正确的做法,就是为特定的平台或 Python 版本分别生成锁文件——这正是 PDM 2.17.0 引入的锁定目标机制。

生成锁文件时指定锁定目标

pdm lock命令(定义于 src/pdm/cli/commands/lock.py)提供一组 “Lock Target” 参数,用于限定锁文件的适用环境。这些环境判据包括三类:

  • --python=<PYTHON_RANGE>:一个符合 PEP 440 规范的 Python 版本限定符。例如--python=">=3.9,<3.10"会为>=3.9<3.10的 Python 版本生成锁文件。为方便起见,--python=3.10等价于--python=">=3.10",表示面向 3.10 及以上版本解析。

  • --platform=<PLATFORM>:平台限定符。例如pdm lock --platform=linux为 Linux x86_64 平台生成锁文件。可选值包括:

    • linux
    • windows
    • macos
    • alpine
    • windows_amd64
    • windows_x86
    • windows_arm64
    • macos_arm64
    • macos_x86_64
    • macos_X_Y_arm64
    • macos_X_Y_x86_64
    • manylinux_X_Y_x86_64
    • manylinux_X_Y_aarch64
    • musllinux_X_Y_x86_64
    • musllinux_X_Y_aarch64

    从 src/pdm/models/markers.py 的EnvSpec.replace()实现可以看到,传入的 platform 字符串会经由Platform.parse()解析成结构化的平台对象;像manylinux_X_Y_x86_64macos_13_arm64这类带版本/架构的形态,可用于锁定 glibc/musl 或 macOS 的特定版本组合。

  • --implementation=cpython|pypy|pyston:Python 实现限定符,目前仅支持cpythonpypypyston三种。

三个判据可以任意组合,也可以只指定其中一部分。例如只指定--platform=linux时,生成的锁文件适用于 Linux 平台、但覆盖所有 Python 实现(Implementation不设限)。在 src/pdm/cli/commands/lock.py 中可以看到,只有--python--platform--implementation三者中至少指定一个时,才会构造env_spec;命令随后通过project.environment.allow_all_spec.replace(**replace_dict)得到具体的锁定目标EnvSpec

python 判据与 requires-python 的关系

需要特别注意的是,--python参数(以及锁文件中的requires_python判据)仍然受pyproject.tomlrequires-python的约束,两者取交集。例如requires-python>=3.9而你指定--python="<3.11",则最终的锁定目标 Python 范围是>=3.9,<3.11

这一逻辑在 src/pdm/cli/actions.py 的do_lock中有明确实现:锁定时会把project.environment.python_requires(即项目声明的requires-python)与每个目标的requires_python做交集运算,若交集为空则抛出PdmUsageError,提示“该目标要求的 Python 范围与项目的 requires-python 不兼容”;否则用合并后的范围替换目标。tests/cli/test_lock.py中的test_forbidden_lock_target_options也验证了诸如--python "<3.6"(与项目范围无交集)时命令会报错退出。

多锁定文件的组织:分文件或合并

当项目需要不止一个锁定目标时,PDM 同时支持两种组织方式:为每个目标生成独立的锁文件,或把多个目标的解析结果合并进同一个锁文件。

方式一:为每个目标生成独立锁文件

# 为 Linux 平台和 Python 3.9 生成锁文件,结果写入 py38-linux.lock pdm lock --platform=linux --python="==3.9.*" --lockfile=py38-linux.lock

之后在 Linux + Python 3.9 的环境安装依赖时,直接指定该锁文件即可:

pdm install --lockfile=py38-linux.lock

这种方式的优点在于每个锁文件职责单一、体积可控;缺点是需要手动维护多个锁文件的同步更新。此外,pdm lock也支持配合--group/--with/--without等组选择参数,为某个锁文件只锁定部分依赖组(详见 docs/usage/lockfile.md)。

方式二:用 --append 把多目标合并进同一锁文件

# 为 Linux 平台和 Python 3.9 生成锁文件,并把结果追加到 pdm.lock pdm lock --platform=linux --python="==3.9.*" --append

不加--append时,新的锁定目标会覆盖原锁文件;加了--append后,新目标的解析结果会被追加到现有锁文件中,与已有目标并存。src/pdm/cli/actions.pydo_lock中有一段关键处理:追加前会先检查env_spec是否已存在于locked_repo.targets,若已存在则直接打印 “Lock target ... already exists, skip locking.” 并跳过重复解析(tests/cli/test_lock.py中的相关用例也断言了这一行为)。同时,--append--refresh--strategy-change(如-S cross_platform)等操作互斥,源码中会抛出PdmUsageError予以拒绝。

单锁文件的优势在于:更新依赖时不必逐个同步多个文件。但代价也同样明显:

  • 无法为不同的目标配置不同的锁定策略(lock strategy);
  • 更新锁的解析耗时预期会更高,因为需要为多个目标分别求解。

灵活组合:锁文件与锁定目标的多对多

每个锁文件可以包含一个或多个锁定目标,因此组织方式非常灵活:可以把某些目标合并进一个锁文件,把特定组与特定目标放进另外的独立锁文件,按需混搭。下一节的完整示例将展示这种组合用法。

完整示例:三平台两版本的项目

假设pyproject.toml内容如下:

[project] name = "myproject" requires-python = ">=3.9" dependencies = [ "numpy<1.25; python_version < '3.10'", "numpy>=1.25; python_version >= '3.10'", "pandas" ] [project.optional-dependencies] windows = ["pywin32"] macos = ["pyobjc"]

该项目的numpy按 Python 版本(3.10 为分界)选取不同版本,同时存在面向 Windows 与 macOS 的平台相关可选依赖。现在希望为 Linux、Windows、macOS 三个平台,以及 Python 3.9、3.10 两种版本生成锁文件。按顺序执行以下命令:

pdm lock --python=">=3.10" pdm lock --python="<3.10" --append pdm lock --platform=windows --python=">=3.10" --lockfile=py310-windows.lock --with windows pdm lock --platform=macos --python=">=3.10" --lockfile=py310-macos.lock --with macos

执行后共得到 3 个锁文件:

  • pdm.lock:默认主锁文件,适用于所有平台以及>=3.9范围内的 Python 版本,不包含平台相关依赖。该锁文件内包含两个版本的numpy(分别适用于 3.10 及以上与 3.10 以下),PDM 安装器会根据实际 Python 版本自动挑选正确的那个。目标合并的过程依赖--append:第二条命令把<3.10的目标追加进pdm.lock,与第一条命令的>=3.10目标并存。
  • py310-windows.lock:面向 Windows 平台与 Python 3.10 及以上,通过--with windows包含 Windows 平台的可选依赖pywin32
  • py310-macos.lock:面向 macOS 平台与 Python 3.10 及以上,通过--with macos包含 macOS 平台的可选依赖pyobjc

目标匹配与安装时的选择逻辑

上述示例中pdm.lock内含两个目标,安装时 PDM 如何选择?在 src/pdm/cli/actions.py 的安装解析流程中,PDM 会先根据当前环境构造env_spec,检查其是否精确命中locked_repo.targets;若未命中,则比较当前环境与各目标的兼容性(EnvCompatibility),优先选取不高于当前环境的宽松目标,找不到任何兼容目标时才会报错提示。这意味着锁文件中的多目标与当前环境按“最贴近”原则匹配,从而保证在 CI 的不同平台上各取所需。

锁定目标在锁文件中的表现

合并了多个目标的锁文件,会在元数据中记录目标列表。查看 src/pdm/project/lockfile/pdmlock.py 可以看到,生成锁文件时metadata中会写入"targets": [t.as_dict() for t in repository.targets],与groupsstrategylock_version等字段并列;每个目标在锁文件中以包含requires_pythonplatformimplementation等字段的形式持久化。正因为目标信息被完整记录,后续pdm installpdm syncpdm update才能依据目标列表做精确匹配与增量追加。

另外值得留意的是,旧的cross_platform锁定策略已在新版 PDM 中被标记为弃用,官方提示其被新的 lock targets 机制取代(src/pdm/cli/actions.py 中会打印弃用警告并指向本文所讲的功能文档)。在 tests/cli/test_lock.py 中,test_lock_specific_platform_wheels还验证了指定平台锁定后,锁文件中的 wheel 文件哈希列表会随平台收窄——例如同一包在 Windows 目标下可能收录 2 个 wheel,而在 Linux 目标下只收录 1 个,同时锁策略中不再包含cross_platform标志,说明解析确实按单平台收敛。

小结

锁定目标机制让 PDM 从“一锁全平台”的默认模式,进化出按平台、Python 版本与 Python 实现精细切分的解析能力。关键要点可概括为:

  1. 默认跨环境:不加任何目标参数时,PDM 在requires-python范围内尽量求一个通用解;
  2. 三类判据--python(PEP 440 范围)、--platform(含 manylinux/musllinux/macOS 细分形态)、--implementation(cpython/pypy/pyston),可组合、可省略;
  3. 交集约束--python最终范围会与requires-python取交集,冲突时报错;
  4. 两种组织方式:独立锁文件(配合--lockfile)或单文件多目标(配合--append),前者便于隔离管理,后者便于统一更新;
  5. 安装自动匹配:多目标锁文件在安装时会按当前环境选择最贴近的目标,无需人工干预。

在 CI 矩阵、多平台分发等复杂依赖场景中,善用锁定目标即可在“单文件简单性”与“多环境精确性”之间找到最适合项目的平衡点。该功能从 PDM 2.17.0 起提供,完整命令参数与行为也可通过pdm lock --help查看。

【免费下载链接】pdmA modern Python package and dependency manager supporting the latest PEP standards项目地址: https://gitcode.com/GitHub_Trending/pd/pdm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询