返回资讯中心
外部精选
软件工程
#开发者实时流

PyPA 发布 PEP 723 规范:Python 单文件脚本可内嵌元数据

来源标题:PEP 723 – Inline script metadata

PyPA 在规范页面维护 PEP 723 的正式版本,该规范定义了一种可嵌入单文件 Python 脚本的元数据格式,用于帮助启动器、IDE 等外部工具识别和处理脚本。这类脚本通常以单个文件形式通过邮件、链接或聊天窗口分享,不会发展为带 pyproject.toml 的完整项目,此前缺乏为执行工具定义元数据的标准机制。原始 PEP 文档已作为历史文档保留。

Hacker News 最新debo_2026/11/4 · 今天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

本 PEP 是一份历史文档。最新、权威的规范《Inline script metadata》维护在 PyPA specs 页面。关于如何提出变更,请参见 PyPA 规范更新流程。

本 PEP 规定了一种元数据格式,可嵌入到单文件 Python 脚本中,以帮助启动器、IDE 以及其他可能需要与这类脚本交互的外部工具。

Python 常被用作脚本语言,Python 脚本是 shell 脚本、批处理文件等(更好的)替代方案。当 Python 代码以脚本形式组织时,通常存储为单个文件,并且不期望存在任何其他可用于导入的本地代码。因此,可以通过任意基于文本的方式与他人分享,例如电子邮件、指向脚本的 URL,甚至聊天窗口。以这种方式组织的代码可能永远作为单个文件存在,永远不会成为一个拥有自己目录和 pyproject.toml 文件的完整项目。

用户在使用这种方式时遇到的一个问题是:对于那些负责执行此类脚本的工具来说,没有标准机制来定义元数据。例如,运行脚本的工具可能需要知道需要哪些依赖项,或者支持哪些 Python 版本。

目前还没有解决这一问题的标准工具,本 PEP 也不试图定义一个。然而,任何解决这一问题的工具都需要知道脚本的运行时需求。通过定义一种存储此类元数据的标准格式,现有工具以及未来任何工具都将能够获取该信息,而无需用户在其脚本中包含特定于工具的元数据。

本 PEP 定义了一种将元数据嵌入脚本本身而非外部文件的机制。

该元数据格式在设计上类似于 Python 项目目录中 pyproject.toml 文件的数据布局,以便为有编写 Python 项目经验的用户提供熟悉的体验。通过使用类似的格式,我们避免了打包工具之间不必要的不一致,这是用户在最近的打包调查中表达的一个常见困扰。

以下是本 PEP 希望支持的一些用例:

本 PEP 定义了一种元数据注释块格式,其灵感松散地来源于 [2] reStructuredText 指令。

任何 Python 脚本都可以有顶级注释块,这些注释块必须以行 # /// TYPE 开头,其中 TYPE 决定如何处理内容。也就是说:一个 #,后跟一个空格,后跟三个正斜杠,后跟一个空格,后跟元数据类型。块必须以行 # /// 结束。也就是说:一个 #,后跟一个空格,后跟三个正斜杠。TYPE 只能由 ASCII 字母、数字和连字符组成。

这两行(# /// TYPE 和 # ///)之间的每一行都必须是始于 # 的注释。如果 # 后面有字符,则第一个字符必须是空格。嵌入内容是通过去掉每一行的前两个字符(如果第二个字符是空格),否则只去掉第一个字符(这意味着该行仅由一个 # 组成)来形成的。

结束行 # /// 的优先级在下一行不是如上所述的有效嵌入内容行时生效。例如,以下是一个单一且完全有效的块:

# /// some-toml
# embedded-csharp = """
# /// <summary>
# /// text
# ///
# /// </summary>
# public class MyClass { }
# """
# ///

起始行不得放在另一个起始行与其结束行之间。在这种情况下,工具可以产生错误。未闭合的块必须被忽略。

当定义了多个相同 TYPE 的注释块时,工具必须产生错误。

读取嵌入元数据的工具可以遵循标准的 Python 编码声明。如果它们选择不这样做,则必须将文件按 UTF-8 处理。

以下是可用于解析元数据的规范正则表达式:

(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$

在文本规范与正则表达式之间存在差异的情况下,以文本规范为准。

工具不得读取类型未经本 PEP 或未来 PEP 标准化的元数据块。

第一种元数据块名为 script,包含脚本元数据(依赖数据和工具配置)。

本文档可以包含顶级字段 dependencies 和 requires-python,并且可以选择性地包含 [tool] 表。

[tool] 表可供任何工具、脚本运行器或其他工具用来配置行为。其语义与 pyproject.toml 中的 tool 表相同。

如果无法提供指定的依赖项,脚本运行器必须报错。

如果无法提供满足指定 requires-python 的任何 Python 版本,脚本运行器应报错。

以下是一个包含嵌入元数据的脚本示例:

# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "requests<3",
#     "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

参考实现

以下是在 Python 3.11 或更高版本上读取元数据的示例。

import re
import tomllib

REGEX = r'(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$'

def read(script: str) -> dict | None:
    name = 'script'
    matches = list(
        filter(lambda m: m.group('type') == name, re.finditer(REGEX, script))
    )
    if len(matches) > 1:
        raise ValueError(f'Multiple {name} blocks found')
    elif len(matches) == 1:
        content = ''.join(
            line[2:] if line.startswith('# ') else line[1:]
            for line in matches[0].group('content').splitlines(keepends=True)
        )
        return tomllib.loads(content)
    else:
        return None

通常工具会像包管理器或依赖更新那样编辑依赖项。

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →