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__':
不会被执行,这会引发相关代码的逻辑问题,所以要注意这样的写法。