Python 打包工具 zipapp

Python 标准库里提供了一个项目打包压缩的工具 zipapp,可以把项目打包到一个可执行文件里,从而方便地发布或者分享出去;收到文件后,也不需要解包等操作,直接运行即可。

打包

比如项目结构是这样的:

myapp/
├── client.py
├── config.py
├── myapp.py
└── server.py

可以这样配置 Makefile:

.PHONY: myapp
myapp: client.py config.py myapp.py server.py
    python3 -m zipapp $@ -o $@.pyz -p "/usr/bin/env python3" -m "$@:main"

这会生成 myapp.pyz。按 zipapp 的惯例,生成的文件以 .pyz 作为扩展名。

并且,因为通过 -p 选项指定了解释器,所以这个文件可以直接运行:

$ ./myapp.pyz ...

具体 zipapp 的用法,可以看这里的文档说明 。比如,打包时还可以添加 --compress 参数,这样可以通过压缩来进一步控制生成文件的尺寸。

解包

如果查看这个文件的内容,可以发现几乎都是文本,以及少量的二进制数据。

如果需要解包,可以用以下方法:

>>> import zipfile
>>>
>>> a = zipfile.ZipFile('myapp.pyz', 'r')
>>> a.extractall('.')

操作完成后,可以看到所有文件都解包在当前目录下,跟打包前的结构是一样的。

小坑

要注意打包生成的 myapp.pyz 的程序入口。如果 myapp.py 在 main() 之前还有其他的调用,比如定义了一个全局变量 a,并且在后续代码里会使用到,这种情况是有问题的:

if __name__ == '__main__':
    a = (1, 2, 3)
    sys.exit(main())

因为 zipapp 打包后,会生成新的程序入口 __main__.py

# -*- coding: utf-8 -*-
import myapp
myapp.main()

这种情况下,之前的代码段 if __name__ == '__main__': 不会被执行,这会引发相关代码的逻辑问题,所以要注意这样的写法。

Read More: