zipapp — 管理可执行的 Python zip 归档文件

在 3.5 版本中添加。

源代码: Lib/zipapp.py


此模块提供了用于管理包含 Python 代码的 zip 文件创建的工具,这些 zip 文件可以被 Python 解释器直接执行。该模块提供了一个 命令行接口 和一个 Python API

基本示例

以下示例显示了如何使用 命令行接口 从包含 Python 代码的目录创建可执行归档文件。运行时,该归档文件将执行归档文件中模块 myappmain 函数。

$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>

命令行接口

从命令行作为程序调用时,使用以下形式

$ python -m zipapp source [options]

如果 source 是一个目录,这将从 source 的内容创建一个归档文件。如果 source 是一个文件,它应该是一个归档文件,并且它将被复制到目标归档文件(如果指定了 –info 选项,则会显示其 shebang 行的内容)。

理解以下选项

-o <output>, --output=<output>

将输出写入名为 output 的文件。如果未指定此选项,则输出文件名将与输入 source 相同,并添加扩展名 .pyz。如果给出了显式文件名,则按原样使用(因此如果需要,应包括 .pyz 扩展名)。

如果 source 是归档文件,则必须指定输出文件名(在这种情况下,output 不能与 source 相同)。

-p <interpreter>, --python=<interpreter>

向归档文件添加一个 #! 行,指定 interpreter 作为要运行的命令。此外,在 POSIX 上,使归档文件可执行。默认值是不写入 #! 行,并且不使该文件可执行。

-m <mainfn>, --main=<mainfn>

向归档文件写入一个 __main__.py 文件,该文件执行 mainfnmainfn 参数应具有 “pkg.mod:fn” 的形式,其中 “pkg.mod” 是归档文件中的包/模块,“fn” 是给定模块中的可调用对象。 __main__.py 文件将执行该可调用对象。

复制归档文件时,不能指定 --main

-c, --compress

使用 deflate 方法压缩文件,减小输出文件的大小。默认情况下,文件在归档文件中以未压缩方式存储。

复制归档文件时,--compress 无效。

在 3.7 版本中添加。

--info

显示嵌入在归档文件中的解释器,用于诊断目的。在这种情况下,任何其他选项都将被忽略,并且 SOURCE 必须是归档文件,而不是目录。

-h, --help

打印简短的使用消息并退出。

Python API

该模块定义了两个便捷函数

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

source 创建应用程序归档文件。源可以是以下任何一种

  • 目录的名称,或引用目录的 类路径对象,在这种情况下,将从该目录的内容创建新的应用程序归档文件。

  • 现有应用程序归档文件的名称,或引用此类文件的 类路径对象,在这种情况下,该文件将被复制到目标(修改它以反映为 interpreter 参数给出的值)。文件名应包括 .pyz 扩展名(如果需要)。

  • 以字节模式打开以进行读取的文件对象。该文件的内容应为应用程序归档文件,并且假定文件对象位于归档文件的开头。

target 参数确定将写入结果归档文件的位置

  • 如果它是文件的名称,或 类路径对象,则归档文件将被写入该文件。

  • 如果它是一个打开的文件对象,则归档文件将被写入该文件对象,该文件对象必须以字节模式打开以进行写入。

  • 如果省略目标(或 None),则源必须是一个目录,并且目标将是与源同名的文件,并添加 .pyz 扩展名。

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

main 参数指定将用作归档文件主程序的可调用对象的名称。仅当源是目录,并且源不包含 __main__.py 文件时,才可以指定该参数。main 参数应采用 “pkg.module:callable” 的形式,并且归档文件将通过导入 “pkg.module” 并执行给定的可调用对象(不带任何参数)来运行。如果源是目录且不包含 __main__.py 文件,则省略 main 是错误的,否则生成的归档文件将不可执行。

可选的 filter 参数指定一个回调函数,该函数传递一个 Path 对象,该对象表示要添加的文件路径(相对于源目录)。如果要添加文件,它应返回 True

可选的 compressed 参数确定是否压缩文件。如果设置为 True,则归档文件中的文件将使用 deflate 方法压缩;否则,文件将以未压缩方式存储。复制现有归档文件时,此参数无效。

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

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

在 3.7 版本中更改: 添加了 filtercompressed 参数。

zipapp.get_interpreter(archive)

返回归档文件开头 #! 行中指定的解释器。如果没有 #! 行,则返回 Nonearchive 参数可以是文件名或以字节模式打开以进行读取的类文件对象。假设它位于归档文件的开头。

示例

将一个目录打包到归档文件中,并运行它。

$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>

可以使用 create_archive() 函数完成相同的操作

>>> import zipapp
>>> zipapp.create_archive('myapp', 'myapp.pyz')

为了使应用程序在 POSIX 上直接可执行,请指定要使用的解释器。

$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>

要替换现有归档文件上的 shebang 行,请使用 create_archive() 函数创建一个修改后的归档文件

>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

要就地更新文件,请使用 BytesIO 对象在内存中进行替换,然后覆盖源。请注意,就地覆盖文件存在风险,即错误会导致原始文件丢失。此代码不保护免受此类错误的影响,但生产代码应采取保护措施。此外,此方法仅在归档文件适合内存时才有效

>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>>     f.write(temp.getvalue())

指定解释器

请注意,如果指定解释器然后分发应用程序归档文件,则需要确保所用的解释器是可移植的。Windows 的 Python 启动器支持 POSIX #! 行的大多数常见形式,但还有其他问题需要考虑

  • 如果使用“/usr/bin/env python”(或“python”命令的其他形式,如“/usr/bin/python”),则需要考虑用户可能将 Python 2 或 Python 3 作为其默认版本,并编写代码以在两个版本下工作。

  • 如果使用显式版本,例如“/usr/bin/env python3”,则应用程序将无法在没有该版本的用户上运行。(如果您的代码不兼容 Python 2,这可能是您想要的)。

  • 没有办法说“python X.Y 或更高版本”,因此请注意不要使用像“/usr/bin/env python3.4”这样的精确版本,因为例如,对于 Python 3.5 的用户,您需要更改 shebang 行。

通常,您应该使用“/usr/bin/env python2”或“/usr/bin/env python3”,具体取决于您的代码是为 Python 2 还是 3 编写的。

使用 zipapp 创建独立应用程序

使用 zipapp 模块,可以创建自包含的 Python 程序,这些程序可以分发给只需要在其系统上安装合适的 Python 版本的最终用户。这样做的关键是将应用程序的所有依赖项与应用程序代码一起捆绑到归档文件中。

创建独立归档文件的步骤如下

  1. 像往常一样在目录中创建应用程序,因此您有一个包含 __main__.py 文件和任何支持应用程序代码的 myapp 目录。

  2. 使用 pip 将应用程序的所有依赖项安装到 myapp 目录中

    $ python -m pip install -r requirements.txt --target myapp
    

    (这假设您在 requirements.txt 文件中包含了项目要求 - 如果没有,您可以只在 pip 命令行中手动列出依赖项)。

  3. 使用以下命令打包应用程序

    $ python -m zipapp -p "interpreter" myapp
    

这将生成一个独立的exe文件,该文件可以在任何具有可用相应解释器的计算机上运行。有关详细信息,请参阅 指定解释器。它可以作为单个文件发送给用户。

在 Unix 上,myapp.pyz 文件本身是可执行的。如果希望使用“普通”命令名,可以重命名文件以删除 .pyz 扩展名。在 Windows 上,由于 Python 解释器在安装时注册了 .pyz.pyzw 文件扩展名,因此 myapp.pyz[w] 文件是可执行的。

注意事项

如果您的应用程序依赖于包含 C 扩展的软件包,则无法从 zip 文件运行该软件包(这是一个操作系统限制,因为可执行代码必须存在于文件系统中才能供操作系统加载程序加载)。在这种情况下,您可以将该依赖项从 zip 文件中排除,并要求用户安装它,或者将其与 zip 文件一起发送,并在 __main__.py 中添加代码以将包含未压缩模块的目录包含到 sys.path 中。在这种情况下,您需要确保为目标架构发送适当的二进制文件(并可能选择要添加到 sys.path 的正确版本,基于用户的机器)。

Python Zip 应用程序归档文件格式

自 2.6 版本以来,Python 就可以执行包含 __main__.py 文件的 zip 文件。为了让 Python 执行,应用程序归档文件只需是一个包含 __main__.py 文件的标准 zip 文件,该文件将作为应用程序的入口点运行。与任何 Python 脚本一样,脚本的父级(在这种情况下为 zip 文件)将被放置在 sys.path 上,因此可以从 zip 文件导入其他模块。

zip 文件格式允许在 zip 文件前添加任意数据。zip 应用程序格式使用此功能在文件前添加一个标准的 POSIX “shebang” 行(#!/path/to/interpreter)。

因此,正式的 Python zip 应用程序格式为

  1. 一个可选的 shebang 行,包含字符 b'#!',后跟一个解释器名称,然后是一个换行符(b'\n')字符。解释器名称可以是操作系统 “shebang” 处理或 Windows 上的 Python 启动器接受的任何名称。解释器在 Windows 上应以 UTF-8 编码,在 POSIX 上应以 sys.getfilesystemencoding() 编码。

  2. 标准 zip 文件数据,由 zipfile 模块生成。zip 文件内容*必须*包含一个名为 __main__.py 的文件(该文件必须位于 zip 文件的“根”目录中 - 即,它不能位于子目录中)。zip 文件数据可以压缩或未压缩。

如果应用程序归档文件具有 shebang 行,则可以在 POSIX 系统上设置可执行位,以允许直接执行它。

不需要使用此模块中的工具来创建应用程序归档文件 - 该模块只是为了方便,但通过任何方式创建的上述格式的归档文件都是 Python 可以接受的。