Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 441 – 改进 Python ZIP 应用程序支持

作者:
Daniel Holth <dholth at gmail.com>, Paul Moore <p.f.moore at gmail.com>
讨论至:
Python-Dev 消息
状态:
最终版
类型:
标准跟踪
创建日期:
2013年3月30日
Python 版本:
3.5
发布历史:
2013年3月30日,2013年4月1日,2015年2月16日
决议:
Python-Dev 消息

目录

改进 Python ZIP 应用程序支持

自 Python 2.6 版本以来,Python 就具备了将目录或 ZIP 格式存档作为脚本执行的能力 [1]。当解释器以 zip 文件或目录作为其第一个参数被调用时,它会将该目录添加到 sys.path 并执行 __main__ 模块。这些存档提供了一种很好的方式来发布需要作为单个文件脚本分发但又足够复杂需要编写为模块集合的软件。

这个功能并没有它应有的那么受欢迎,主要是因为它没有作为 Python 2.6 的一部分进行推广 [2],因此相对不为人知;同时也是因为 Windows 安装程序没有为这种格式的文件注册文件扩展名(除了 .py),以与启动器关联。

本 PEP 提议通过重新宣传该功能、定义 .pyz.pyzw 扩展名分别为“Python ZIP 应用程序”和“窗口化 Python ZIP 应用程序”,并提供一些简单的工具来管理该格式,从而解决这些问题。

一个新的 Python ZIP 应用程序扩展

术语“Python Zip 应用程序”将是用于指代包含可由 Python 直接执行的代码(具体来说,它必须在存档的根目录中包含一个 __main__.py 文件)的 zip 格式存档的正式术语。.pyz 扩展名将正式与此类文件关联。

Python 3.5 安装程序将把 .pyz.pyzw“Python Zip 应用程序”与平台启动器关联,以便它们可以执行。.pyz 存档是一个控制台应用程序,而 .pyzw 存档是一个窗口化应用程序,指示在运行应用程序时是否应显示控制台。

在 Unix 上,如果 .pyz 扩展名和“Python Zip Application”名称能够注册(在 MIME 类型数据库中?),那将是理想的。然而,这种关联超出了本 PEP 的范围。

Python Zip 应用程序可以以指向正确的 Python 解释器和可选解释的 #! 行作为前缀。

#!/usr/bin/env python3
#  Python application packed with zipapp module
(binary contents of archive)

在 Unix 上,这允许操作系统通过标准的“shebang”支持使用正确的解释器运行文件。在 Windows 上,Python 启动器实现了 shebang 支持。

然而,始终可以通过直接向 Python 解释器提供文件名来执行 .pyz 应用程序。

作为背景,ZIP 存档的定义包含一个包含文件末尾相对偏移量的页脚。当它们连接到任何其他文件的末尾时,它们仍然有效。此功能完全符合标准,自解压 ZIP 存档和 bdist_wininst 安装程序格式就是这样工作的。

最小化工具:zipapp 模块

本 PEP 还提议包含一个用于处理这些存档的模块。该模块将包含用于处理 Python zip 应用程序存档的函数,以及用于创建和操作它们的命令行界面(通过 python -m zipapp)。

我们鼓励在 PyPI 上将更完整的 Python Zip 应用程序管理工具作为第三方应用程序提供。目前,pyzzer [5] 和 pex [6] 是其中两个工具。

模块接口

zipapp 模块将提供以下函数

create_archive(source, target=None, interpreter=None, main=None)

source 创建应用程序存档。source 可以是以下任何一种:

  • 目录的名称,在这种情况下,将从该目录的内容创建一个新的应用程序存档。
  • 现有应用程序存档文件的名称,在这种情况下,文件将被复制到目标。如果需要,文件名应包含 .pyz.pyzw 扩展名。
  • 以字节模式打开进行读取的文件对象。文件的内容应为应用程序存档,并且假定文件对象位于存档的开头。

target 参数决定了生成的存档将写入何处

  • 如果它是一个文件名,存档将被写入该文件。
  • 如果它是一个已打开的文件对象,存档将被写入该文件对象,该文件对象必须以字节模式打开进行写入。
  • 如果省略(或为 None)目标,则源必须是一个目录,并且目标将是一个与源同名但添加了 .pyz 扩展名的文件。

interpreter 参数指定了用于执行存档的 Python 解释器的名称。它被作为“shebang”行写入存档的开头。在 Unix 上,这将由操作系统解释;在 Windows 上,它将由 Python 启动器处理。省略 interpreter 将导致不写入 shebang 行。如果指定了解释器,并且目标是一个文件名,则目标文件的可执行位将被设置。

main 参数指定将用作存档主程序的 callable 的名称。它只能在源是目录且源不包含 __main__.py 文件时指定。main 参数应采用“pkg.module:callable”的形式,并且存档将通过导入“pkg.module”并执行给定 callable(不带任何参数)来运行。如果源是目录且不包含 __main__.py 文件而省略 main,则会出错,否则生成的存档将不可执行。

如果为 sourcetarget 指定了文件对象,则调用方有责任在调用 create_archive 后关闭它。

在复制现有存档时,提供的文件对象仅需要 readreadlinewrite 方法。从目录创建存档时,如果目标是文件对象,它将被传递给 zipfile.ZipFile 类,并且必须提供该类所需的方法。

get_interpreter(archive)

返回在 archive 的 shebang 行中指定的解释器。如果没有 shebang,函数返回 Nonearchive 参数可以是文件名或以字节模式打开进行读取的文件类对象。

命令行用法

zipapp 模块可以使用 python -m 标志运行。命令行接口如下:

python -m zipapp directory [options]

    Create an archive from the given directory.  An archive will
    be created from the contents of that directory.  The archive
    will have the same name as the source directory with a .pyz
    extension.

    The following options can be specified:

    -o archive / --output archive

        The destination archive will have the specified name.  The
        given name will be used as written, so should include the
        ".pyz" or ".pyzw" extension.

    -p interpreter / --python interpreter

        The given interpreter will be written to the shebang line
        of the archive.  If this option is not given, the archive
        will have no shebang line.

    -m pkg.mod:fn / --main pkg.mod:fn

        The source directory must not have a __main__.py file. The
        archiver will write a __main__.py file into the target
        which calls fn from the module pkg.mod.

命令行接口的行为与 zipapp.create_archive() 的行为匹配。

此外,可以使用命令行界面来处理现有存档。

python -m zipapp app.pyz --show

    Displays the shebang line of an archive.  Output is of the
    form

        Interpreter: /usr/bin/env
    or
        Interpreter: <none>

    and is intended for diagnostic use, not for scripts.

python -m zipapp app.pyz -o newapp.pyz [-p interpreter]

    Copy app.pyz to newapp.pyz, modifying the shebang line based
    on the -p option (as for creating an archive, no -p option
    means remove the shebang line).  Specifying a destination is
    mandatory.

    In-place modification of an archive is *not* supported, as the
    risk of damaging archives is too great for a simple tool.

如前所述,存档是标准的 zip 文件,因此可以使用任何标准 ZIP 工具或 Python 的 zipfile 模块进行解包。因此,不提供或不需要列出存档内容或解包它们的接口。

常见问题

你确定标准的 ZIP 工具能处理开头的 #! 吗?
当然可以。zipfile 规范允许在 zip 文件开头附加任意数据。此功能通常用于“自解压 zip”程序。如果您的存档程序无法处理此问题,那是您的存档程序中的一个 bug。
zipapp 不仅仅是 zipfile 模块的一个非常薄的封装吗?
是的。如果您喜欢使用其他工具构建自己的 Python zip 应用程序存档,它们也能同样正常工作。zipapp 模块只是一种便利,仅此而已。
为什么不直接使用 .zip.py 扩展名?
用户期望 .zip 文件用存档工具打开,并期望 .py 文件包含可读文本。对于这种用例,两者都会造成混淆。
这如何与现有软件包格式竞争?
sdist、bdist 和 wheel 格式旨在用于打包模块,以便安装到现有的 Python 安装中。它们不打算在不安装的情况下使用。可执行 zip 格式专门设计用于独立使用,无需安装。它们实际上是独立 Python 脚本的多文件版本。

被拒绝的提案

Shebang 行的便捷值

是否有必要为任何常见的解释器值设置“便利”形式?例如,-p 3-p "/usr/bin/env python3" 含义相同。这可以为常见情况节省大量输入,并为不愿或不需要了解“其他”平台上 shebang 处理复杂性的人提供跨平台选项。

缺点是缩写词的翻译不明显。例如,“3”应该表示“/usr/bin/env python3”、“/usr/bin/python3”、“python3”还是其他什么?此外,对于“/usr/bin/env python”(任何可用版本的 Python)这个关键情况没有明显的简写形式,这很容易导致编写的脚本带有过于严格的 shebang 行。

总的来说,这似乎弊大于利,因此已被放弃考虑。

.pyz 注册为媒体类型

有人建议 [3].pyz 扩展名注册到 Unix 扩展名数据库中。虽然这样做与 Windows 安装程序注册扩展名相同,但 .py 扩展名并未列在媒体类型数据库中 [4]。在没有 .py 的情况下注册 .pyz 似乎不合理,因此该想法已从本 PEP 中删除。感兴趣的各方可以在未来安排同时注册 .py.pyz

默认解释器

本 PEP 的最初草案提议使用 /usr/bin/env python 作为默认解释器。Unix 用户对此行为有疑问,因为在许多发行版上,python 命令的默认值是 Python 2,并且认为本 PEP 默认应首选 Python 3。然而,使用 python3 命令可能会导致 Windows 用户出现意外行为,在 Windows 上,启动器对 python 命令的默认行为通常由用户自定义,但 python3 的行为可能未修改以匹配。

因此,遵循“面对歧义,拒绝猜测”的原则,除非明确请求,否则存档没有 shebang 行。在 Windows 上,存档仍将由启动器运行(使用默认 Python),而在 Unix 上,存档可以通过明确调用所需的 Python 解释器来运行。

管理 Shebang 行的命令行工具

可以想象,用户可能希望修改现有存档的 shebang 行,甚至只是显示当前的 shebang 行。使用现有工具很难做到这一点(zip 程序通常完全忽略预置数据,而文本编辑器在编辑包含二进制数据的文件时可能会遇到问题)。

zipapp 模块提供了处理 shebang 行的函数,但不包含该功能的命令行界面。这是因为不清楚如何在不使生成的界面过于复杂和可能令人困惑的情况下提供一个。更改 shebang 行预计是一种不常见的需求。

参考实现

参考实现位于 http://bugs.python.org/issue23491

参考资料

本 PEP 的讨论在 python-dev 邮件列表上进行,主题始于 https://mail.python.org/pipermail/python-dev/2015-February/138277.html


来源: https://github.com/python/peps/blob/main/peps/pep-0441.rst

最后修改时间:2025-02-01 08:55:40 GMT