pylock.toml 规范

pylock.toml 文件格式用于指定依赖项,以实现 Python 环境中可复现的安装。

注意

此规范最初在 PEP 751 中定义。

文件名

锁定文件必须命名为 pylock.toml,或者如果需要命名锁定文件或存在多个锁定文件,则必须匹配正则表达式 r"^pylock\.([^.]+)\.toml$"(对于任何文件名,正则表达式为 r"^pylock\.([^.]+\.)?toml$")。命名文件的前缀和后缀在可能的情况下必须是小写,以便于检测和删除,例如:

if len(filename) > 11 and filename.startswith("pylock.") and filename.endswith(".toml"):
    name = filename.removeprefix("pylock.").removesuffix(".toml")

服务将自动从锁定文件中安装,并会搜索

  1. 带有服务名称的锁定文件,并进行默认安装

  2. 一个具有服务名称的依赖组的 pylock.toml 多用途文件

  3. pylock.toml 的默认安装

例如,一个名为“spam”的云主机服务将首先查找 pylock.spam.toml 进行安装,如果该文件不存在,则从 pylock.toml 进行安装,并查找名为“spam”的依赖组(如果存在)。

锁定文件应位于适合该锁定文件作用域的目录中。例如,锁定单个 pyproject.toml 会将 pylock.toml 放在同一目录中。如果锁定文件涵盖了单体仓库中的多个项目,那么 pylock.toml 文件应位于包含所有被锁定项目的目录中。

文件格式

该文件格式为 TOML

工具应以一致的方式写入其锁定文件,以最大程度地减少 diff 输出中的噪音。表中的键(包括顶级表)应以一致的顺序记录(如果需要灵感,本规范已尝试按逻辑顺序记录键)。同样,工具应以一致的顺序对数组进行排序。内联表的用法也应保持一致。

lock-version

  • 类型:字符串;值为 "1.0"

  • 必需?:是

  • 灵感Metadata-Version

  • 记录文件所遵循的文件格式版本。

  • 此 PEP 指定了初始版本——也是唯一有效值,直到标准的未来更新更改它——为 "1.0"

  • 如果工具支持主版本但不支持次版本,则在看到未知键时应发出警告。

  • 如果工具不支持主版本,则必须引发错误。

environments

  • 类型:字符串数组

  • 必需?:否

  • 灵感uv

  • 一个 环境标记 列表,锁定文件被认为与这些环境标记兼容。

  • 工具应写入互斥/不重叠的环境标记,以便于理解。

requires-python

  • 类型:字符串

  • 必需?:否

  • 灵感PDMPoetryuv

  • 指定锁定文件所支持的任何环境的最低 Python 版本兼容的 Requires-Python(即锁定文件的最低可用 Python 版本)。

extras

  • 类型:字符串数组

  • 必需?:否;默认为 []

  • 灵感Provides-Extra (多用途)

  • 此锁定文件支持的 extras 列表。

  • 锁定器可以选择不支持写入支持 extras 和依赖项组的锁定文件(即,工具可能只支持导出单用途锁定文件)。

  • 支持 extras 的工具也必须支持依赖项组。

  • 工具应显式将此键设置为空数组,以表明用于生成锁定文件的输入没有 extras(例如,pyproject.toml 文件没有 [project.optional-dependencies] 表),表明该锁定文件实际上是多用途的,即使它看起来是单用途的。

dependency-groups

  • 类型:字符串数组

  • 必需?:否;默认为 []

  • 灵感任意工具配置:[tool] 表

  • 此锁定文件公开支持的 依赖项组 列表(即用户期望可以通过工具 UI 指定的依赖项组)。

  • 锁定器可以选择不支持写入支持 extras 和依赖项组的锁定文件(即,工具可能只支持导出单用途锁定文件)。

  • 支持依赖项组的工具也必须支持 extras。

  • 工具应显式将此键设置为空数组,以表明用于生成锁定文件的输入没有依赖项组(例如,pyproject.toml 文件没有 [dependency-groups] 表),表明该锁定文件实际上是多用途的,即使它看起来是单用途的。

default-groups

  • 类型:字符串数组

  • 必需?:否;默认为 []

  • 灵感PoetryPDM

  • 表示默认应安装的合成依赖项组的名称(例如,[project.dependencies] 隐式表示的)。

  • 用于 packages.marker 需要存在此类组的情况。

  • 此键列出的组不应列在 dependency-groups 中,因为这些组不打算通过名称直接暴露给用户,而是通过安装程序的 UI。

