On this page
zipapp-Management 可执行的 Python zip 存档
3.5 版中的新Function。
源代码: Lib/zipapp.py
该模块提供了一些工具来 Management 包含 Python 代码(可以为由 Python 解释器直接执行)的 zip 文件的创建。该模块同时提供Command-Line Interface和Python API。
Basic Example
以下示例显示了如何使用Command-Line Interface从包含 Python 代码的目录中创建可执行 Files。运行时,归档文件将从归档文件中的模块myapp
执行main
Function。
$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>
Command-Line Interface
从命令行作为程序调用时,使用以下形式:
$ python -m zipapp source [options]
如果* source 是目录,则将从 source 的内容创建 Files。如果 source *是文件,则应为归档文件,并将其复制到目标归档文件(如果指定了–info 选项,则将显示其 shebang 行的内容)。
可以理解以下选项:
-o
<output>
,
--output
=<output>
- 将输出写入名为* output 的文件。如果未指定此选项,则输出文件名将与 Importing source *相同,并添加 extensions
.pyz
。如果给出了明确的文件名,则按原样使用(因此,如果需要,应包含.pyz
extensions)。
- 将输出写入名为* output 的文件。如果未指定此选项,则输出文件名将与 Importing source *相同,并添加 extensions
如果* source 是归档文件,则必须指定输出文件名(在这种情况下, output 不能与 source *相同)。
-p
<interpreter>
,
--python
=<interpreter>
- 在存档中添加
#!
行,并指定* interpreter *作为运行命令。另外,在 POSIX 上,使归档文件可执行。默认为不写#!
行,并且不使文件可执行。
- 在存档中添加
-m
<mainfn>
,
--main
=<mainfn>
- 将
__main__.py
文件写入执行* mainfn *的 Files 中。 * mainfn *参数的格式应为“ pkg.mod:fn”,其中“ pkg.mod”是归档中的软件包/模块,而“ fn”是给定模块中的可调用对象。__main__.py
文件将执行该可调用项。
- 将
复制 Files 时无法指定--main。
-c
`,` `--compress`
- 使用 deflate 方法 zipfile,以减小输出文件的大小。默认情况下,文件未压缩地存储在存档中。
--compress在复制存档时无效。
3.7 版中的新Function。
--info
``- 显示嵌入在归档文件中的解释器,以用于诊断。在这种情况下,任何其他选项都将被忽略,并且 SOURCE 必须是归档文件,而不是目录。
-h
`,` `--help`
- 打印简短用法消息并退出。
Python API
该模块定义了两个便捷Function:
zipapp.
create_archive
(* source , target = None , interpreter = None , main = None , filter = None , compressed = False *)- 从* source *创建一个应用程序 Files。来源可以是以下任意一种:
目录的名称,或表示目录的path-like object,在这种情况下,将从该目录的内容中创建一个新的应用程序存档。
现有应用程序归档文件的名称,或表示该文件的path-like object,在这种情况下,该文件将复制到目标(对其进行修改以反映为* interpreter *参数给出的值)。如果需要,文件名应包含
.pyz
extensions。打开一个文件对象以字节模式读取。文件的内容应为应用程序存档,并且假定文件对象位于存档的开头。
- target *参数确定将结果归档文件写入的位置:
如果它是文件名或path-like object,则存档将被写入该文件。
如果它是一个打开的文件对象,则存档将被写入该文件对象,该文件对象必须处于打开状态才能以字节模式写入。
如果Ellipsis了目标(或
None
),则源必须是目录,并且目标将是与源同名的文件,并添加.pyz
extensions。
interpreter 参数指定将用于执行存档的 Python 解释器的名称。在存档开始处将其写为“ shebang”行。在 POSIX 上,这将由 OS 解释,在 Windows 上,将由 Python 启动器处理。Ellipsis interpreter *不会导致写入任何 shebang 行。如果指定了解释器,并且目标是文件名,则将设置目标文件的可执行位。
main *参数指定一个可调用的名称,该名称将用作归档文件的主程序。仅当源是目录并且源不包含
__main__.py
文件时才可以指定它。 * main 参数应采用“ pkg.module:callable”的形式,并且将pass导入“ pkg.module”并执行不带参数的给定可调用对象来运行存档。如果源是目录并且不包含__main__.py
文件,则忽略 main *是错误的,否则生成的归档文件将不可执行。
可选的* filter *参数指定一个回调函数,该函数将传递一个 Path 对象,该 Path 对象表示要添加的文件的路径(相对于源目录)。如果要添加文件,则应返回True
。
可选的* compressed *参数确定是否 zipfile。如果设置为True
,则归档文件中的文件将使用 deflate 方法压缩;否则,文件将以未压缩的方式存储。复制现有归档文件时,此参数无效。
如果为* source 或 target *指定了文件对象,则调用方有责任在调用 create_archive 之后将其关闭。
复制现有存档时,提供的文件对象仅需要read
和readline
或write
方法。从目录创建 Files 时,如果目标是文件对象,它将被传递到zipfile.ZipFile
类,并且必须提供该类所需的方法。
3.7 版的新Function:添加了* filter 和 compressed *参数。
zipapp.
get_interpreter
(* archive *)- 返回 Files 开头
#!
行中指定的解释器。如果没有#!
行,则返回None。 * archive *参数可以是打开的文件名或类似文件的对象,以字节模式读取。假定它在存档的开始。
- 返回 Files 开头
Examples
将目录打包到存档中,然后运行它。
$ 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())
指定 Interpreter
请注意,如果您指定一个解释器然后分发您的应用程序 Files,则需要确保使用的解释器是可移植的。 Windows 的 Python 启动器支持 POSIX #!
行的最常见形式,但是还需要考虑其他问题:
如果您使用“/usr/bin/env python”(或“ python”命令的其他形式,例如“/usr/bin/python”),则需要考虑您的用户可能拥有 Python 2 或 Python 3 作为它们的默认设置,并编写您的代码以在这两个版本下工作。
如果使用显式版本,例如“/usr/bin/env python3”,则您的应用程序将不适用于没有该版本的用户。 (如果您没有使代码与 Python 2 兼容,这可能就是您想要的)。
没有办法说“ python XY 或更高版本”,因此请谨慎使用“/usr/bin/env python3.4”之类的确切版本,例如,您需要为 Python 3.5 的用户更改 shebang 行。
通常,您应该使用“/usr/bin/env python2”或“/usr/bin/env python3”,具体取决于您的代码是针对 Python 2 还是 3 编写的。
使用 zipapp 创建独立应用程序
使用zipapp模块,可以创建独立的 Python 程序,这些程序可以分发给只需要在系统上安装合适版本的 Python 的finally用户。这样做的关键是将所有应用程序的依赖项以及应用程序代码 Binding 到存档中。
创建独立存档的步骤如下:
照常在目录中创建应用程序,因此您将拥有一个
myapp
目录,其中包含__main__.py
文件以及所有支持的应用程序代码。使用 pip 将应用程序的所有依赖项安装到
myapp
目录中:
$ python -m pip install -r requirements.txt --target myapp
(这假定您在requirements.txt
文件中有项目要求-如果没有,则可以在 pip 命令行上手动列出依赖项)。
(可选)删除
myapp
目录中 pip 创建的.dist-info
目录。这些保留用于 pip 来 Management 软件包的元数据,并且您将不需要使用 pip 进行进一步使用-尽管您离开它们不会造成任何危害,但不需要使用它们。使用以下方法打包应用程序:
$ python -m zipapp -p "interpreter" myapp
这将产生一个独立的可执行文件,可以在具有适当解释器的任何计算机上运行。有关详情,请参见指定 Interpreter。它可以作为单个文件传送给用户。
在 Unix 上,myapp.pyz
文件是可执行的。如果您希望使用“普通”命令名称,则可以重命名文件以删除.pyz
extensions。在 Windows 上,myapp.pyz[w]
文件是可执行文件,这是因为 Python 解释器在安装时会注册.pyz
和.pyzw
文件 extensions。
使 Windows 可执行
在 Windows 上,.pyz
extensions 的注册是可选的,此外,某些地方无法“透明地”识别已注册的 extensions(最简单的示例是subprocess.run(['myapp'])
找不到您的应用程序-您需要明确指定 extensions) 。
因此,在 Windows 上,通常最好从 zipapp 创建可执行文件。尽管确实需要 C 编译器,但这相对容易。基本方法依赖于以下事实:zip 文件可以带有任意数据,而 Windows exe 文件可以带有任意数据。因此,pass创建合适的启动器并将.pyz
文件添加到文件末尾,您finally将获得运行应用程序的单文件可执行文件。
合适的启动器可以像下面这样简单:
#define Py_LIMITED_API 1
#include "Python.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef WINDOWS
int WINAPI wWinMain(
HINSTANCE hInstance, /* handle to current instance */
HINSTANCE hPrevInstance, /* handle to previous instance */
LPWSTR lpCmdLine, /* pointer to command line */
int nCmdShow /* show state of window */
)
#else
int wmain()
#endif
{
wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
myargv[0] = __wargv[0];
memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
return Py_Main(__argc+1, myargv);
}
如果定义WINDOWS
预处理器符号,它将生成一个 GUI 可执行文件,而没有它,则将生成一个控制台可执行文件。
要编译可执行文件,可以使用标准的 MSVC 命令行工具,也可以利用 distutils 知道如何编译 Python 源代码这一事实:
>>> from distutils.ccompiler import new_compiler
>>> import distutils.sysconfig
>>> import sys
>>> import os
>>> from pathlib import Path
>>> def compile(src):
>>> src = Path(src)
>>> cc = new_compiler()
>>> exe = src.stem
>>> cc.add_include_dir(distutils.sysconfig.get_python_inc())
>>> cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
>>> # First the CLI executable
>>> objs = cc.compile([str(src)])
>>> cc.link_executable(objs, exe)
>>> # Now the GUI executable
>>> cc.define_macro('WINDOWS')
>>> objs = cc.compile([str(src)])
>>> cc.link_executable(objs, exe + 'w')
>>> if __name__ == "__main__":
>>> compile("zastub.c")
生成的启动器使用“有限的 ABI”,因此它在任何版本的 Python 3.x 中都将保持不变。它只需要 Python(python3.dll
)位于用户的PATH
上。
对于完全独立的分发,您可以分发启动器,并附加应用程序,并与 Python“嵌入式”分发 Binding 在一起。它可以在具有适当体系结构(32 位或 64 位)的任何 PC 上运行。
Caveats
将应用程序 Binding 到单个文件中的过程存在一些限制。在大多数情况下(即使不是全部),也可以解决这些问题,而无需对应用程序进行重大更改。
如果您的应用程序依赖于包含 Cextensions 的软件包,则不能从 zip 文件运行该软件包(这是 OS 的限制,因为文件系统中必须存在可执行代码才能由 OS loader 加载)。在这种情况下,您可以从 zip 文件中排除该依赖关系,或者要求您的用户安装该依赖关系,或者将其与 zip 文件一起运送,然后将代码添加到
__main__.py
以在sys.path
中包括包含解压缩模块的目录。在这种情况下,您将需要确保为目标体系结构交付适当的二进制文件(并有可能根据用户的机器,在运行时选择正确的版本添加到sys.path
)。如果您如上所述交付 Windows 可执行文件,则需要确保用户的 PATH 上具有
python3.dll
(这不是安装程序的默认行为),或者应将应用程序与嵌入式发行版 Binding 在一起。上面建议的启动器使用 Python 嵌入 API。这意味着在您的应用程序中,
sys.executable
将成为您的应用程序,而不是常规的 Python 解释器。您的代码及其依赖项需要为此做好准备。例如,如果您的应用程序使用multiprocessing模块,则需要调用multiprocessing.set_executable()以使该模块知道在哪里可以找到标准的 Python 解释器。
Python Zip 应用程序存档格式
从 2.6 版开始,Python 便能够执行包含__main__.py
文件的 zip 文件。为了由 Python 执行,应用程序归档文件只需是包含__main__.py
文件的标准 zip 文件,该文件将作为应用程序的入口点运行。与任何 Python 脚本一样,脚本的父级(在本例中为 zip 文件)将放在sys.path上,因此可以从 zip 文件中导入其他模块。
zip 文件格式允许将任意数据放在 zip 文件之前。 zip 应用程序格式使用此Function在文件(#!/path/to/interpreter
)之前添加标准 POSIX“ shebang”行。
因此,正式而言,Python zip 应用程序格式为:
可选的 shebang 行,包含字符
b'#!'
,后跟解释器名称,然后是换行符(b'\n'
)。解释器名称可以是 os“ shebang”处理可接受的任何名称,也可以是 Windows 上的 Python 启动器。解释程序应在 Windows 上使用 UTF-8 编码,在 POSIX 上使用sys.getfilesystemencoding()编码。由zipfile模块生成的标准 zipfile 数据。 zipfile 内容必须包含一个名为
__main__.py
的文件(该文件必须位于 zipfile 的“根目录”中-即它不能位于子目录中)。 zipfile 数据可以压缩或解压缩。
如果应用程序 Files 中有 shebang 行,则可能在 POSIX 系统上设置了可执行位,以使其可以直接执行。
不需要使用此模块中的工具来创建应用程序归档文件-该模块很方便,但是 Python 可以接受pass任何方式创建的上述格式的归档文件。