created-by

  • 类型:字符串

  • 必需?:是

  • 灵感:在锁定文件名中包含其名称的工具

  • 记录用于创建锁定文件的工具名称。

  • 工具可以(MAY)使用 [tool] 表来记录足够详细的信息,以便推断出用于创建锁定文件的输入。

  • 工具应记录工具的规范化名称(如果它是一个 Python 包),以方便查找该工具。

[[packages]]

  • 类型:表数组

  • 必需?:是

  • 灵感PDMPoetryuv

  • 一个包含*可能*被安装的所有包的数组。

  • 包可能被列出多次,具有不同的数据,但所有要安装的包在安装时必须缩减为单个条目。

packages.name

  • 类型:字符串

  • 必需?:是

  • 灵感Name

  • 包的*规范化*名称。

packages.version

  • 类型:字符串

  • 必需?:否

  • 灵感Version

  • 包的版本。

  • 当版本已知稳定时(即指定了 sdistwheels 时),应指定版本。

  • 当无法保证版本与使用的代码一致时(即使用 源树 时),不得包含版本。

packages.marker

  • 类型:字符串

  • 必需?:否

  • 灵感PDM

  • 指定应在何时安装包的*环境标记*。

packages.requires-python

  • 类型:字符串

  • 必需?:否

  • 灵感Requires-Python

  • 包含包的 Python 版本兼容性的*版本说明符*。

[[packages.dependencies]]

  • 类型:表数组

  • 必需?:否

  • 灵感PDMPoetryuv

  • 记录 [[packages]] 中是此包直接依赖项的其他条目。

  • 每个条目都是一个表,其中包含使查找相应包的条目没有歧义所需的最小信息(例如,如果存在两个 spam 包的条目,则可以包含版本号,如 {name = "spam", version = "1.0.0"},或按来源,如 {name = "spam", vcs = { url = "..."})。

  • 工具在安装时不得使用此信息;它仅用于审计目的。

[packages.vcs]

  • 类型:表

  • 必需?:否;与 [packages.directory][packages.archive][packages.sdist][[packages.wheels]] 互斥。

  • 灵感Direct URL Data Structure

  • 记录其中包含的*源树*的版本控制系统详细信息。

  • 工具可以(MAY)选择不支持版本控制系统,无论是在锁定还是安装角度。

  • 工具可以(MAY)选择仅支持可用 VCS 类型的一个子集。

  • 工具应(SHOULD)提供一种方式让用户选择启用/禁用版本控制系统。

  • 从版本控制系统安装被认为是源自*直接 URL 引用*。

packages.vcs.type
  • 类型:字符串;支持的值在 Registered VCS 中指定。

  • 必需?:是

  • 灵感VCS URLs

  • 使用的版本控制系统的类型。

packages.vcs.url
packages.vcs.path
  • 类型:字符串

  • 必需?:如果未指定 packages.vcs.url

  • 灵感VCS URLs

  • 源树本地目录的*路径*。

  • 如果使用相对路径,则它必须相对于此文件的位置。

  • 如果路径是相对的,则可以(MAY)显式使用 POSIX 风格的路径分隔符以提高可移植性。

packages.vcs.requested-revision
  • 类型:字符串

  • 必需?:否

  • 灵感VCS URLs

  • 用户请求的分支/标签/引用/提交/修订版等。

  • 这纯粹是信息性的,用于帮助编写 Direct URL Data Structure;不得(MUST NOT)用于检出存储库。

packages.vcs.commit-id
  • 类型:字符串

  • 必需?:是

  • 灵感VCS URLs

  • 要安装的确切提交/修订版号。

  • 如果 VCS 支持基于提交哈希的修订标识符,则必须(MUST)使用该提交哈希作为 commit ID,以引用代码的不可变版本。

packages.vcs.subdirectory
  • 类型:字符串

  • 必需?:否

  • 灵感子目录中的项目

  • 源树中项目根目录所在的子目录(例如,pyproject.toml 文件的位置)。

  • 路径必须(MUST)相对于源树结构的根目录。

[packages.directory]

  • 类型:表

  • 必需?:否;与 [packages.vcs][packages.archive][packages.sdist][[packages.wheels]] 互斥。

  • 灵感本地目录

  • 记录其中包含的*源树*的本地目录详细信息。

  • 工具可以(MAY)选择不支持本地目录,无论是在锁定还是安装角度。

  • 工具应(SHOULD)提供一种方式让用户选择启用/禁用本地目录。

  • 从目录安装被认为是源自*直接 URL 引用*。

packages.directory.path
  • 类型:字符串

  • 必需?:是

  • 灵感本地目录

  • 源树所在的本地目录。

  • 如果路径是相对的,则它必须相对于锁定文件的位置。

  • 如果路径是相对的,则可以(MAY)使用 POSIX 风格的路径分隔符以提高可移植性。

packages.directory.editable
  • 类型:布尔值

  • 必需?:否;默认为 false

  • 灵感本地目录

  • 一个表示在锁定时间源树是否为可编辑安装的标志。

  • 安装程序可以(MAY)选择忽略此标志,如果用户操作或上下文使得可编辑安装不必要或不受欢迎(例如,一个容器镜像,它不会被挂载用于开发目的,而是部署到生产环境,在那里它将被视为只读)。

packages.directory.subdirectory

参见 packages.vcs.subdirectory

[packages.archive]

  • 类型:表

  • 必需?:否

  • 灵感Archive URLs

  • 一个直接引用要安装的归档文件(这可以包括 wheels 和 sdists,以及包含源树的其他归档格式)。

  • 工具可以(MAY)选择不支持归档文件,无论是在锁定还是安装角度。

  • 工具应(SHOULD)提供一种方式让用户选择启用/禁用归档文件。

  • 从归档文件安装被认为是源自*直接 URL 引用*。

packages.archive.url

参见 packages.vcs.url

packages.archive.path

参见 packages.vcs.path

packages.archive.size
  • 类型:整数

  • 必需?:否

  • 灵感uvSimple repository API

  • 归档文件的大小。

  • 工具在合理可能的情况下应(SHOULD)提供文件大小(例如,文件大小可通过 Content-Length 标头从 HEAD HTTP 请求获得)。

packages.archive.upload-time
  • 类型:datetime

  • 必需?:否

  • 灵感Simple repository API

  • 文件上传的时间。

  • 日期和时间必须(MUST)以 UTC 记录。

[packages.archive.hashes]
  • 类型:字符串表

  • 必需?:是

  • 灵感PDMPoetryuvSimple repository API

  • 一个列出文件已知哈希值的表,其中键是哈希算法,值是哈希值。

  • 该表必须(MUST)包含至少一个条目。

  • 哈希算法键应(SHOULD)为小写。

  • 应(SHOULD)始终包含至少一个来自 hashlib.algorithms_guaranteed 的安全算法(在撰写本文时,特别推荐 sha256。

packages.archive.subdirectory

参见 packages.vcs.subdirectory

packages.index

  • 类型:字符串

  • 必需?:否

  • 灵感uv

  • 来自 Simple repository API 的包索引的基本 URL,在此找到 sdist 和/或 wheels(例如,https://pypi.ac.cn/simple/)。

  • 如果可能,应(SHOULD)指定此项,以帮助生成*软件物料清单*(SBOM)或辅助查找文件(如果 URL 不再有效)。

  • 工具可以(MAY)支持从索引安装,如果特定文件的 URL 不再有效(例如,返回 404 HTTP 错误代码)。

[packages.sdist]

  • 类型:表

  • 必需?:否;与 [packages.vcs][packages.directory][packages.archive] 互斥。

  • 灵感uv

  • 包的*源分发文件名*的详细信息。

  • 工具可以(MAY)选择不支持 sdist 文件,无论是在锁定还是安装角度。

  • 工具应(SHOULD)提供一种方式让用户选择启用/禁用 sdist 文件。

packages.sdist.name
packages.sdist.upload-time

参见 packages.archive.upload-time

packages.sdist.url

参见 packages.archive.url

packages.sdist.path

参见 packages.archive.path

packages.sdist.size

参见 packages.archive.size

packages.sdist.hashes

参见 [packages.archive.hashes]

[[packages.wheels]]

packages.wheels.name
packages.wheels.upload-time

参见 packages.archive.upload-time

packages.wheels.url

参见 packages.archive.url

packages.wheels.path

参见 packages.archive.path

packages.wheels.size

参见 packages.archive.size

packages.wheels.hashes

参见 [packages.archive.hashes]

[[packages.attestation-identities]]

  • 类型:表数组

  • 必需?:否

  • 灵感Provenance objects

  • 对*此包所有*已记录文件的证明的记录。

  • 如果可用,工具应(SHOULD)包含找到的证明标识符。

  • 发布者特定的键将按原样包含在此表中(即,顶级),遵循 Index hosted attestations 的规范。

packages.attestation-identities.kind
  • 类型:字符串

  • 必需?:是

  • 灵感Provenance objects

  • 受信任发布者的唯一标识符。

[packages.tool]

  • 类型:表

  • 必需?:否

  • 灵感任意工具配置:[tool] 表

  • 用法与 pyproject.toml 规范 中的 [tool] 表类似,但位于包版本级别而不是锁定文件级别(后者也可通过 [tool] 访问)。

  • 表中记录的数据必须(MUST)是可丢弃的(即,它不得(MUST NOT)影响安装)。

[tool]

示例

lock-version = '1.0'
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'"]
requires-python = '== 3.12'
created-by = 'mousebender'

[[packages]]
name = 'attrs'
version = '25.1.0'
requires-python = '>= 3.8'

    [[packages.wheels]]
    name = 'attrs-25.1.0-py3-none-any.whl'
    upload-time = 2025-01-25T11:30:10.164985+00:00
    url = 'https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl'
    size = 63152
    hashes = {sha256 = 'c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a'}

    [[packages.attestation-identities]]
    environment = 'release-pypi'
    kind = 'GitHub'
    repository = 'python-attrs/attrs'
    workflow = 'pypi-package.yml'

[[packages]]
name = 'cattrs'
version = '24.1.2'
requires-python = '>= 3.8'
dependencies = [
    {name = 'attrs'},
]

    [[packages.wheels]]
    name = 'cattrs-24.1.2-py3-none-any.whl'
    upload-time = 2024-09-22T14:58:34.812643+00:00
    url = 'https://files.pythonhosted.org/packages/c8/d5/867e75361fc45f6de75fe277dd085627a9db5ebb511a87f27dc1396b5351/cattrs-24.1.2-py3-none-any.whl'
    size = 66446
    hashes = {sha256 = '67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0'}

[[packages]]
name = 'numpy'
version = '2.2.3'
requires-python = '>= 3.10'

    [[packages.wheels]]
    name = 'numpy-2.2.3-cp312-cp312-win_amd64.whl'
    upload-time = 2025-02-13T16:51:21.821880+00:00
    url = 'https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl'
    size = 12626357
    hashes = {sha256 = '83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d'}

    [[packages.wheels]]
    name = 'numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'
    upload-time = 2025-02-13T16:50:00.079662+00:00
    url = 'https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl'
    size = 16116679
    hashes = {sha256 = '3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe'}

[tool.mousebender]
command = ['.', 'lock', '--platform', 'cpython3.12-windows-x64', '--platform', 'cpython3.12-manylinux2014-x64', 'cattrs', 'numpy']
run-on = 2025-03-06T12:28:57.760769

安装

以下概述了从锁定文件安装的步骤(虽然要求是规定性的,但一般步骤和顺序是建议性的)。

  1. 收集要安装的 extras 和依赖项组,并分别设置 extrasdependency_groups 以进行标记评估。

    1. extras 默认应(SHOULD)设置为空集。

    2. dependency_groups 默认应(SHOULD)是根据 default-groups 创建的集合。

  2. 检查 lock-version 指定的元数据版本是否受支持;必须(MUST)酌情引发错误或警告。

  3. 如果指定了 requires-python,请检查正在安装的环境是否满足要求;如果不满足,则必须(MUST)引发错误。

  4. 如果指定了 environments,请检查是否至少有一个环境标记表达式得到满足;如果不满足任何表达式,则必须(MUST)引发错误。

  5. 对于 [[packages]] 中列出的每个包:

    1. 如果指定了 packages.marker,请检查它是否得到满足;如果未满足,则跳到下一个包。

    2. 如果指定了 packages.requires-python,请检查它是否得到满足;如果不满足,则必须(MUST)引发错误。

    3. 检查是否没有其他冲突的包实例已被计划安装;否则,必须(MUST)引发关于歧义的错误。

    4. 检查包的来源是否已适当地指定(即,在包条目中没有冲突的来源);如果发现任何问题,则必须(MUST)引发错误。

    5. 将包添加到要安装的包集合中。

  6. 对于每个要安装的包:

历史

  • 2025 年 4 月:初始版本,通过 PEP 751 批准。